Merge tag '1.3.4' into debian
Release fwupd 1.3.4
Mario Limonciello
4 years ago
0 | 0 | fwupd Release Notes |
1 | 1 | |
2 | Write release entries: | |
2 | 0. Create release version | |
3 | 3 | |
4 | git log --format="%s" --cherry-pick --right-only 1.3.2... | grep -i -v trivial | grep -v Merge | sort | uniq | |
4 | export release_ver= | |
5 | ||
6 | 1. Write release entries: | |
7 | ||
8 | git log --format="%s" --cherry-pick --right-only $(git describe --tags --abbrev=0)..HEAD | grep -i -v trivial | grep -v Merge | sort | uniq | |
5 | 9 | Add any user visible changes into ../data/org.freedesktop.fwupd.metainfo.xml |
6 | 10 | appstream-util appdata-to-news ../data/org.freedesktop.fwupd.metainfo.xml > NEWS |
7 | 11 | |
8 | Update translations: | |
12 | 2. Update translations: | |
9 | 13 | |
10 | 14 | ninja-build fwupd-pot |
11 | 15 | tx push --source |
13 | 17 | ninja-build fix-translations |
14 | 18 | git add ../po/*.po |
15 | 19 | |
16 | 2. Commit changes to git: | |
17 | ||
18 | # MAKE SURE THIS IS CORRECT | |
19 | export release_ver="1.3.3" | |
20 | 3. Commit changes to git: | |
20 | 21 | |
21 | 22 | git commit -a -m "Release fwupd ${release_ver}" |
22 | 23 | git tag -s -f -m "Release fwupd ${release_ver}" "${release_ver}" |
24 | 25 | git push --tags |
25 | 26 | git push |
26 | 27 | |
27 | 3. Generate the tarball: | |
28 | 4. Generate the tarball: | |
28 | 29 | |
29 | 30 | ninja dist |
30 | 31 | |
31 | 3a. Generate the additional verification metadata | |
32 | 4a. Generate the additional verification metadata | |
32 | 33 | |
33 | 34 | gpg -b -a meson-dist/fwupd-${release_ver}.tar.xz |
34 | 35 | |
35 | 4. Upload tarball: | |
36 | 5. Upload tarball: | |
36 | 37 | |
37 | 38 | scp meson-dist/fwupd-${release_ver}.tar.* hughsient@people.freedesktop.org:~/public_html/releases |
38 | ||
39 | 5. Do post release version bump in meson.build | |
40 | ||
41 | 6. Commit changes: | |
42 | ||
43 | git commit -a -m "trivial: post release version bump" | |
44 | git push |
15 | 15 | pkgver() { |
16 | 16 | cd ${pkgname} |
17 | 17 | |
18 | VERSION=$(git describe | sed 's/-/.r/;s/-/./') | |
19 | [ -z $VERSION ] && VERSION=$(head meson.build | grep ' version :' | cut -d \' -f2) | |
20 | ||
18 | VERSION=$(./contrib/get-version.py | sed 's/-/.r/;s/-/./') | |
21 | 19 | echo $VERSION |
22 | 20 | } |
23 | 21 |
0 | #!/usr/bin/python3 | |
1 | # | |
2 | # Copyright (C) 2019 Richard Hughes <richard@hughsie.com> | |
3 | # | |
4 | # SPDX-License-Identifier: LGPL-2.1+ | |
5 | ||
6 | import sys | |
7 | import uuid | |
8 | import argparse | |
9 | import struct | |
10 | ||
11 | CAPSULE_FLAGS_PERSIST_ACROSS_RESET = 0x00010000 | |
12 | CAPSULE_FLAGS_POPULATE_SYSTEM_TABLE = 0x00020000 | |
13 | CAPSULE_FLAGS_INITIATE_RESET = 0x00040000 | |
14 | ||
15 | def main(args): | |
16 | ||
17 | # parse GUID from command line | |
18 | try: | |
19 | guid = uuid.UUID(args.guid) | |
20 | except ValueError as e: | |
21 | print(e) | |
22 | return 1 | |
23 | try: | |
24 | with open(args.bin, 'rb') as f: | |
25 | bin_data = f.read() | |
26 | except FileNotFoundError as e: | |
27 | print(e) | |
28 | return 1 | |
29 | ||
30 | # check if already has header | |
31 | hdrsz = struct.calcsize('<16sIII') | |
32 | if len(bin_data) >= hdrsz: | |
33 | hdr = struct.unpack('<16sIII', bin_data[:hdrsz]) | |
34 | imgsz = hdr[3] | |
35 | if imgsz == len(bin_data): | |
36 | print('Replacing existing CAPSULE_HEADER of:') | |
37 | guid_mixed = uuid.UUID(bytes_le=hdr[0]) | |
38 | hdrsz_old = hdr[1] | |
39 | flags = hdr[2] | |
40 | print('GUID: %s' % guid_mixed) | |
41 | print('HdrSz: 0x%04x' % hdrsz_old) | |
42 | print('Flags: 0x%04x' % flags) | |
43 | print('PayloadSz: 0x%04x' % imgsz) | |
44 | bin_data = bin_data[hdrsz_old:] | |
45 | ||
46 | # set header flags | |
47 | flags = CAPSULE_FLAGS_PERSIST_ACROSS_RESET | CAPSULE_FLAGS_INITIATE_RESET | |
48 | if args.flags: | |
49 | flags = int(args.flags, 16) | |
50 | ||
51 | # build update capsule header | |
52 | imgsz = hdrsz + len(bin_data) | |
53 | hdr = struct.pack('<16sIII', guid.bytes_le, hdrsz, flags, imgsz) | |
54 | with open(args.cap, 'wb') as f: | |
55 | f.write(hdr + bin_data) | |
56 | print('Wrote capsule %s' % args.cap) | |
57 | print('GUID: %s' % guid) | |
58 | print('HdrSz: 0x%04x' % hdrsz) | |
59 | print('Flags: 0x%04x' % flags) | |
60 | print('PayloadSz: 0x%04x' % imgsz) | |
61 | return 0 | |
62 | ||
63 | parser = argparse.ArgumentParser(description='Add capsule header on firmware') | |
64 | parser.add_argument('--guid', help='GUID of the device', required=True) | |
65 | parser.add_argument('--bin', help='Path to the .bin file', required=True) | |
66 | parser.add_argument('--cap', help='Output capsule file path', required=True) | |
67 | parser.add_argument('--flags', help='Flags, e.g. 0x40000', default=None) | |
68 | args = parser.parse_args() | |
69 | ||
70 | sys.exit(main(args)) |
10 | 10 | #prepare |
11 | 11 | export DEBFULLNAME="CI Builder" |
12 | 12 | export DEBEMAIL="ci@travis-ci.org" |
13 | VERSION=`git describe | sed 's/-/+r/;s/-/+/'` | |
14 | [ -z $VERSION ] && VERSION=`head meson.build | grep ' version :' | cut -d \' -f2` | |
13 | VERSION=`./contrib/get-version.py | sed 's/-/+r/;s/-/+/'` | |
15 | 14 | rm -rf build/ |
16 | 15 | mkdir -p build |
17 | 16 | shopt -s extglob |
895 | 895 | <package /> |
896 | 896 | </distro> |
897 | 897 | <distro id="debian"> |
898 | <control /> | |
899 | <package variant="x86_64" /> | |
900 | <package variant="i386" /> | |
901 | </distro> | |
902 | <distro id="ubuntu"> | |
903 | <control /> | |
898 | <control> | |
899 | <inclusive>amd64</inclusive> | |
900 | <inclusive>arm64</inclusive> | |
901 | <inclusive>armhf</inclusive> | |
902 | <inclusive>i386</inclusive> | |
903 | </control> | |
904 | <package variant="x86_64" /> | |
905 | <package variant="i386" /> | |
906 | </distro> | |
907 | <distro id="ubuntu"> | |
908 | <control> | |
909 | <inclusive>amd64</inclusive> | |
910 | <inclusive>arm64</inclusive> | |
911 | <inclusive>armhf</inclusive> | |
912 | <inclusive>i386</inclusive> | |
913 | </control> | |
904 | 914 | <package variant="x86_64" /> |
905 | 915 | </distro> |
906 | 916 | </dependency> |
1318 | 1328 | <package>tpm2-tss-devel</package> |
1319 | 1329 | </distro> |
1320 | 1330 | <distro id="debian"> |
1321 | <control /> | |
1322 | <package variant="x86_64" /> | |
1323 | <package variant="i386" /> | |
1324 | </distro> | |
1325 | <distro id="ubuntu"> | |
1326 | <control /> | |
1331 | <control> | |
1332 | <inclusive>amd64</inclusive> | |
1333 | <inclusive>arm64</inclusive> | |
1334 | <inclusive>armhf</inclusive> | |
1335 | <inclusive>i386</inclusive> | |
1336 | </control> | |
1337 | <package variant="x86_64" /> | |
1338 | <package variant="i386" /> | |
1339 | </distro> | |
1340 | <distro id="ubuntu"> | |
1341 | <control> | |
1342 | <inclusive>amd64</inclusive> | |
1343 | <inclusive>arm64</inclusive> | |
1344 | <inclusive>armhf</inclusive> | |
1345 | <inclusive>i386</inclusive> | |
1346 | </control> | |
1327 | 1347 | <package variant="x86_64" /> |
1328 | 1348 | </distro> |
1329 | 1349 | </dependency> |
19 | 19 | -Dplugin_synaptics=true $@ |
20 | 20 | ninja-build dist |
21 | 21 | popd |
22 | VERSION=`meson introspect build --projectinfo | jq -r .version` | |
22 | VERSION=`./contrib/get-version.py` | |
23 | 23 | mkdir -p $HOME/rpmbuild/SOURCES/ |
24 | 24 | mv build/meson-dist/fwupd-$VERSION.tar.xz $HOME/rpmbuild/SOURCES/ |
25 | 25 | |
26 | 26 | #generate a spec file |
27 | 27 | sed "s,#VERSION#,$VERSION,; |
28 | s,#TARBALL_VERSION#,$VERSION,; | |
28 | 29 | s,#BUILD#,1,; |
29 | 30 | s,#LONGDATE#,`date '+%a %b %d %Y'`,; |
30 | 31 | s,#ALPHATAG#,alpha,; |
0 | # Firmware Packager | |
1 | ||
2 | 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` | |
3 | ||
4 | ## Prerequisites | |
5 | ||
6 | To run this script you will need | |
7 | ||
8 | 1. Python3.5, a standard install should include all packages you need | |
9 | 2. 7z (for extracting .exe files) | |
10 | 3. gcab (for creating the cab file) | |
11 | ||
12 | ## Usage | |
13 | ||
14 | To create a firmware package, you must supply, at a minimum: | |
15 | ||
16 | 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) | |
17 | 2. A short name for the firmware package, again you are free to choose this (`--firmware-name`). | |
18 | 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` | |
19 | 4. The firmware version (`--release-version`), try to match the manufacturers versioning scheme | |
20 | 5. The path to the executable file to repackage (`--exe`) | |
21 | 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. | |
22 | 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 | |
23 | bin file inside the archive, I would pass `--exe dell-thunderbolt-firmware.exe --bin Intel/tbt.bin` | |
24 | 7. The path to the cab file to output (`--out`). | |
25 | ||
26 | ## Documentation | |
27 | ||
28 | `--firmware-name` Short name of the firmware package can be customized (e.g. DellTBT) **REQUIRED** | |
29 | ||
30 | `--firmware-summary` One line description of the firmware package (e.g. Dell thunderbolt firmware) | |
31 | ||
32 | `--firmware-description` Longer description of the firmware package. Theoretically this can include HTML but I haven't tried it | |
33 | ||
34 | `--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** | |
35 | ||
36 | `--firmware-homepage` Website for the firmware provider (e.g. http://www.dell.com) | |
37 | ||
38 | `-contact-info` Email address of the firmware developer (e.g. someone@something.net) | |
39 | ||
40 | `--developer-name` Name of the firmware developer (e.g. Dell) **REQUIRED** | |
41 | ||
42 | `--release-version` Version number of the firmware package (e.g. 4.21.01.002) **REQUIRED** | |
43 | `--release-description` Description of the firmware release, again this can theoretically include HTML but I didn't try it. | |
44 | ||
45 | `--exe` Executable file to extract firmware from (e.g. `dell-thunderbolt-firmware.exe`) **REQUIRED** | |
46 | ||
47 | `--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** | |
48 | ||
49 | `--out` Output cab file path (e.g. `updates/firmware.cab`) **REQUIRED** | |
50 | ||
51 | ## Example | |
52 | ||
53 | 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. | |
54 | ||
55 | 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. | |
56 | ||
57 | Next we use `fwupdmgr` to get the device ID for the thunderbolt controller: | |
58 | ||
59 | ``` | |
60 | $ fwupdmgr get-devices | |
61 | Thunderbolt Controller | |
62 | Guid: 72533768-6a6c-5c06-994a-367374336810 | |
63 | DeviceID: 08001575 | |
64 | Plugin: thunderbolt | |
65 | Flags: internal|allow-online | |
66 | DeviceVendor: Intel | |
67 | Version: 21.00 | |
68 | Created: 2017-08-16 | |
69 | ``` | |
70 | The GUID field contains what we are looking for | |
71 | ||
72 | We can then run the firmware-packager with the following arguments: | |
73 | ||
74 | ``` | |
75 | $ 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 | |
76 | Using temp directory /tmp/tmpoey6_zx_ | |
77 | Extracting firmware exe | |
78 | Locating firmware bin | |
79 | Creating metainfo | |
80 | Cabbing firmware files | |
81 | Done | |
82 | ``` | |
83 | And we should have a firmware.cab that contains the packaged firmware. We can then install this firmware with | |
84 | ``` | |
85 | $ fwupdmgr install firmware.cab | |
86 | ``` |
0 | #!/usr/bin/python3 | |
1 | # | |
2 | # Copyright (C) 2017 Max Ehrlich max.ehr@gmail.com | |
3 | # | |
4 | # SPDX-License-Identifier: LGPL-2.1+ | |
5 | # | |
6 | ||
7 | import argparse | |
8 | import subprocess | |
9 | import contextlib | |
10 | import os | |
11 | import shutil | |
12 | import tempfile | |
13 | import time | |
14 | ||
15 | ||
16 | @contextlib.contextmanager | |
17 | def cd(path): | |
18 | prev_cwd = os.getcwd() | |
19 | os.chdir(path) | |
20 | yield | |
21 | os.chdir(prev_cwd) | |
22 | ||
23 | firmware_metainfo_template = """ | |
24 | <?xml version="1.0" encoding="UTF-8"?> | |
25 | <component type="firmware"> | |
26 | <id>org.{developer_name}.guid{firmware_id}</id> | |
27 | <name>{firmware_name}</name> | |
28 | <summary>{firmware_summary}</summary> | |
29 | <description> | |
30 | {firmware_description} | |
31 | </description> | |
32 | <provides> | |
33 | <firmware type="flashed">{device_guid}</firmware> | |
34 | </provides> | |
35 | <url type="homepage">{firmware_homepage}</url> | |
36 | <metadata_license>CC0-1.0</metadata_license> | |
37 | <project_license>proprietary</project_license> | |
38 | <updatecontact>{contact_info}</updatecontact> | |
39 | <developer_name>{developer_name}</developer_name> | |
40 | <releases> | |
41 | <release version="{release_version}" timestamp="{timestamp}"> | |
42 | <description> | |
43 | {release_description} | |
44 | </description> | |
45 | </release> | |
46 | </releases> | |
47 | </component> | |
48 | """ | |
49 | ||
50 | ||
51 | def make_firmware_metainfo(firmware_info, dst): | |
52 | local_info = vars(firmware_info) | |
53 | local_info["firmware_id"] = local_info["device_guid"][0:8] | |
54 | firmware_metainfo = firmware_metainfo_template.format(**local_info, timestamp=time.time()) | |
55 | ||
56 | with open(os.path.join(dst, 'firmware.metainfo.xml'), 'w') as f: | |
57 | f.write(firmware_metainfo) | |
58 | ||
59 | ||
60 | def extract_exe(exe, dst): | |
61 | command = ['7z', 'x', '-o{}'.format(dst), exe] | |
62 | subprocess.check_call(command, stdout=subprocess.DEVNULL) | |
63 | ||
64 | ||
65 | def get_firmware_bin(root, bin_path, dst): | |
66 | with cd(root): | |
67 | shutil.copy(bin_path, os.path.join(dst, 'firmware.bin')) | |
68 | ||
69 | ||
70 | def create_firmware_cab(exe, folder): | |
71 | with cd(folder): | |
72 | if os.name == "nt": | |
73 | directive = os.path.join (folder, "directive") | |
74 | with open (directive, 'w') as wfd: | |
75 | wfd.write('.OPTION EXPLICIT\r\n') | |
76 | wfd.write('.Set CabinetNameTemplate=firmware.cab\r\n') | |
77 | wfd.write('.Set DiskDirectory1=.\r\n') | |
78 | wfd.write('firmware.bin\r\n') | |
79 | wfd.write('firmware.metainfo.xml\r\n') | |
80 | command = ['makecab.exe', '/f', directive] | |
81 | else: | |
82 | command = ['gcab', '--create', 'firmware.cab', 'firmware.bin', 'firmware.metainfo.xml'] | |
83 | subprocess.check_call(command) | |
84 | ||
85 | ||
86 | def main(args): | |
87 | with tempfile.TemporaryDirectory() as dir: | |
88 | print('Using temp directory {}'.format(dir)) | |
89 | ||
90 | if args.exe: | |
91 | print('Extracting firmware exe') | |
92 | extract_exe(args.exe, dir) | |
93 | ||
94 | print('Locating firmware bin') | |
95 | get_firmware_bin(dir, args.bin, dir) | |
96 | ||
97 | print('Creating metainfo') | |
98 | make_firmware_metainfo(args, dir) | |
99 | ||
100 | print('Cabbing firmware files') | |
101 | create_firmware_cab(args, dir) | |
102 | ||
103 | print('Done') | |
104 | shutil.copy(os.path.join(dir, 'firmware.cab'), args.out) | |
105 | ||
106 | parser = argparse.ArgumentParser(description='Create fwupd packaged from windows executables') | |
107 | parser.add_argument('--firmware-name', help='Name of the firmware package can be customized (e.g. DellTBT)', required=True) | |
108 | parser.add_argument('--firmware-summary', help='One line description of the firmware package') | |
109 | parser.add_argument('--firmware-description', help='Longer description of the firmware package') | |
110 | 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) | |
111 | parser.add_argument('--firmware-homepage', help='Website for the firmware provider') | |
112 | parser.add_argument('--contact-info', help='Email address of the firmware developer') | |
113 | parser.add_argument('--developer-name', help='Name of the firmware developer', required=True) | |
114 | parser.add_argument('--release-version', help='Version number of the firmware package', required=True) | |
115 | parser.add_argument('--release-description', help='Description of the firmware release') | |
116 | parser.add_argument('--exe', help='(optional) Executable file to extract firmware from') | |
117 | 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) | |
118 | parser.add_argument('--out', help='Output cab file path', required=True) | |
119 | args = parser.parse_args() | |
120 | ||
121 | main(args) |
0 | if get_option('firmware-packager') | |
1 | install_data('firmware-packager', | |
2 | install_dir : 'share/fwupd') | |
3 | endif |
0 | # Firmware Packager | |
1 | ||
2 | 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` | |
3 | ||
4 | ## Prerequisites | |
5 | ||
6 | To run this script you will need | |
7 | ||
8 | 1. Python3.5, a standard install should include all packages you need | |
9 | 2. 7z (for extracting .exe files) | |
10 | 3. gcab (for creating the cab file) | |
11 | ||
12 | ## Usage | |
13 | ||
14 | To create a firmware package, you must supply, at a minimum: | |
15 | ||
16 | 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) | |
17 | 2. A short name for the firmware package, again you are free to choose this (`--firmware-name`). | |
18 | 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` | |
19 | 4. The firmware version (`--release-version`), try to match the manufacturers versioning scheme | |
20 | 5. The path to the executable file to repackage (`--exe`) | |
21 | 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. | |
22 | 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 | |
23 | bin file inside the archive, I would pass `--exe dell-thunderbolt-firmware.exe --bin Intel/tbt.bin` | |
24 | 7. The path to the cab file to output (`--out`). | |
25 | ||
26 | ## Documentation | |
27 | ||
28 | `--firmware-name` Short name of the firmware package can be customized (e.g. DellTBT) **REQUIRED** | |
29 | ||
30 | `--firmware-summary` One line description of the firmware package (e.g. Dell thunderbolt firmware) | |
31 | ||
32 | `--firmware-description` Longer description of the firmware package. Theoretically this can include HTML but I haven't tried it | |
33 | ||
34 | `--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** | |
35 | ||
36 | `--firmware-homepage` Website for the firmware provider (e.g. http://www.dell.com) | |
37 | ||
38 | `-contact-info` Email address of the firmware developer (e.g. someone@something.net) | |
39 | ||
40 | `--developer-name` Name of the firmware developer (e.g. Dell) **REQUIRED** | |
41 | ||
42 | `--release-version` Version number of the firmware package (e.g. 4.21.01.002) **REQUIRED** | |
43 | `--release-description` Description of the firmware release, again this can theoretically include HTML but I didn't try it. | |
44 | ||
45 | `--exe` Executable file to extract firmware from (e.g. `dell-thunderbolt-firmware.exe`) **REQUIRED** | |
46 | ||
47 | `--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** | |
48 | ||
49 | `--out` Output cab file path (e.g. `updates/firmware.cab`) **REQUIRED** | |
50 | ||
51 | ## Example | |
52 | ||
53 | 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. | |
54 | ||
55 | 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. | |
56 | ||
57 | Next we use `fwupdmgr` to get the device ID for the thunderbolt controller: | |
58 | ||
59 | ``` | |
60 | $ fwupdmgr get-devices | |
61 | Thunderbolt Controller | |
62 | Guid: 72533768-6a6c-5c06-994a-367374336810 | |
63 | DeviceID: 08001575 | |
64 | Plugin: thunderbolt | |
65 | Flags: internal|allow-online | |
66 | DeviceVendor: Intel | |
67 | Version: 21.00 | |
68 | Created: 2017-08-16 | |
69 | ``` | |
70 | The GUID field contains what we are looking for | |
71 | ||
72 | We can then run the firmware-packager with the following arguments: | |
73 | ||
74 | ``` | |
75 | $ 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 | |
76 | Using temp directory /tmp/tmpoey6_zx_ | |
77 | Extracting firmware exe | |
78 | Locating firmware bin | |
79 | Creating metainfo | |
80 | Cabbing firmware files | |
81 | Done | |
82 | ``` | |
83 | And we should have a firmware.cab that contains the packaged firmware. We can then install this firmware with | |
84 | ``` | |
85 | $ fwupdmgr install firmware.cab | |
86 | ``` |
0 | #!/usr/bin/python3 | |
1 | # | |
2 | # Copyright (C) 2019 Richard Hughes <richard@hughsie.com> | |
3 | # | |
4 | # SPDX-License-Identifier: LGPL-2.1+ | |
5 | ||
6 | import sys | |
7 | import uuid | |
8 | import argparse | |
9 | import ctypes | |
10 | ||
11 | CAPSULE_FLAGS_PERSIST_ACROSS_RESET = 0x00010000 | |
12 | CAPSULE_FLAGS_POPULATE_SYSTEM_TABLE = 0x00020000 | |
13 | CAPSULE_FLAGS_INITIATE_RESET = 0x00040000 | |
14 | ||
15 | def add_header(infile, outfile, gd, fl=None): | |
16 | # parse GUID from command line | |
17 | try: | |
18 | guid = uuid.UUID(gd) | |
19 | except ValueError as e: | |
20 | print(e) | |
21 | return 1 | |
22 | import struct | |
23 | try: | |
24 | with open(infile, 'rb') as f: | |
25 | bin_data = f.read() | |
26 | except FileNotFoundError as e: | |
27 | print(e) | |
28 | return 1 | |
29 | ||
30 | # check if already has header | |
31 | hdrsz = struct.calcsize('<16sIII') | |
32 | if len(bin_data) >= hdrsz: | |
33 | hdr = struct.unpack('<16sIII', bin_data[:hdrsz]) | |
34 | imgsz = hdr[3] | |
35 | if imgsz == len(bin_data): | |
36 | print('Replacing existing CAPSULE_HEADER of:') | |
37 | guid_mixed = uuid.UUID(bytes_le=hdr[0]) | |
38 | hdrsz_old = hdr[1] | |
39 | flags = hdr[2] | |
40 | print('GUID: %s' % guid_mixed) | |
41 | print('HdrSz: 0x%04x' % hdrsz_old) | |
42 | print('Flags: 0x%04x' % flags) | |
43 | print('PayloadSz: 0x%04x' % imgsz) | |
44 | bin_data = bin_data[hdrsz_old:] | |
45 | ||
46 | # set header flags | |
47 | flags = CAPSULE_FLAGS_PERSIST_ACROSS_RESET | CAPSULE_FLAGS_INITIATE_RESET | CAPSULE_FLAGS_POPULATE_SYSTEM_TABLE | |
48 | if fl: | |
49 | flags = int(fl, 16) | |
50 | ||
51 | # build update capsule header | |
52 | hdrsz = 4096 | |
53 | imgsz = hdrsz + len(bin_data) | |
54 | hdr = ctypes.create_string_buffer(hdrsz) | |
55 | struct.pack_into('<16sIII', hdr, 0, guid.bytes_le, hdrsz, flags, imgsz) | |
56 | with open(outfile, 'wb') as f: | |
57 | f.write(hdr) | |
58 | f.write(bin_data) | |
59 | print('Wrote capsule %s' % outfile) | |
60 | print('GUID: %s' % guid) | |
61 | print('HdrSz: 0x%04x' % hdrsz) | |
62 | print('Flags: 0x%04x' % flags) | |
63 | print('PayloadSz: 0x%04x' % imgsz) | |
64 | return 0 | |
65 | ||
66 | if __name__ == '__main__': | |
67 | parser = argparse.ArgumentParser(description='Add capsule header on firmware') | |
68 | parser.add_argument('--guid', help='GUID of the device', required=True) | |
69 | parser.add_argument('--bin', help='Path to the .bin file', required=True) | |
70 | parser.add_argument('--cap', help='Output capsule file path', required=True) | |
71 | parser.add_argument('--flags', help='Flags, e.g. 0x40000', default=None) | |
72 | args = parser.parse_args() | |
73 | ||
74 | sys.exit(add_header(args.bin, args.cap, args.guid, args.flags)) |
0 | #!/usr/bin/python3 | |
1 | # | |
2 | # Copyright (C) 2017 Max Ehrlich max.ehr@gmail.com | |
3 | # | |
4 | # SPDX-License-Identifier: LGPL-2.1+ | |
5 | # | |
6 | ||
7 | import argparse | |
8 | import subprocess | |
9 | import contextlib | |
10 | import os | |
11 | import shutil | |
12 | import tempfile | |
13 | import time | |
14 | ||
15 | ||
16 | @contextlib.contextmanager | |
17 | def cd(path): | |
18 | prev_cwd = os.getcwd() | |
19 | os.chdir(path) | |
20 | yield | |
21 | os.chdir(prev_cwd) | |
22 | ||
23 | firmware_metainfo_template = """ | |
24 | <?xml version="1.0" encoding="UTF-8"?> | |
25 | <component type="firmware"> | |
26 | <id>org.{developer_name}.guid{firmware_id}</id> | |
27 | <name>{firmware_name}</name> | |
28 | <summary>{firmware_summary}</summary> | |
29 | <description> | |
30 | {firmware_description} | |
31 | </description> | |
32 | <provides> | |
33 | <firmware type="flashed">{device_guid}</firmware> | |
34 | </provides> | |
35 | <url type="homepage">{firmware_homepage}</url> | |
36 | <metadata_license>CC0-1.0</metadata_license> | |
37 | <project_license>proprietary</project_license> | |
38 | <updatecontact>{contact_info}</updatecontact> | |
39 | <developer_name>{developer_name}</developer_name> | |
40 | <releases> | |
41 | <release version="{release_version}" timestamp="{timestamp}"> | |
42 | <description> | |
43 | {release_description} | |
44 | </description> | |
45 | </release> | |
46 | </releases> | |
47 | </component> | |
48 | """ | |
49 | ||
50 | ||
51 | def make_firmware_metainfo(firmware_info, dst): | |
52 | local_info = vars(firmware_info) | |
53 | local_info["firmware_id"] = local_info["device_guid"][0:8] | |
54 | firmware_metainfo = firmware_metainfo_template.format(**local_info, timestamp=time.time()) | |
55 | ||
56 | with open(os.path.join(dst, 'firmware.metainfo.xml'), 'w') as f: | |
57 | f.write(firmware_metainfo) | |
58 | ||
59 | ||
60 | def extract_exe(exe, dst): | |
61 | command = ['7z', 'x', '-o{}'.format(dst), exe] | |
62 | subprocess.check_call(command, stdout=subprocess.DEVNULL) | |
63 | ||
64 | ||
65 | def get_firmware_bin(root, bin_path, dst): | |
66 | with cd(root): | |
67 | shutil.copy(bin_path, os.path.join(dst, 'firmware.bin')) | |
68 | ||
69 | ||
70 | def create_firmware_cab(exe, folder): | |
71 | with cd(folder): | |
72 | if os.name == "nt": | |
73 | directive = os.path.join (folder, "directive") | |
74 | with open (directive, 'w') as wfd: | |
75 | wfd.write('.OPTION EXPLICIT\r\n') | |
76 | wfd.write('.Set CabinetNameTemplate=firmware.cab\r\n') | |
77 | wfd.write('.Set DiskDirectory1=.\r\n') | |
78 | wfd.write('firmware.bin\r\n') | |
79 | wfd.write('firmware.metainfo.xml\r\n') | |
80 | command = ['makecab.exe', '/f', directive] | |
81 | else: | |
82 | command = ['gcab', '--create', 'firmware.cab', 'firmware.bin', 'firmware.metainfo.xml'] | |
83 | subprocess.check_call(command) | |
84 | ||
85 | ||
86 | def main(args): | |
87 | with tempfile.TemporaryDirectory() as dir: | |
88 | print('Using temp directory {}'.format(dir)) | |
89 | ||
90 | if args.exe: | |
91 | print('Extracting firmware exe') | |
92 | extract_exe(args.exe, dir) | |
93 | ||
94 | print('Locating firmware bin') | |
95 | get_firmware_bin(dir, args.bin, dir) | |
96 | ||
97 | print('Creating metainfo') | |
98 | make_firmware_metainfo(args, dir) | |
99 | ||
100 | print('Cabbing firmware files') | |
101 | create_firmware_cab(args, dir) | |
102 | ||
103 | print('Done') | |
104 | shutil.copy(os.path.join(dir, 'firmware.cab'), args.out) | |
105 | ||
106 | if __name__ == '__main__': | |
107 | parser = argparse.ArgumentParser(description='Create fwupd packaged from windows executables') | |
108 | parser.add_argument('--firmware-name', help='Name of the firmware package can be customized (e.g. DellTBT)', required=True) | |
109 | parser.add_argument('--firmware-summary', help='One line description of the firmware package') | |
110 | parser.add_argument('--firmware-description', help='Longer description of the firmware package') | |
111 | 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) | |
112 | parser.add_argument('--firmware-homepage', help='Website for the firmware provider') | |
113 | parser.add_argument('--contact-info', help='Email address of the firmware developer') | |
114 | parser.add_argument('--developer-name', help='Name of the firmware developer', required=True) | |
115 | parser.add_argument('--release-version', help='Version number of the firmware package', required=True) | |
116 | parser.add_argument('--release-description', help='Description of the firmware release') | |
117 | parser.add_argument('--exe', help='(optional) Executable file to extract firmware from') | |
118 | 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) | |
119 | parser.add_argument('--out', help='Output cab file path', required=True) | |
120 | args = parser.parse_args() | |
121 | ||
122 | main(args) |
0 | #!/usr/bin/python3 | |
1 | # | |
2 | # Copyright (C) 2019 Mario Limonciello <mario.limonciello@dell.com> | |
3 | # | |
4 | # SPDX-License-Identifier: LGPL-2.1+ | |
5 | ||
6 | import dbus | |
7 | import os.path | |
8 | import sys | |
9 | import tempfile | |
10 | import gi | |
11 | gi.require_version('Fwupd', '2.0') | |
12 | from gi.repository import Fwupd #pylint: disable=wrong-import-position | |
13 | from simple_client import install, check_exists | |
14 | from add_capsule_header import add_header | |
15 | from firmware_packager import make_firmware_metainfo, create_firmware_cab | |
16 | ||
17 | class Variables: | |
18 | def __init__(self, device_guid, version): | |
19 | self.device_guid = device_guid | |
20 | self.developer_name = "Dell Inc" | |
21 | self.firmware_name = "New firmware" | |
22 | self.firmware_summary = "Unknown" | |
23 | self.firmware_description = "Unknown" | |
24 | self.firmware_homepage = "https://support.dell.com" | |
25 | self.contact_info = "Unknown" | |
26 | self.release_version = version | |
27 | self.release_description = "Unknown" | |
28 | ||
29 | def parse_args(): | |
30 | """Parse arguments for this client""" | |
31 | import argparse | |
32 | parser = argparse.ArgumentParser(description="Interact with fwupd daemon") | |
33 | parser.add_argument('exe', nargs='?', help='exe file') | |
34 | parser.add_argument('deviceid', nargs='?', | |
35 | help='DeviceID to operate on(optional)') | |
36 | args = parser.parse_args() | |
37 | return args | |
38 | ||
39 | def generate_cab(infile, directory, guid, version): | |
40 | output = os.path.join(directory, "firmware.bin") | |
41 | ret = add_header(infile, output, guid) | |
42 | if ret: | |
43 | sys.exit(ret) | |
44 | variables = Variables(guid, version) | |
45 | make_firmware_metainfo(variables, directory) | |
46 | create_firmware_cab(variables, directory) | |
47 | cab = os.path.join(directory, "firmware.cab") | |
48 | print("Generated CAB file %s" % cab) | |
49 | return cab | |
50 | ||
51 | def find_uefi_device(client, deviceid): | |
52 | devices = client.get_devices() | |
53 | for item in devices: | |
54 | #match the device we were given | |
55 | if deviceid: | |
56 | if item.get_id() != deviceid: | |
57 | continue | |
58 | # internal | |
59 | if not item.has_flag(1 << 0): | |
60 | continue | |
61 | # needs reboot | |
62 | if not item.has_flag(1 << 8): | |
63 | continue | |
64 | # return the first hit for UEFI plugin | |
65 | if item.get_plugin() == 'uefi': | |
66 | print("Installing to %s" % item.get_name()) | |
67 | return item.get_guid_default(),item.get_id(),item.get_version() | |
68 | print("Couldn't find any UEFI devices") | |
69 | sys.exit(1) | |
70 | ||
71 | def prompt_reboot(): | |
72 | print("An update requires a reboot to complete") | |
73 | while True: | |
74 | res = input("Restart now? (Y/N) ") | |
75 | if res.lower() == 'n': | |
76 | print("Reboot your machine manually to finish the update.") | |
77 | break | |
78 | if res.lower() != 'y': | |
79 | continue | |
80 | #reboot using logind | |
81 | obj = dbus.SystemBus().get_object('org.freedesktop.login1', | |
82 | '/org/freedesktop/login1') | |
83 | obj.Reboot(True, dbus_interface='org.freedesktop.login1.Manager') | |
84 | ||
85 | if __name__ == '__main__': | |
86 | ARGS = parse_args() | |
87 | CLIENT = Fwupd.Client() | |
88 | CLIENT.connect() | |
89 | check_exists(ARGS.exe) | |
90 | directory = tempfile.mkdtemp() | |
91 | guid, deviceid, version=find_uefi_device(CLIENT, ARGS.deviceid) | |
92 | cab = generate_cab(ARGS.exe, directory, guid, version) | |
93 | install(CLIENT, cab, deviceid, True, True) | |
94 | prompt_reboot()⏎ |
0 | if get_option('firmware-packager') | |
1 | install_data('firmware_packager.py', | |
2 | install_dir : 'share/fwupd') | |
3 | install_data('simple_client.py', | |
4 | install_dir : 'share/fwupd') | |
5 | install_data('add_capsule_header.py', | |
6 | install_dir : 'share/fwupd') | |
7 | install_data('install_dell_bios_exe.py', | |
8 | install_dir : 'share/fwupd') | |
9 | endif |
0 | #!/usr/bin/python3 | |
1 | # SPDX-License-Identifier: LGPL-2.1+ | |
2 | """A simple fwupd frontend""" | |
3 | import sys | |
4 | import os | |
5 | import gi | |
6 | from gi.repository import GLib | |
7 | gi.require_version('Fwupd', '2.0') | |
8 | from gi.repository import Fwupd #pylint: disable=wrong-import-position | |
9 | ||
10 | class Progress(): | |
11 | """Class to track the signal changes of progress events""" | |
12 | def __init__(self): | |
13 | self.device = None | |
14 | self.status = None | |
15 | self.percent = 0 | |
16 | self.erase = 0 | |
17 | ||
18 | def device_changed(self, new_device): | |
19 | """Indicate new device string to track""" | |
20 | if self.device != new_device: | |
21 | self.device = new_device | |
22 | print("\nUpdating %s" % self.device) | |
23 | ||
24 | def status_changed(self, percent, status): | |
25 | """Indicate new status string or % complete to track""" | |
26 | if self.status != status or self.percent != percent: | |
27 | for i in range(0, self.erase): | |
28 | sys.stdout.write("\b \b") | |
29 | self.status = status | |
30 | self.percent = percent | |
31 | status_str = "[" | |
32 | for i in range(0, 50): | |
33 | if i < percent/2: | |
34 | status_str += '*' | |
35 | else: | |
36 | status_str += ' ' | |
37 | status_str += "] %d%% %s" %(percent, status) | |
38 | self.erase = len(status_str) | |
39 | sys.stdout.write(status_str) | |
40 | sys.stdout.flush() | |
41 | if 'idle' in status: | |
42 | sys.stdout.write("\n") | |
43 | ||
44 | def parse_args(): | |
45 | """Parse arguments for this client""" | |
46 | import argparse | |
47 | parser = argparse.ArgumentParser(description="Interact with fwupd daemon") | |
48 | parser.add_argument("--allow-older", action="store_true", | |
49 | help="Install older payloads(default False)") | |
50 | parser.add_argument("--allow-reinstall", action="store_true", | |
51 | help="Reinstall payloads(default False)") | |
52 | parser.add_argument("command", choices=["get-devices", | |
53 | "get-details", | |
54 | "install"], help="What to do") | |
55 | parser.add_argument('cab', nargs='?', help='CAB file') | |
56 | parser.add_argument('deviceid', nargs='?', | |
57 | help='DeviceID to operate on(optional)') | |
58 | args = parser.parse_args() | |
59 | return args | |
60 | ||
61 | def get_devices(client): | |
62 | """Use fwupd client to fetch devices""" | |
63 | devices = client.get_devices() | |
64 | for item in devices: | |
65 | print(item.to_string()) | |
66 | ||
67 | def get_details(client, cab): | |
68 | """Use fwupd client to fetch details for a CAB file""" | |
69 | devices = client.get_details(cab, None) | |
70 | for device in devices: | |
71 | print(device.to_string()) | |
72 | ||
73 | def status_changed(client, spec, progress): #pylint: disable=unused-argument | |
74 | """Signal emitted by fwupd daemon indicating status changed""" | |
75 | progress.status_changed(client.get_percentage(), | |
76 | Fwupd.status_to_string(client.get_status())) | |
77 | ||
78 | def device_changed(client, device, progress): #pylint: disable=unused-argument | |
79 | """Signal emitted by fwupd daemon indicating active device changed""" | |
80 | progress.device_changed(device.get_name()) | |
81 | ||
82 | def install(client, cab, target, older, reinstall): | |
83 | """Use fwupd client to install CAB file to applicable devices""" | |
84 | # FWUPD_DEVICE_ID_ANY | |
85 | if not target: | |
86 | target = '*' | |
87 | flags = Fwupd.InstallFlags.NONE | |
88 | if older: | |
89 | flags |= Fwupd.InstallFlags.ALLOW_OLDER | |
90 | if reinstall: | |
91 | flags |= Fwupd.InstallFlags.ALLOW_REINSTALL | |
92 | progress = Progress() | |
93 | parent = super(client.__class__, client) | |
94 | parent.connect('device-changed', device_changed, progress) | |
95 | parent.connect('notify::percentage', status_changed, progress) | |
96 | parent.connect('notify::status', status_changed, progress) | |
97 | try: | |
98 | client.install(target, cab, flags, None) | |
99 | except GLib.Error as glib_err: #pylint: disable=catching-non-exception | |
100 | progress.status_changed(0, 'idle') | |
101 | print("%s" % glib_err) | |
102 | sys.exit(1) | |
103 | print("\n") | |
104 | ||
105 | def check_exists(cab): | |
106 | """Check that CAB file exists""" | |
107 | if not cab: | |
108 | print("Need to specify payload") | |
109 | sys.exit(1) | |
110 | if not os.path.isfile(cab): | |
111 | print("%s doesn't exist or isn't a file" % cab) | |
112 | sys.exit(1) | |
113 | ||
114 | if __name__ == '__main__': | |
115 | ARGS = parse_args() | |
116 | CLIENT = Fwupd.Client() | |
117 | CLIENT.connect() | |
118 | ||
119 | if ARGS.command == "get-devices": | |
120 | get_devices(CLIENT) | |
121 | elif ARGS.command == "get-details": | |
122 | check_exists(ARGS.cab) | |
123 | get_details(CLIENT, ARGS.cab) | |
124 | elif ARGS.command == "install": | |
125 | check_exists(ARGS.cab) | |
126 | install(CLIENT, ARGS.cab, ARGS.deviceid, ARGS.allow_older, ARGS.allow_reinstall) |
5 | 5 | %global json_glib_version 1.1.1 |
6 | 6 | |
7 | 7 | %define alphatag #ALPHATAG# |
8 | %define tarball_version #TARBALL_VERSION# | |
8 | 9 | |
9 | 10 | %global enable_ci 0 |
10 | 11 | %global enable_tests 1 |
35 | 36 | Summary: Firmware update daemon |
36 | 37 | Name: fwupd |
37 | 38 | Version: #VERSION# |
38 | Release: 0.#BUILD#%{?alphatag}%{?dist} | |
39 | Release: 999.#BUILD#%{?alphatag}%{?dist} | |
39 | 40 | License: LGPLv2+ |
40 | 41 | URL: https://github.com/fwupd/fwupd |
41 | Source0: http://people.freedesktop.org/~hughsient/releases/%{name}-%{version}.tar.xz | |
42 | Source0: http://people.freedesktop.org/~hughsient/releases/%{name}-%{tarball_version}.tar.xz | |
42 | 43 | |
43 | 44 | BuildRequires: gettext |
44 | 45 | BuildRequires: glib2-devel >= %{glib2_version} |
135 | 136 | Data files for installed tests. |
136 | 137 | |
137 | 138 | %prep |
138 | %autosetup -p1 | |
139 | %autosetup -p1 -n %{name}-%{tarball_version} | |
139 | 140 | |
140 | 141 | %build |
141 | 142 | |
214 | 215 | # delete most files from the subproject |
215 | 216 | rm ${RPM_BUILD_ROOT}%{_includedir}/libflashrom.h |
216 | 217 | rm ${RPM_BUILD_ROOT}%{_libdir}/libflashrom.so |
217 | rm ${RPM_BUILD_ROOT}%{_libdir}/pkgconfig/libflashrom.pc | |
218 | rm ${RPM_BUILD_ROOT}%{_libdir}/pkgconfig/flashrom.pc | |
218 | 219 | rm ${RPM_BUILD_ROOT}%{_sbindir}/flashrom |
219 | 220 | %endif |
220 | 221 | |
281 | 282 | %{_datadir}/man/man1/fwupdmgr.1.gz |
282 | 283 | %{_datadir}/metainfo/org.freedesktop.fwupd.metainfo.xml |
283 | 284 | %{_datadir}/icons/hicolor/scalable/apps/org.freedesktop.fwupd.svg |
284 | %{_datadir}/fwupd/firmware-packager | |
285 | %{_datadir}/fwupd/firmware_packager.py | |
286 | %{_datadir}/fwupd/simple_client.py | |
287 | %{_datadir}/fwupd/add_capsule_header.py | |
288 | %{_datadir}/fwupd/install_dell_bios_exe.py | |
285 | 289 | %{_unitdir}/fwupd-offline-update.service |
286 | 290 | %{_unitdir}/fwupd.service |
287 | 291 | %{_unitdir}/fwupd-refresh.service |
348 | 352 | %{_libdir}/fwupd-plugins-3/libfu_plugin_uefi.so |
349 | 353 | %{_libdir}/fwupd-plugins-3/libfu_plugin_uefi_recovery.so |
350 | 354 | %endif |
351 | %{_libdir}/fwupd-plugins-3/libfu_plugin_unifying.so | |
355 | %{_libdir}/fwupd-plugins-3/libfu_plugin_logitech_hidpp.so | |
352 | 356 | %{_libdir}/fwupd-plugins-3/libfu_plugin_upower.so |
353 | 357 | %{_libdir}/fwupd-plugins-3/libfu_plugin_vli_usbhub.so |
354 | 358 | %{_libdir}/fwupd-plugins-3/libfu_plugin_wacom_raw.so |
0 | #!/usr/bin/python3 | |
1 | # | |
2 | # Copyright (C) 2019 Dell, Inc. | |
3 | # | |
4 | # SPDX-License-Identifier: LGPL-2.1+ | |
5 | # | |
6 | ||
7 | import xml.etree.ElementTree as etree | |
8 | import os | |
9 | import subprocess | |
10 | ||
11 | def sanitize_for_ci(version): | |
12 | if not 'CI' in os.environ: | |
13 | return version | |
14 | OS=os.getenv('OS') | |
15 | if not OS: | |
16 | return version | |
17 | if "fedora" in OS: | |
18 | return version.replace('-','.') | |
19 | return version | |
20 | ||
21 | def get_version_git(): | |
22 | try: | |
23 | version = subprocess.check_output(['git', 'describe'], stderr=subprocess.DEVNULL) | |
24 | return version.strip().decode('utf-8') | |
25 | except (subprocess.CalledProcessError, PermissionError, FileNotFoundError): | |
26 | return '' | |
27 | ||
28 | def get_version(): | |
29 | tree = etree.parse(os.path.join("data", "org.freedesktop.fwupd.metainfo.xml")) | |
30 | version = '' | |
31 | for child in tree.findall('releases'): | |
32 | for release in child: | |
33 | if not "version" in release.attrib: | |
34 | continue | |
35 | if release.attrib['version'] > version: | |
36 | version = release.attrib['version'] | |
37 | return version | |
38 | ||
39 | if __name__ == '__main__': | |
40 | ||
41 | version = get_version_git() | |
42 | if version: | |
43 | version = sanitize_for_ci(version) | |
44 | else: | |
45 | version = get_version() | |
46 | print(version) |
0 | #!/usr/bin/python3 | |
1 | # SPDX-License-Identifier: LGPL-2.1+ | |
2 | """A simple fwupd frontend""" | |
3 | import sys | |
4 | import os | |
5 | import gi | |
6 | from gi.repository import GLib | |
7 | gi.require_version('Fwupd', '2.0') | |
8 | from gi.repository import Fwupd #pylint: disable=wrong-import-position | |
9 | ||
10 | class Progress(): | |
11 | """Class to track the signal changes of progress events""" | |
12 | def __init__(self): | |
13 | self.device = None | |
14 | self.status = None | |
15 | self.percent = 0 | |
16 | self.erase = 0 | |
17 | ||
18 | def device_changed(self, new_device): | |
19 | """Indicate new device string to track""" | |
20 | if self.device != new_device: | |
21 | self.device = new_device | |
22 | print("\nUpdating %s" % self.device) | |
23 | ||
24 | def status_changed(self, percent, status): | |
25 | """Indicate new status string or % complete to track""" | |
26 | if self.status != status or self.percent != percent: | |
27 | for i in range(0, self.erase): | |
28 | sys.stdout.write("\b \b") | |
29 | self.status = status | |
30 | self.percent = percent | |
31 | status_str = "[" | |
32 | for i in range(0, 50): | |
33 | if i < percent/2: | |
34 | status_str += '*' | |
35 | else: | |
36 | status_str += ' ' | |
37 | status_str += "] %d%% %s" %(percent, status) | |
38 | status_str.erase = len(status_str) | |
39 | sys.stdout.write(status_str) | |
40 | sys.stdout.flush() | |
41 | if 'idle' in status: | |
42 | sys.stdout.write("\n") | |
43 | ||
44 | def parse_args(): | |
45 | """Parse arguments for this client""" | |
46 | import argparse | |
47 | parser = argparse.ArgumentParser(description="Interact with fwupd daemon") | |
48 | parser.add_argument("--allow-older", action="store_true", | |
49 | help="Install older payloads(default False)") | |
50 | parser.add_argument("--allow-reinstall", action="store_true", | |
51 | help="Reinstall payloads(default False)") | |
52 | parser.add_argument("command", choices=["get-devices", | |
53 | "get-details", | |
54 | "install"], help="What to do") | |
55 | parser.add_argument('cab', nargs='?', help='CAB file') | |
56 | parser.add_argument('deviceid', nargs='?', | |
57 | help='DeviceID to operate on(optional)') | |
58 | args = parser.parse_args() | |
59 | return args | |
60 | ||
61 | def get_devices(client): | |
62 | """Use fwupd client to fetch devices""" | |
63 | devices = client.get_devices() | |
64 | for item in devices: | |
65 | print(item.to_string()) | |
66 | ||
67 | def get_details(client, cab): | |
68 | """Use fwupd client to fetch details for a CAB file""" | |
69 | devices = client.get_details(cab, None) | |
70 | for device in devices: | |
71 | print(device.to_string()) | |
72 | ||
73 | def status_changed(client, spec, progress): #pylint: disable=unused-argument | |
74 | """Signal emitted by fwupd daemon indicating status changed""" | |
75 | progress.status_changed(client.get_percentage(), | |
76 | Fwupd.status_to_string(client.get_status())) | |
77 | ||
78 | def device_changed(client, device, progress): #pylint: disable=unused-argument | |
79 | """Signal emitted by fwupd daemon indicating active device changed""" | |
80 | progress.device_changed(device.get_name()) | |
81 | ||
82 | def install(client, cab, target, older, reinstall): | |
83 | """Use fwupd client to install CAB file to applicable devices""" | |
84 | # FWUPD_DEVICE_ID_ANY | |
85 | if not target: | |
86 | target = '*' | |
87 | flags = Fwupd.InstallFlags.NONE | |
88 | if older: | |
89 | flags |= Fwupd.InstallFlags.ALLOW_OLDER | |
90 | if reinstall: | |
91 | flags |= Fwupd.InstallFlags.ALLOW_REINSTALL | |
92 | progress = Progress() | |
93 | parent = super(client.__class__, client) | |
94 | parent.connect('device-changed', device_changed, progress) | |
95 | parent.connect('notify::percentage', status_changed, progress) | |
96 | parent.connect('notify::status', status_changed, progress) | |
97 | try: | |
98 | client.install(target, cab, flags, None) | |
99 | except GLib.Error as glib_err: #pylint: disable=catching-non-exception | |
100 | progress.status_changed(0, 'idle') | |
101 | print("%s" % glib_err) | |
102 | sys.exit(1) | |
103 | ||
104 | def check_cab(cab): | |
105 | """Check that CAB file exists""" | |
106 | if not cab: | |
107 | print("Need to specify payload") | |
108 | sys.exit(1) | |
109 | if not os.path.isfile(cab): | |
110 | print("%s doesn't exist or isn't a file" % cab) | |
111 | sys.exit(1) | |
112 | ||
113 | if __name__ == '__main__': | |
114 | ARGS = parse_args() | |
115 | CLIENT = Fwupd.Client() | |
116 | CLIENT.connect() | |
117 | ||
118 | if ARGS.command == "get-devices": | |
119 | get_devices(CLIENT) | |
120 | elif ARGS.command == "get-details": | |
121 | check_cab(ARGS.cab) | |
122 | get_details(CLIENT, ARGS.cab) | |
123 | elif ARGS.command == "install": | |
124 | check_cab(ARGS.cab) | |
125 | install(CLIENT, ARGS.cab, ARGS.deviceid, ARGS.allow_older, ARGS.allow_reinstall) |
117 | 117 | test.add_file('0a29848de74d26348bc5a6e24fc9f03778eddf0e-hughski-colorhug2-2.0.7.cab', '2.0.7') |
118 | 118 | tests.append(test) |
119 | 119 | |
120 | # Logitech Unifying Receiver (RQR12) using 'unifying' | |
120 | # Logitech Unifying Receiver (RQR12) using logitech_hidpp | |
121 | 121 | test = Test('UnifyingRQR12', '9d131a0c-a606-580f-8eda-80587250b8d6') |
122 | 122 | test.add_file('6e5ab5961ec4c577bff198ebb465106e979cf686-Logitech-Unifying-RQR12.05_B0028.cab', 'RQR12.05_B0028') |
123 | 123 | test.add_file('938fec082652c603a1cdafde7cd25d76baadc70d-Logitech-Unifying-RQR12.07_B0029.cab', 'RQR12.07_B0029') |
124 | 124 | tests.append(test) |
125 | 125 | |
126 | # Logitech Unifying Receiver (RQR24) using 'unifying' | |
126 | # Logitech Unifying Receiver (RQR24) using logitech_hidpp | |
127 | 127 | test = Test('UnifyingRQR24', 'cc4cbfa9-bf9d-540b-b92b-172ce31013c1') |
128 | 128 | test.add_file('82b90b2614a9a4d0aced1ab8a4a99e228c95585c-Logitech-Unifying-RQ024.03_B0027.cab', 'RQR24.03_B0027') |
129 | 129 | test.add_file('4511b9b0d123bdbe8a2007233318ab215a59dfe6-Logitech-Unifying-RQR24.05_B0029.cab', 'RQR24.05_B0029') |
31 | 31 | <binary>fwupdmgr</binary> |
32 | 32 | </provides> |
33 | 33 | <releases> |
34 | <release version="1.3.4" date="2019-11-22"> | |
35 | <description> | |
36 | <p>This release adds the following features:</p> | |
37 | <ul> | |
38 | <li>Add a new property Interactive to the daemon</li> | |
39 | <li>Add a new script for installing a Dell BIOS from an EXE file</li> | |
40 | <li>Add support for Foxconn T77W968 and DW5821e eSIM</li> | |
41 | <li>Add support for matching firmware requirements on device parents</li> | |
42 | <li>Add support for writing VIA PD and I2C devices</li> | |
43 | <li>Add versions formats for the Microsoft Surface devices</li> | |
44 | </ul> | |
45 | <p>This release fixes the following bugs:</p> | |
46 | <ul> | |
47 | <li>Allows confined snaps to activate fwupd via D-Bus</li> | |
48 | <li>Correct Wacom panel HWID support</li> | |
49 | <li>Don't assume all udev devices have device_file</li> | |
50 | <li>Dynamically determine release version</li> | |
51 | <li>Fall back to `ID_LIKE` when the path for `ID` doesn't exist</li> | |
52 | <li>Fix a fastboot regression when updating modem firmware</li> | |
53 | <li>Fix regression when coldplugging superio devices</li> | |
54 | <li>Fix the linking of the UEFI update binary</li> | |
55 | <li>Fix the vendor id of hidraw devices</li> | |
56 | <li>Make loading USB device strings non-fatal</li> | |
57 | <li>Reject invalid Synaptics MST chip IDs</li> | |
58 | <li>Skip cleanup after device is done updating if required</li> | |
59 | </ul> | |
60 | </description> | |
61 | </release> | |
34 | 62 | <release version="1.3.3" date="2019-11-01"> |
35 | 63 | <description> |
36 | 64 | <p>This release adds the following features:</p> |
3 | 3 | Exec=@libexecdir@/fwupd/fwupd |
4 | 4 | User=root |
5 | 5 | SystemdService=fwupd.service |
6 | AssumedAppArmorLabel=unconfined |
411 | 411 | set process-wide. |
412 | 412 | This allows plugins to detect when they should output detailed debugging |
413 | 413 | information that would normally be too verbose to keep in the journal. |
414 | For example, using <code>--plugin-verbose=unifying</code> would set | |
415 | <code>FWUPD_UNIFYING_VERBOSE=1</code>. | |
414 | For example, using <code>--plugin-verbose=logitech_hidpp</code> would set | |
415 | <code>FWUPD_LOGITECH_HID_VERBOSE=1</code>. | |
416 | 416 | </para> |
417 | 417 | </section> |
418 | 418 |
37 | 37 | typedef struct { |
38 | 38 | FwupdStatus status; |
39 | 39 | gboolean tainted; |
40 | gboolean interactive; | |
40 | 41 | guint percentage; |
41 | 42 | gchar *daemon_version; |
42 | 43 | gchar *host_product; |
62 | 63 | PROP_TAINTED, |
63 | 64 | PROP_HOST_PRODUCT, |
64 | 65 | PROP_HOST_MACHINE_ID, |
66 | PROP_INTERACTIVE, | |
65 | 67 | PROP_LAST |
66 | 68 | }; |
67 | 69 | |
162 | 164 | g_object_notify (G_OBJECT (client), "tainted"); |
163 | 165 | } |
164 | 166 | } |
167 | if (g_variant_dict_contains (dict, "Interactive")) { | |
168 | g_autoptr(GVariant) val = NULL; | |
169 | val = g_dbus_proxy_get_cached_property (proxy, "Interactive"); | |
170 | if (val != NULL) { | |
171 | priv->interactive = g_variant_get_boolean (val); | |
172 | g_object_notify (G_OBJECT (client), "interactive"); | |
173 | } | |
174 | } | |
165 | 175 | if (g_variant_dict_contains (dict, "Percentage")) { |
166 | 176 | g_autoptr(GVariant) val = NULL; |
167 | 177 | val = g_dbus_proxy_get_cached_property (proxy, "Percentage"); |
282 | 292 | val2 = g_dbus_proxy_get_cached_property (priv->proxy, "Tainted"); |
283 | 293 | if (val2 != NULL) |
284 | 294 | priv->tainted = g_variant_get_boolean (val2); |
295 | val2 = g_dbus_proxy_get_cached_property (priv->proxy, "Interactive"); | |
296 | if (val2 != NULL) | |
297 | priv->interactive = g_variant_get_boolean (val2); | |
285 | 298 | val = g_dbus_proxy_get_cached_property (priv->proxy, "HostProduct"); |
286 | 299 | if (val != NULL) |
287 | 300 | fwupd_client_set_host_product (client, g_variant_get_string (val, NULL)); |
1260 | 1273 | FwupdClientPrivate *priv = GET_PRIVATE (client); |
1261 | 1274 | g_return_val_if_fail (FWUPD_IS_CLIENT (client), FALSE); |
1262 | 1275 | return priv->tainted; |
1276 | } | |
1277 | ||
1278 | ||
1279 | /** | |
1280 | * fwupd_client_get_daemon_interactive: | |
1281 | * @client: A #FwupdClient | |
1282 | * | |
1283 | * Gets if the daemon is running in an interactive terminal. | |
1284 | * | |
1285 | * Returns: %TRUE if the daemon is running in an interactive terminal | |
1286 | * | |
1287 | * Since: 1.3.4 | |
1288 | **/ | |
1289 | gboolean | |
1290 | fwupd_client_get_daemon_interactive (FwupdClient *client) | |
1291 | { | |
1292 | FwupdClientPrivate *priv = GET_PRIVATE (client); | |
1293 | g_return_val_if_fail (FWUPD_IS_CLIENT (client), FALSE); | |
1294 | return priv->interactive; | |
1263 | 1295 | } |
1264 | 1296 | |
1265 | 1297 | /** |
1754 | 1786 | case PROP_HOST_MACHINE_ID: |
1755 | 1787 | g_value_set_string (value, priv->host_machine_id); |
1756 | 1788 | break; |
1789 | case PROP_INTERACTIVE: | |
1790 | g_value_set_boolean (value, priv->interactive); | |
1791 | break; | |
1757 | 1792 | default: |
1758 | 1793 | G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); |
1759 | 1794 | break; |
1897 | 1932 | g_object_class_install_property (object_class, PROP_TAINTED, pspec); |
1898 | 1933 | |
1899 | 1934 | /** |
1935 | * FwupdClient:interactive: | |
1936 | * | |
1937 | * If the daemon is running in an interactive terminal | |
1938 | * | |
1939 | * Since: 1.3.4 | |
1940 | */ | |
1941 | pspec = g_param_spec_boolean ("interactive", NULL, NULL, FALSE, | |
1942 | G_PARAM_READABLE | G_PARAM_STATIC_NAME); | |
1943 | g_object_class_install_property (object_class, PROP_INTERACTIVE, pspec); | |
1944 | ||
1945 | /** | |
1900 | 1946 | * FwupdClient:percentage: |
1901 | 1947 | * |
1902 | 1948 | * The last-reported percentage of the daemon. |
124 | 124 | GError **error); |
125 | 125 | FwupdStatus fwupd_client_get_status (FwupdClient *client); |
126 | 126 | gboolean fwupd_client_get_tainted (FwupdClient *client); |
127 | gboolean fwupd_client_get_daemon_interactive (FwupdClient *client); | |
127 | 128 | guint fwupd_client_get_percentage (FwupdClient *client); |
128 | 129 | const gchar *fwupd_client_get_daemon_version (FwupdClient *client); |
129 | 130 | const gchar *fwupd_client_get_host_product (FwupdClient *client); |
492 | 492 | return FWUPD_VERSION_FORMAT_INTEL_ME; |
493 | 493 | if (g_strcmp0 (str, "intel-me2") == 0) |
494 | 494 | return FWUPD_VERSION_FORMAT_INTEL_ME2; |
495 | if (g_strcmp0 (str, "surface-legacy") == 0) | |
496 | return FWUPD_VERSION_FORMAT_SURFACE_LEGACY; | |
497 | if (g_strcmp0 (str, "surface") == 0) | |
498 | return FWUPD_VERSION_FORMAT_SURFACE; | |
495 | 499 | return FWUPD_VERSION_FORMAT_UNKNOWN; |
496 | 500 | } |
497 | 501 | |
524 | 528 | return "intel-me"; |
525 | 529 | if (kind == FWUPD_VERSION_FORMAT_INTEL_ME2) |
526 | 530 | return "intel-me2"; |
527 | return NULL; | |
528 | } | |
531 | if (kind == FWUPD_VERSION_FORMAT_SURFACE_LEGACY) | |
532 | return "surface-legacy"; | |
533 | if (kind == FWUPD_VERSION_FORMAT_SURFACE) | |
534 | return "surface"; | |
535 | return NULL; | |
536 | } |
244 | 244 | * @FWUPD_VERSION_FORMAT_BCD: Binary coded decimal notation |
245 | 245 | * @FWUPD_VERSION_FORMAT_INTEL_ME: Intel ME-style bitshifted notation |
246 | 246 | * @FWUPD_VERSION_FORMAT_INTEL_ME2: Intel ME-style A.B.CC.DDDD notation notation |
247 | * @FWUPD_VERSION_FORMAT_SURFACE_LEGACY: Legacy Microsoft Surface 10b.12b.10b | |
248 | * @FWUPD_VERSION_FORMAT_SURFACE: Microsoft Surface 8b.16b.8b | |
247 | 249 | * |
248 | 250 | * The flags used when parsing version numbers. |
249 | 251 | * |
260 | 262 | FWUPD_VERSION_FORMAT_BCD, /* Since: 1.2.9 */ |
261 | 263 | FWUPD_VERSION_FORMAT_INTEL_ME, /* Since: 1.2.9 */ |
262 | 264 | FWUPD_VERSION_FORMAT_INTEL_ME2, /* Since: 1.2.9 */ |
265 | FWUPD_VERSION_FORMAT_SURFACE_LEGACY, /* Since: 1.3.4 */ | |
266 | FWUPD_VERSION_FORMAT_SURFACE, /* Since: 1.3.4 */ | |
263 | 267 | /*< private >*/ |
264 | 268 | FWUPD_VERSION_FORMAT_LAST |
265 | 269 | } FwupdVersionFormat; |
398 | 398 | fwupd_remote_get_automatic_reports; |
399 | 399 | local: *; |
400 | 400 | } LIBFWUPD_1.3.2; |
401 | ||
402 | LIBFWUPD_1.3.4 { | |
403 | global: | |
404 | fwupd_client_get_daemon_interactive; | |
405 | local: *; | |
406 | } LIBFWUPD_1.3.3; |
0 | 0 | project('fwupd', 'c', |
1 | version : '1.3.3', | |
1 | version : run_command('contrib/get-version.py').stdout().strip(), | |
2 | 2 | license : 'LGPL-2.1+', |
3 | 3 | meson_version : '>=0.47.0', |
4 | 4 | default_options : ['warning_level=2', 'c_std=c99'], |
8 | 8 | varr = fwupd_version.split('.') |
9 | 9 | fwupd_major_version = varr[0] |
10 | 10 | fwupd_minor_version = varr[1] |
11 | fwupd_micro_version = varr[2] | |
12 | ||
11 | fwupd_micro_version = varr[2].split('-')[0] | |
13 | 12 | conf = configuration_data() |
13 | ||
14 | if varr[2].contains('-') | |
15 | fwupd_dirty_version = varr[2].split('-')[1] | |
16 | fwupd_commit = varr[2].split('-')[2] | |
17 | conf.set('FWUPD_DIRTY_VERSION', fwupd_dirty_version) | |
18 | conf.set_quoted('FWUPD_COMMIT_VERSION', fwupd_commit) | |
19 | endif | |
14 | 20 | conf.set('FWUPD_MAJOR_VERSION', fwupd_major_version) |
15 | 21 | conf.set('FWUPD_MINOR_VERSION', fwupd_minor_version) |
16 | 22 | conf.set('FWUPD_MICRO_VERSION', fwupd_micro_version) |
17 | 23 | conf.set_quoted('PACKAGE_VERSION', fwupd_version) |
18 | ||
19 | archiver = find_program('git', required : false) | |
20 | if archiver.found() | |
21 | result = run_command('git', 'describe') | |
22 | if result.returncode() == 0 | |
23 | describe = result.stdout().strip() | |
24 | conf.set_quoted('FWUPD_GIT_DESCRIBE', describe) | |
25 | endif | |
26 | endif | |
27 | 24 | |
28 | 25 | # libtool versioning - this applies to libfwupd |
29 | 26 | # |
49 | 46 | |
50 | 47 | # get supported warning flags |
51 | 48 | warning_flags = [ |
52 | '-fstack-protector-strong', | |
53 | 49 | '-Waggregate-return', |
54 | 50 | '-Wunused', |
55 | 51 | '-Warray-bounds', |
106 | 102 | cc = meson.get_compiler('c') |
107 | 103 | add_project_arguments(cc.get_supported_arguments(warning_flags), language : 'c') |
108 | 104 | |
105 | if not meson.is_cross_build() | |
106 | add_project_arguments('-fstack-protector-strong', language : 'c') | |
107 | endif | |
108 | ||
109 | 109 | # enable full RELRO where possible |
110 | 110 | # FIXME: until https://github.com/mesonbuild/meson/issues/1140 is fixed |
111 | 111 | global_link_args = [] |
164 | 164 | endif |
165 | 165 | if build_standalone |
166 | 166 | gmodule = dependency('gmodule-2.0') |
167 | gudev = dependency('gudev-1.0') | |
168 | if gudev.version().version_compare('>= 232') | |
169 | conf.set('HAVE_GUDEV_232', '1') | |
170 | endif | |
167 | gudev = dependency('gudev-1.0', version : '>= 232') | |
171 | 168 | libxmlb = dependency('xmlb', version : '>= 0.1.13', fallback : ['libxmlb', 'libxmlb_dep']) |
172 | 169 | gusb = dependency('gusb', version : '>= 0.2.9') |
173 | 170 | sqlite = dependency('sqlite3') |
25 | 25 | }; |
26 | 26 | |
27 | 27 | G_DEFINE_TYPE (FuAltosDevice, fu_altos_device, FU_TYPE_USB_DEVICE) |
28 | ||
29 | #ifndef HAVE_GUDEV_232 | |
30 | #pragma clang diagnostic push | |
31 | #pragma clang diagnostic ignored "-Wunused-function" | |
32 | G_DEFINE_AUTOPTR_CLEANUP_FUNC(GUdevClient, g_object_unref) | |
33 | #pragma clang diagnostic pop | |
34 | #endif | |
35 | 28 | |
36 | 29 | static void |
37 | 30 | fu_altos_device_finalize (GObject *object) |
73 | 73 | }; |
74 | 74 | |
75 | 75 | G_DEFINE_TYPE (FuAtaDevice, fu_ata_device, FU_TYPE_UDEV_DEVICE) |
76 | ||
77 | #ifndef HAVE_GUDEV_232 | |
78 | #pragma clang diagnostic push | |
79 | #pragma clang diagnostic ignored "-Wunused-function" | |
80 | G_DEFINE_AUTOPTR_CLEANUP_FUNC(GUdevDevice, g_object_unref) | |
81 | #pragma clang diagnostic pop | |
82 | #endif | |
83 | 76 | |
84 | 77 | guint8 |
85 | 78 | fu_ata_device_get_transfer_mode (FuAtaDevice *self) |
103 | 103 | g_debug ("attempt %d/%d: set control transfer failed: %s", |
104 | 104 | i, HID_MAX_RETRIES, |
105 | 105 | error_local->message); |
106 | sleep (1); | |
106 | g_usleep (G_USEC_PER_SEC); | |
107 | 107 | } |
108 | 108 | } |
109 | 109 | if (actual_len != 192) { |
415 | 415 | } |
416 | 416 | |
417 | 417 | /* get header and payload */ |
418 | fw_hdr = fu_firmware_get_image_by_id_bytes (firmware, "header", error); | |
418 | fw_hdr = fu_firmware_get_image_by_id_bytes (firmware, | |
419 | FU_FIRMWARE_IMAGE_ID_HEADER, | |
420 | error); | |
419 | 421 | if (fw_hdr == NULL) |
420 | 422 | return FALSE; |
421 | fw_payload = fu_firmware_get_image_by_id_bytes (firmware, "payload", error); | |
423 | fw_payload = fu_firmware_get_image_by_id_bytes (firmware, | |
424 | FU_FIRMWARE_IMAGE_ID_PAYLOAD, | |
425 | error); | |
422 | 426 | if (fw_payload == NULL) |
423 | 427 | return FALSE; |
424 | 428 | |
507 | 511 | /* when doing a soft-reboot the device does not re-enumerate properly |
508 | 512 | * so manually reboot the GUsbDevice */ |
509 | 513 | fu_device_set_status (device, FWUPD_STATUS_DEVICE_RESTART); |
510 | if (!g_usb_device_reset (usb_device, error)) { | |
511 | g_prefix_error (error, "failed to force-reset device: "); | |
514 | if (!g_usb_device_reset (usb_device, &error_local)) { | |
515 | g_prefix_error (&error_local, "failed to force-reset device: "); | |
516 | if (fu_device_has_flag (device, FWUPD_DEVICE_FLAG_WILL_DISAPPEAR)) { | |
517 | fu_device_set_remove_delay (device, 0); | |
518 | g_debug ("%s", error_local->message); | |
519 | return TRUE; | |
520 | } | |
521 | g_propagate_error (error, g_steal_pointer (&error_local)); | |
512 | 522 | return FALSE; |
513 | 523 | } |
514 | 524 |
77 | 77 | |
78 | 78 | /* add header */ |
79 | 79 | fw_hdr = g_bytes_new_from_bytes (fw, 0x0, sizeof(FuEbitdoFirmwareHeader)); |
80 | fu_firmware_image_set_id (img_hdr, "header"); | |
80 | fu_firmware_image_set_id (img_hdr, FU_FIRMWARE_IMAGE_ID_HEADER); | |
81 | 81 | fu_firmware_image_set_bytes (img_hdr, fw_hdr); |
82 | 82 | fu_firmware_add_image (firmware, img_hdr); |
83 | 83 | |
84 | 84 | /* add payload */ |
85 | 85 | fw_payload = g_bytes_new_from_bytes (fw, sizeof(FuEbitdoFirmwareHeader), payload_len); |
86 | fu_firmware_image_set_id (img_payload, "payload"); | |
86 | fu_firmware_image_set_id (img_payload, FU_FIRMWARE_IMAGE_ID_PAYLOAD); | |
87 | 87 | fu_firmware_image_set_addr (img_payload, GUINT32_FROM_LE(hdr->destination_addr)); |
88 | 88 | fu_firmware_image_set_bytes (img_payload, fw_payload); |
89 | 89 | fu_firmware_add_image (firmware, img_payload); |
694 | 694 | /* this is a safe default, even using USBv1 */ |
695 | 695 | self->blocksz = 512; |
696 | 696 | fu_device_add_flag (FU_DEVICE (self), FWUPD_DEVICE_FLAG_UPDATABLE); |
697 | fu_device_add_flag (FU_DEVICE (self), FWUPD_DEVICE_FLAG_IS_BOOTLOADER); | |
697 | 698 | fu_device_set_remove_delay (FU_DEVICE (self), FASTBOOT_REMOVE_DELAY_RE_ENUMERATE); |
698 | 699 | } |
699 | 700 |
0 | Logitech HID Support | |
1 | ================ | |
2 | ||
3 | Introduction | |
4 | ------------ | |
5 | ||
6 | This plugin can flash the firmware on Logitech Unifying dongles, both the | |
7 | Nordic (U0007) device and the Texas Instruments (U0008) version. | |
8 | ||
9 | This plugin will not work with the different "Nano" dongle (U0010) as it does | |
10 | not use the Unifying protocol. | |
11 | ||
12 | Some bootloader protocol information was taken from the Mousejack[1] project, | |
13 | specifically logitech-usb-restore.py and unifying.py. Other documentation was | |
14 | supplied by Logitech. | |
15 | ||
16 | Additional constants were taken from the Solaar[2] project. | |
17 | ||
18 | Firmware Format | |
19 | --------------- | |
20 | ||
21 | The daemon will decompress the cabinet archive and extract a firmware blob in | |
22 | a vendor-specific format that appears to be a subset of the Intel HEX format. | |
23 | ||
24 | This plugin supports the following protocol IDs: | |
25 | ||
26 | * com.logitech.unifying | |
27 | * com.logitech.unifyingsigned | |
28 | ||
29 | GUID Generation | |
30 | --------------- | |
31 | ||
32 | These devices use the standard USB DeviceInstanceId values when in DFU mode: | |
33 | ||
34 | * `USB\VID_046D&PID_AAAA&REV_0001` | |
35 | * `USB\VID_046D&PID_AAAA` | |
36 | * `USB\VID_046D` | |
37 | ||
38 | When in runtime mode, the HID raw DeviceInstanceId values are used: | |
39 | ||
40 | * `HIDRAW\VEN_046D&DEV_C52B` | |
41 | * `HIDRAW\VEN_046D` | |
42 | ||
43 | Design Notes | |
44 | ------------ | |
45 | ||
46 | When a dongle is detected in bootloader mode we detach the hidraw driver from | |
47 | the kernel and use raw control transfers. This ensures that we don't accidentally | |
48 | corrupt the uploading firmware. For application firmware we use hidraw which | |
49 | means the hardware keeps working while probing, and also allows us to detect | |
50 | paired devices. | |
51 | ||
52 | [1] https://www.mousejack.com/ | |
53 | [2] https://pwr-Solaar.github.io/Solaar/ |
Binary diff not shown
Binary diff not shown
0 | Bus 001 Device 036: ID 046d:aaaa Logitech, Inc. | |
1 | Device Descriptor: | |
2 | bLength 18 | |
3 | bDescriptorType 1 | |
4 | bcdUSB 2.00 | |
5 | bDeviceClass 0 | |
6 | bDeviceSubClass 0 | |
7 | bDeviceProtocol 0 | |
8 | bMaxPacketSize0 32 | |
9 | idVendor 0x046d Logitech, Inc. | |
10 | idProduct 0xaaaa | |
11 | bcdDevice 1.02 | |
12 | iManufacturer 1 | |
13 | iProduct 2 | |
14 | iSerial 0 | |
15 | bNumConfigurations 1 | |
16 | Configuration Descriptor: | |
17 | bLength 9 | |
18 | bDescriptorType 2 | |
19 | wTotalLength 34 | |
20 | bNumInterfaces 1 | |
21 | bConfigurationValue 1 | |
22 | iConfiguration 4 | |
23 | bmAttributes 0x80 | |
24 | (Bus Powered) | |
25 | MaxPower 98mA | |
26 | Interface Descriptor: | |
27 | bLength 9 | |
28 | bDescriptorType 4 | |
29 | bInterfaceNumber 0 | |
30 | bAlternateSetting 0 | |
31 | bNumEndpoints 1 | |
32 | bInterfaceClass 3 Human Interface Device | |
33 | bInterfaceSubClass 0 | |
34 | bInterfaceProtocol 0 | |
35 | iInterface 0 | |
36 | HID Device Descriptor: | |
37 | bLength 9 | |
38 | bDescriptorType 33 | |
39 | bcdHID 1.11 | |
40 | bCountryCode 0 Not supported | |
41 | bNumDescriptors 1 | |
42 | bDescriptorType 34 Report | |
43 | wDescriptorLength 25 | |
44 | Report Descriptors: | |
45 | ** UNAVAILABLE ** | |
46 | Endpoint Descriptor: | |
47 | bLength 7 | |
48 | bDescriptorType 5 | |
49 | bEndpointAddress 0x81 EP 1 IN | |
50 | bmAttributes 3 | |
51 | Transfer Type Interrupt | |
52 | Synch Type None | |
53 | Usage Type Data | |
54 | wMaxPacketSize 0x0020 1x 32 bytes | |
55 | bInterval 1 |
0 | Bus 001 Device 049: ID 046d:c52b Logitech, Inc. Unifying Receiver | |
1 | Device Descriptor: | |
2 | bLength 18 | |
3 | bDescriptorType 1 | |
4 | bcdUSB 2.00 | |
5 | bDeviceClass 0 | |
6 | bDeviceSubClass 0 | |
7 | bDeviceProtocol 0 | |
8 | bMaxPacketSize0 8 | |
9 | idVendor 0x046d Logitech, Inc. | |
10 | idProduct 0xc52b Unifying Receiver | |
11 | bcdDevice 12.07 | |
12 | iManufacturer 1 Logitech | |
13 | iProduct 2 USB Receiver | |
14 | iSerial 0 | |
15 | bNumConfigurations 1 | |
16 | Configuration Descriptor: | |
17 | bLength 9 | |
18 | bDescriptorType 2 | |
19 | wTotalLength 84 | |
20 | bNumInterfaces 3 | |
21 | bConfigurationValue 1 | |
22 | iConfiguration 4 RQR12.07_B0029 | |
23 | bmAttributes 0xa0 | |
24 | (Bus Powered) | |
25 | Remote Wakeup | |
26 | MaxPower 98mA | |
27 | Interface Descriptor: | |
28 | bLength 9 | |
29 | bDescriptorType 4 | |
30 | bInterfaceNumber 0 | |
31 | bAlternateSetting 0 | |
32 | bNumEndpoints 1 | |
33 | bInterfaceClass 3 Human Interface Device | |
34 | bInterfaceSubClass 1 Boot Interface Subclass | |
35 | bInterfaceProtocol 1 Keyboard | |
36 | iInterface 0 | |
37 | HID Device Descriptor: | |
38 | bLength 9 | |
39 | bDescriptorType 33 | |
40 | bcdHID 1.11 | |
41 | bCountryCode 0 Not supported | |
42 | bNumDescriptors 1 | |
43 | bDescriptorType 34 Report | |
44 | wDescriptorLength 59 | |
45 | Report Descriptor: (length is 59) | |
46 | Endpoint Descriptor: | |
47 | bLength 7 | |
48 | bDescriptorType 5 | |
49 | bEndpointAddress 0x81 EP 1 IN | |
50 | bmAttributes 3 | |
51 | Transfer Type Interrupt | |
52 | Synch Type None | |
53 | Usage Type Data | |
54 | wMaxPacketSize 0x0008 1x 8 bytes | |
55 | bInterval 8 | |
56 | Interface Descriptor: | |
57 | bLength 9 | |
58 | bDescriptorType 4 | |
59 | bInterfaceNumber 1 | |
60 | bAlternateSetting 0 | |
61 | bNumEndpoints 1 | |
62 | bInterfaceClass 3 Human Interface Device | |
63 | bInterfaceSubClass 1 Boot Interface Subclass | |
64 | bInterfaceProtocol 2 Mouse | |
65 | iInterface 0 | |
66 | HID Device Descriptor: | |
67 | bLength 9 | |
68 | bDescriptorType 33 | |
69 | bcdHID 1.11 | |
70 | bCountryCode 0 Not supported | |
71 | bNumDescriptors 1 | |
72 | bDescriptorType 34 Report | |
73 | wDescriptorLength 148 | |
74 | Report Descriptors: | |
75 | ** UNAVAILABLE ** | |
76 | Endpoint Descriptor: | |
77 | bLength 7 | |
78 | bDescriptorType 5 | |
79 | bEndpointAddress 0x82 EP 2 IN | |
80 | bmAttributes 3 | |
81 | Transfer Type Interrupt | |
82 | Synch Type None | |
83 | Usage Type Data | |
84 | wMaxPacketSize 0x0008 1x 8 bytes | |
85 | bInterval 2 | |
86 | Interface Descriptor: | |
87 | bLength 9 | |
88 | bDescriptorType 4 | |
89 | bInterfaceNumber 2 | |
90 | bAlternateSetting 0 | |
91 | bNumEndpoints 1 | |
92 | bInterfaceClass 3 Human Interface Device | |
93 | bInterfaceSubClass 0 | |
94 | bInterfaceProtocol 0 | |
95 | iInterface 0 | |
96 | HID Device Descriptor: | |
97 | bLength 9 | |
98 | bDescriptorType 33 | |
99 | bcdHID 1.11 | |
100 | bCountryCode 0 Not supported | |
101 | bNumDescriptors 1 | |
102 | bDescriptorType 34 Report | |
103 | wDescriptorLength 93 | |
104 | Report Descriptors: | |
105 | ** UNAVAILABLE ** | |
106 | Endpoint Descriptor: | |
107 | bLength 7 | |
108 | bDescriptorType 5 | |
109 | bEndpointAddress 0x83 EP 3 IN | |
110 | bmAttributes 3 | |
111 | Transfer Type Interrupt | |
112 | Synch Type None | |
113 | Usage Type Data | |
114 | wMaxPacketSize 0x0020 1x 32 bytes | |
115 | bInterval 2 | |
116 | Device Status: 0x0000 | |
117 | (Bus Powered) |
0 | ||
1 | Bus 003 Device 036: ID 046d:aaac Logitech, Inc. | |
2 | Device Descriptor: | |
3 | bLength 18 | |
4 | bDescriptorType 1 | |
5 | bcdUSB 2.00 | |
6 | bDeviceClass 0 | |
7 | bDeviceSubClass 0 | |
8 | bDeviceProtocol 0 | |
9 | bMaxPacketSize0 32 | |
10 | idVendor 0x046d Logitech, Inc. | |
11 | idProduct 0xaaac | |
12 | bcdDevice 3.01 | |
13 | iManufacturer 1 Logitech | |
14 | iProduct 2 USB BootLoader | |
15 | iSerial 0 | |
16 | bNumConfigurations 1 | |
17 | Configuration Descriptor: | |
18 | bLength 9 | |
19 | bDescriptorType 2 | |
20 | wTotalLength 34 | |
21 | bNumInterfaces 1 | |
22 | bConfigurationValue 1 | |
23 | iConfiguration 4 BOT03.01_B0008 | |
24 | bmAttributes 0x80 | |
25 | (Bus Powered) | |
26 | MaxPower 98mA | |
27 | Interface Descriptor: | |
28 | bLength 9 | |
29 | bDescriptorType 4 | |
30 | bInterfaceNumber 0 | |
31 | bAlternateSetting 0 | |
32 | bNumEndpoints 1 | |
33 | bInterfaceClass 3 Human Interface Device | |
34 | bInterfaceSubClass 0 | |
35 | bInterfaceProtocol 0 | |
36 | iInterface 0 | |
37 | HID Device Descriptor: | |
38 | bLength 9 | |
39 | bDescriptorType 33 | |
40 | bcdHID 1.11 | |
41 | bCountryCode 0 Not supported | |
42 | bNumDescriptors 1 | |
43 | bDescriptorType 34 Report | |
44 | wDescriptorLength 25 | |
45 | Report Descriptor: (length is 25) | |
46 | Item(Global): Usage Page, data= [ 0xb0 0xff ] 65456 | |
47 | (null) | |
48 | Item(Local ): Usage, data= [ 0x01 ] 1 | |
49 | (null) | |
50 | Item(Main ): Collection, data= [ 0x01 ] 1 | |
51 | Application | |
52 | Item(Global): Report Size, data= [ 0x08 ] 8 | |
53 | Item(Global): Report Count, data= [ 0x20 ] 32 | |
54 | Item(Global): Logical Minimum, data= [ 0x00 ] 0 | |
55 | Item(Global): Logical Maximum, data= [ 0xff 0x00 ] 255 | |
56 | Item(Local ): Usage, data= [ 0x01 ] 1 | |
57 | (null) | |
58 | Item(Main ): Input, data= [ 0x00 ] 0 | |
59 | Data Array Absolute No_Wrap Linear | |
60 | Preferred_State No_Null_Position Non_Volatile Bitfield | |
61 | Item(Local ): Usage, data= [ 0x01 ] 1 | |
62 | (null) | |
63 | Item(Main ): Output, data= [ 0x00 ] 0 | |
64 | Data Array Absolute No_Wrap Linear | |
65 | Preferred_State No_Null_Position Non_Volatile Bitfield | |
66 | Item(Main ): End Collection, data=none | |
67 | Endpoint Descriptor: | |
68 | bLength 7 | |
69 | bDescriptorType 5 | |
70 | bEndpointAddress 0x81 EP 1 IN | |
71 | bmAttributes 3 | |
72 | Transfer Type Interrupt | |
73 | Synch Type None | |
74 | Usage Type Data | |
75 | wMaxPacketSize 0x0020 1x 32 bytes | |
76 | bInterval 1 | |
77 | Device Status: 0x0000 | |
78 | (Bus Powered) |
0 | ||
1 | Bus 003 Device 039: ID 046d:aaac Logitech, Inc. | |
2 | Device Descriptor: | |
3 | bLength 18 | |
4 | bDescriptorType 1 | |
5 | bcdUSB 2.00 | |
6 | bDeviceClass 0 | |
7 | bDeviceSubClass 0 | |
8 | bDeviceProtocol 0 | |
9 | bMaxPacketSize0 32 | |
10 | idVendor 0x046d Logitech, Inc. | |
11 | idProduct 0xaaac | |
12 | bcdDevice 3.00 | |
13 | iManufacturer 1 Logitech | |
14 | iProduct 2 USB BootLoader | |
15 | iSerial 0 | |
16 | bNumConfigurations 1 | |
17 | Configuration Descriptor: | |
18 | bLength 9 | |
19 | bDescriptorType 2 | |
20 | wTotalLength 34 | |
21 | bNumInterfaces 1 | |
22 | bConfigurationValue 1 | |
23 | iConfiguration 4 BOT03.00_B0006 | |
24 | bmAttributes 0x80 | |
25 | (Bus Powered) | |
26 | MaxPower 98mA | |
27 | Interface Descriptor: | |
28 | bLength 9 | |
29 | bDescriptorType 4 | |
30 | bInterfaceNumber 0 | |
31 | bAlternateSetting 0 | |
32 | bNumEndpoints 1 | |
33 | bInterfaceClass 3 Human Interface Device | |
34 | bInterfaceSubClass 0 | |
35 | bInterfaceProtocol 0 | |
36 | iInterface 0 | |
37 | HID Device Descriptor: | |
38 | bLength 9 | |
39 | bDescriptorType 33 | |
40 | bcdHID 1.11 | |
41 | bCountryCode 0 Not supported | |
42 | bNumDescriptors 1 | |
43 | bDescriptorType 34 Report | |
44 | wDescriptorLength 25 | |
45 | Report Descriptor: (length is 25) | |
46 | Item(Global): Usage Page, data= [ 0xb0 0xff ] 65456 | |
47 | (null) | |
48 | Item(Local ): Usage, data= [ 0x01 ] 1 | |
49 | (null) | |
50 | Item(Main ): Collection, data= [ 0x01 ] 1 | |
51 | Application | |
52 | Item(Global): Report Size, data= [ 0x08 ] 8 | |
53 | Item(Global): Report Count, data= [ 0x20 ] 32 | |
54 | Item(Global): Logical Minimum, data= [ 0x00 ] 0 | |
55 | Item(Global): Logical Maximum, data= [ 0xff 0x00 ] 255 | |
56 | Item(Local ): Usage, data= [ 0x01 ] 1 | |
57 | (null) | |
58 | Item(Main ): Input, data= [ 0x00 ] 0 | |
59 | Data Array Absolute No_Wrap Linear | |
60 | Preferred_State No_Null_Position Non_Volatile Bitfield | |
61 | Item(Local ): Usage, data= [ 0x01 ] 1 | |
62 | (null) | |
63 | Item(Main ): Output, data= [ 0x00 ] 0 | |
64 | Data Array Absolute No_Wrap Linear | |
65 | Preferred_State No_Null_Position Non_Volatile Bitfield | |
66 | Item(Main ): End Collection, data=none | |
67 | Endpoint Descriptor: | |
68 | bLength 7 | |
69 | bDescriptorType 5 | |
70 | bEndpointAddress 0x81 EP 1 IN | |
71 | bmAttributes 3 | |
72 | Transfer Type Interrupt | |
73 | Synch Type None | |
74 | Usage Type Data | |
75 | wMaxPacketSize 0x0020 1x 32 bytes | |
76 | bInterval 1 | |
77 | Device Status: 0x0000 | |
78 | (Bus Powered) |
0 | ||
1 | Bus 003 Device 033: ID 046d:c52b Logitech, Inc. Unifying Receiver | |
2 | Device Descriptor: | |
3 | bLength 18 | |
4 | bDescriptorType 1 | |
5 | bcdUSB 2.00 | |
6 | bDeviceClass 0 | |
7 | bDeviceSubClass 0 | |
8 | bDeviceProtocol 0 | |
9 | bMaxPacketSize0 32 | |
10 | idVendor 0x046d Logitech, Inc. | |
11 | idProduct 0xc52b Unifying Receiver | |
12 | bcdDevice 24.01 | |
13 | iManufacturer 1 Logitech | |
14 | iProduct 2 USB Receiver | |
15 | iSerial 0 | |
16 | bNumConfigurations 1 | |
17 | Configuration Descriptor: | |
18 | bLength 9 | |
19 | bDescriptorType 2 | |
20 | wTotalLength 84 | |
21 | bNumInterfaces 3 | |
22 | bConfigurationValue 1 | |
23 | iConfiguration 4 RQR24.01_B0023 | |
24 | bmAttributes 0xa0 | |
25 | (Bus Powered) | |
26 | Remote Wakeup | |
27 | MaxPower 98mA | |
28 | Interface Descriptor: | |
29 | bLength 9 | |
30 | bDescriptorType 4 | |
31 | bInterfaceNumber 0 | |
32 | bAlternateSetting 0 | |
33 | bNumEndpoints 1 | |
34 | bInterfaceClass 3 Human Interface Device | |
35 | bInterfaceSubClass 1 Boot Interface Subclass | |
36 | bInterfaceProtocol 1 Keyboard | |
37 | iInterface 0 | |
38 | HID Device Descriptor: | |
39 | bLength 9 | |
40 | bDescriptorType 33 | |
41 | bcdHID 1.11 | |
42 | bCountryCode 0 Not supported | |
43 | bNumDescriptors 1 | |
44 | bDescriptorType 34 Report | |
45 | wDescriptorLength 59 | |
46 | Report Descriptor: (length is 59) | |
47 | Item(Global): Usage Page, data= [ 0x01 ] 1 | |
48 | Generic Desktop Controls | |
49 | Item(Local ): Usage, data= [ 0x06 ] 6 | |
50 | Keyboard | |
51 | Item(Main ): Collection, data= [ 0x01 ] 1 | |
52 | Application | |
53 | Item(Global): Usage Page, data= [ 0x07 ] 7 | |
54 | Keyboard | |
55 | Item(Local ): Usage Minimum, data= [ 0xe0 ] 224 | |
56 | Control Left | |
57 | Item(Local ): Usage Maximum, data= [ 0xe7 ] 231 | |
58 | GUI Right | |
59 | Item(Global): Logical Minimum, data= [ 0x00 ] 0 | |
60 | Item(Global): Logical Maximum, data= [ 0x01 ] 1 | |
61 | Item(Global): Report Size, data= [ 0x01 ] 1 | |
62 | Item(Global): Report Count, data= [ 0x08 ] 8 | |
63 | Item(Main ): Input, data= [ 0x02 ] 2 | |
64 | Data Variable Absolute No_Wrap Linear | |
65 | Preferred_State No_Null_Position Non_Volatile Bitfield | |
66 | Item(Main ): Input, data= [ 0x03 ] 3 | |
67 | Constant Variable Absolute No_Wrap Linear | |
68 | Preferred_State No_Null_Position Non_Volatile Bitfield | |
69 | Item(Global): Report Count, data= [ 0x05 ] 5 | |
70 | Item(Global): Usage Page, data= [ 0x08 ] 8 | |
71 | LEDs | |
72 | Item(Local ): Usage Minimum, data= [ 0x01 ] 1 | |
73 | NumLock | |
74 | Item(Local ): Usage Maximum, data= [ 0x05 ] 5 | |
75 | Kana | |
76 | Item(Main ): Output, data= [ 0x02 ] 2 | |
77 | Data Variable Absolute No_Wrap Linear | |
78 | Preferred_State No_Null_Position Non_Volatile Bitfield | |
79 | Item(Global): Report Count, data= [ 0x01 ] 1 | |
80 | Item(Global): Report Size, data= [ 0x03 ] 3 | |
81 | Item(Main ): Output, data= [ 0x01 ] 1 | |
82 | Constant Array Absolute No_Wrap Linear | |
83 | Preferred_State No_Null_Position Non_Volatile Bitfield | |
84 | Item(Global): Report Count, data= [ 0x06 ] 6 | |
85 | Item(Global): Report Size, data= [ 0x08 ] 8 | |
86 | Item(Global): Logical Minimum, data= [ 0x00 ] 0 | |
87 | Item(Global): Logical Maximum, data= [ 0xa4 0x00 ] 164 | |
88 | Item(Global): Usage Page, data= [ 0x07 ] 7 | |
89 | Keyboard | |
90 | Item(Local ): Usage Minimum, data= [ 0x00 ] 0 | |
91 | No Event | |
92 | Item(Local ): Usage Maximum, data= [ 0xa4 0x00 ] 164 | |
93 | ExSel | |
94 | Item(Main ): Input, data= [ 0x00 ] 0 | |
95 | Data Array Absolute No_Wrap Linear | |
96 | Preferred_State No_Null_Position Non_Volatile Bitfield | |
97 | Item(Main ): End Collection, data=none | |
98 | Endpoint Descriptor: | |
99 | bLength 7 | |
100 | bDescriptorType 5 | |
101 | bEndpointAddress 0x81 EP 1 IN | |
102 | bmAttributes 3 | |
103 | Transfer Type Interrupt | |
104 | Synch Type None | |
105 | Usage Type Data | |
106 | wMaxPacketSize 0x0008 1x 8 bytes | |
107 | bInterval 8 | |
108 | Interface Descriptor: | |
109 | bLength 9 | |
110 | bDescriptorType 4 | |
111 | bInterfaceNumber 1 | |
112 | bAlternateSetting 0 | |
113 | bNumEndpoints 1 | |
114 | bInterfaceClass 3 Human Interface Device | |
115 | bInterfaceSubClass 1 Boot Interface Subclass | |
116 | bInterfaceProtocol 2 Mouse | |
117 | iInterface 0 | |
118 | HID Device Descriptor: | |
119 | bLength 9 | |
120 | bDescriptorType 33 | |
121 | bcdHID 1.11 | |
122 | bCountryCode 0 Not supported | |
123 | bNumDescriptors 1 | |
124 | bDescriptorType 34 Report | |
125 | wDescriptorLength 148 | |
126 | Report Descriptor: (length is 148) | |
127 | Item(Global): Usage Page, data= [ 0x01 ] 1 | |
128 | Generic Desktop Controls | |
129 | Item(Local ): Usage, data= [ 0x02 ] 2 | |
130 | Mouse | |
131 | Item(Main ): Collection, data= [ 0x01 ] 1 | |
132 | Application | |
133 | Item(Global): Report ID, data= [ 0x02 ] 2 | |
134 | Item(Local ): Usage, data= [ 0x01 ] 1 | |
135 | Pointer | |
136 | Item(Main ): Collection, data= [ 0x00 ] 0 | |
137 | Physical | |
138 | Item(Global): Usage Page, data= [ 0x09 ] 9 | |
139 | Buttons | |
140 | Item(Local ): Usage Minimum, data= [ 0x01 ] 1 | |
141 | Button 1 (Primary) | |
142 | Item(Local ): Usage Maximum, data= [ 0x10 ] 16 | |
143 | (null) | |
144 | Item(Global): Logical Minimum, data= [ 0x00 ] 0 | |
145 | Item(Global): Logical Maximum, data= [ 0x01 ] 1 | |
146 | Item(Global): Report Count, data= [ 0x10 ] 16 | |
147 | Item(Global): Report Size, data= [ 0x01 ] 1 | |
148 | Item(Main ): Input, data= [ 0x02 ] 2 | |
149 | Data Variable Absolute No_Wrap Linear | |
150 | Preferred_State No_Null_Position Non_Volatile Bitfield | |
151 | Item(Global): Usage Page, data= [ 0x01 ] 1 | |
152 | Generic Desktop Controls | |
153 | Item(Global): Logical Minimum, data= [ 0x01 0xf8 ] 63489 | |
154 | Item(Global): Logical Maximum, data= [ 0xff 0x07 ] 2047 | |
155 | Item(Global): Report Size, data= [ 0x0c ] 12 | |
156 | Item(Global): Report Count, data= [ 0x02 ] 2 | |
157 | Item(Local ): Usage, data= [ 0x30 ] 48 | |
158 | Direction-X | |
159 | Item(Local ): Usage, data= [ 0x31 ] 49 | |
160 | Direction-Y | |
161 | Item(Main ): Input, data= [ 0x06 ] 6 | |
162 | Data Variable Relative No_Wrap Linear | |
163 | Preferred_State No_Null_Position Non_Volatile Bitfield | |
164 | Item(Global): Logical Minimum, data= [ 0x81 ] 129 | |
165 | Item(Global): Logical Maximum, data= [ 0x7f ] 127 | |
166 | Item(Global): Report Size, data= [ 0x08 ] 8 | |
167 | Item(Global): Report Count, data= [ 0x01 ] 1 | |
168 | Item(Local ): Usage, data= [ 0x38 ] 56 | |
169 | Wheel | |
170 | Item(Main ): Input, data= [ 0x06 ] 6 | |
171 | Data Variable Relative No_Wrap Linear | |
172 | Preferred_State No_Null_Position Non_Volatile Bitfield | |
173 | Item(Global): Usage Page, data= [ 0x0c ] 12 | |
174 | Consumer | |
175 | Item(Local ): Usage, data= [ 0x38 0x02 ] 568 | |
176 | AC Pan | |
177 | Item(Global): Report Count, data= [ 0x01 ] 1 | |
178 | Item(Main ): Input, data= [ 0x06 ] 6 | |
179 | Data Variable Relative No_Wrap Linear | |
180 | Preferred_State No_Null_Position Non_Volatile Bitfield | |
181 | Item(Main ): End Collection, data=none | |
182 | Item(Main ): End Collection, data=none | |
183 | Item(Global): Usage Page, data= [ 0x0c ] 12 | |
184 | Consumer | |
185 | Item(Local ): Usage, data= [ 0x01 ] 1 | |
186 | Consumer Control | |
187 | Item(Main ): Collection, data= [ 0x01 ] 1 | |
188 | Application | |
189 | Item(Global): Report ID, data= [ 0x03 ] 3 | |
190 | Item(Global): Report Size, data= [ 0x10 ] 16 | |
191 | Item(Global): Report Count, data= [ 0x02 ] 2 | |
192 | Item(Global): Logical Minimum, data= [ 0x01 ] 1 | |
193 | Item(Global): Logical Maximum, data= [ 0x8c 0x02 ] 652 | |
194 | Item(Local ): Usage Minimum, data= [ 0x01 ] 1 | |
195 | Consumer Control | |
196 | Item(Local ): Usage Maximum, data= [ 0x8c 0x02 ] 652 | |
197 | (null) | |
198 | Item(Main ): Input, data= [ 0x00 ] 0 | |
199 | Data Array Absolute No_Wrap Linear | |
200 | Preferred_State No_Null_Position Non_Volatile Bitfield | |
201 | Item(Main ): End Collection, data=none | |
202 | Item(Global): Usage Page, data= [ 0x01 ] 1 | |
203 | Generic Desktop Controls | |
204 | Item(Local ): Usage, data= [ 0x80 ] 128 | |
205 | System Control | |
206 | Item(Main ): Collection, data= [ 0x01 ] 1 | |
207 | Application | |
208 | Item(Global): Report ID, data= [ 0x04 ] 4 | |
209 | Item(Global): Report Size, data= [ 0x02 ] 2 | |
210 | Item(Global): Report Count, data= [ 0x01 ] 1 | |
211 | Item(Global): Logical Minimum, data= [ 0x01 ] 1 | |
212 | Item(Global): Logical Maximum, data= [ 0x03 ] 3 | |
213 | Item(Local ): Usage, data= [ 0x82 ] 130 | |
214 | System Sleep | |
215 | Item(Local ): Usage, data= [ 0x81 ] 129 | |
216 | System Power Down | |
217 | Item(Local ): Usage, data= [ 0x83 ] 131 | |
218 | System Wake Up | |
219 | Item(Main ): Input, data= [ 0x60 ] 96 | |
220 | Data Array Absolute No_Wrap Linear | |
221 | No_Preferred_State Null_State Non_Volatile Bitfield | |
222 | Item(Global): Report Size, data= [ 0x06 ] 6 | |
223 | Item(Main ): Input, data= [ 0x03 ] 3 | |
224 | Constant Variable Absolute No_Wrap Linear | |
225 | Preferred_State No_Null_Position Non_Volatile Bitfield | |
226 | Item(Main ): End Collection, data=none | |
227 | Item(Global): Usage Page, data= [ 0xbc 0xff ] 65468 | |
228 | (null) | |
229 | Item(Local ): Usage, data= [ 0x88 ] 136 | |
230 | (null) | |
231 | Item(Main ): Collection, data= [ 0x01 ] 1 | |
232 | Application | |
233 | Item(Global): Report ID, data= [ 0x08 ] 8 | |
234 | Item(Local ): Usage Minimum, data= [ 0x01 ] 1 | |
235 | (null) | |
236 | Item(Local ): Usage Maximum, data= [ 0xff ] 255 | |
237 | (null) | |
238 | Item(Global): Logical Minimum, data= [ 0x01 ] 1 | |
239 | Item(Global): Logical Maximum, data= [ 0xff 0x00 ] 255 | |
240 | Item(Global): Report Size, data= [ 0x08 ] 8 | |
241 | Item(Global): Report Count, data= [ 0x01 ] 1 | |
242 | Item(Main ): Input, data= [ 0x00 ] 0 | |
243 | Data Array Absolute No_Wrap Linear | |
244 | Preferred_State No_Null_Position Non_Volatile Bitfield | |
245 | Item(Main ): End Collection, data=none | |
246 | Endpoint Descriptor: | |
247 | bLength 7 | |
248 | bDescriptorType 5 | |
249 | bEndpointAddress 0x82 EP 2 IN | |
250 | bmAttributes 3 | |
251 | Transfer Type Interrupt | |
252 | Synch Type None | |
253 | Usage Type Data | |
254 | wMaxPacketSize 0x0008 1x 8 bytes | |
255 | bInterval 2 | |
256 | Interface Descriptor: | |
257 | bLength 9 | |
258 | bDescriptorType 4 | |
259 | bInterfaceNumber 2 | |
260 | bAlternateSetting 0 | |
261 | bNumEndpoints 1 | |
262 | bInterfaceClass 3 Human Interface Device | |
263 | bInterfaceSubClass 0 | |
264 | bInterfaceProtocol 0 | |
265 | iInterface 0 | |
266 | HID Device Descriptor: | |
267 | bLength 9 | |
268 | bDescriptorType 33 | |
269 | bcdHID 1.11 | |
270 | bCountryCode 0 Not supported | |
271 | bNumDescriptors 1 | |
272 | bDescriptorType 34 Report | |
273 | wDescriptorLength 98 | |
274 | Report Descriptor: (length is 98) | |
275 | Item(Global): Usage Page, data= [ 0x00 0xff ] 65280 | |
276 | (null) | |
277 | Item(Local ): Usage, data= [ 0x01 ] 1 | |
278 | (null) | |
279 | Item(Main ): Collection, data= [ 0x01 ] 1 | |
280 | Application | |
281 | Item(Global): Report ID, data= [ 0x10 ] 16 | |
282 | Item(Global): Report Size, data= [ 0x08 ] 8 | |
283 | Item(Global): Report Count, data= [ 0x06 ] 6 | |
284 | Item(Global): Logical Minimum, data= [ 0x00 ] 0 | |
285 | Item(Global): Logical Maximum, data= [ 0xff 0x00 ] 255 | |
286 | Item(Local ): Usage, data= [ 0x01 ] 1 | |
287 | (null) | |
288 | Item(Main ): Input, data= [ 0x00 ] 0 | |
289 | Data Array Absolute No_Wrap Linear | |
290 | Preferred_State No_Null_Position Non_Volatile Bitfield | |
291 | Item(Local ): Usage, data= [ 0x01 ] 1 | |
292 | (null) | |
293 | Item(Main ): Output, data= [ 0x00 ] 0 | |
294 | Data Array Absolute No_Wrap Linear | |
295 | Preferred_State No_Null_Position Non_Volatile Bitfield | |
296 | Item(Main ): End Collection, data=none | |
297 | Item(Global): Usage Page, data= [ 0x00 0xff ] 65280 | |
298 | (null) | |
299 | Item(Local ): Usage, data= [ 0x02 ] 2 | |
300 | (null) | |
301 | Item(Main ): Collection, data= [ 0x01 ] 1 | |
302 | Application | |
303 | Item(Global): Report ID, data= [ 0x11 ] 17 | |
304 | Item(Global): Report Size, data= [ 0x08 ] 8 | |
305 | Item(Global): Report Count, data= [ 0x13 ] 19 | |
306 | Item(Global): Logical Minimum, data= [ 0x00 ] 0 | |
307 | Item(Global): Logical Maximum, data= [ 0xff 0x00 ] 255 | |
308 | Item(Local ): Usage, data= [ 0x02 ] 2 | |
309 | (null) | |
310 | Item(Main ): Input, data= [ 0x00 ] 0 | |
311 | Data Array Absolute No_Wrap Linear | |
312 | Preferred_State No_Null_Position Non_Volatile Bitfield | |
313 | Item(Local ): Usage, data= [ 0x02 ] 2 | |
314 | (null) | |
315 | Item(Main ): Output, data= [ 0x00 ] 0 | |
316 | Data Array Absolute No_Wrap Linear | |
317 | Preferred_State No_Null_Position Non_Volatile Bitfield | |
318 | Item(Main ): End Collection, data=none | |
319 | Item(Global): Usage Page, data= [ 0x00 0xff ] 65280 | |
320 | (null) | |
321 | Item(Local ): Usage, data= [ 0x04 ] 4 | |
322 | (null) | |
323 | Item(Main ): Collection, data= [ 0x01 ] 1 | |
324 | Application | |
325 | Item(Global): Report ID, data= [ 0x20 ] 32 | |
326 | Item(Global): Report Size, data= [ 0x08 ] 8 | |
327 | Item(Global): Report Count, data= [ 0x0e ] 14 | |
328 | Item(Global): Logical Minimum, data= [ 0x00 ] 0 | |
329 | Item(Global): Logical Maximum, data= [ 0xff 0x00 ] 255 | |
330 | Item(Local ): Usage, data= [ 0x41 ] 65 | |
331 | (null) | |
332 | Item(Main ): Input, data= [ 0x00 ] 0 | |
333 | Data Array Absolute No_Wrap Linear | |
334 | Preferred_State No_Null_Position Non_Volatile Bitfield | |
335 | Item(Local ): Usage, data= [ 0x41 ] 65 | |
336 | (null) | |
337 | Item(Main ): Output, data= [ 0x00 ] 0 | |
338 | Data Array Absolute No_Wrap Linear | |
339 | Preferred_State No_Null_Position Non_Volatile Bitfield | |
340 | Item(Global): Report ID, data= [ 0x21 ] 33 | |
341 | Item(Global): Report Count, data= [ 0x1f ] 31 | |
342 | Item(Global): Logical Minimum, data= [ 0x00 ] 0 | |
343 | Item(Global): Logical Maximum, data= [ 0xff 0x00 ] 255 | |
344 | Item(Local ): Usage, data= [ 0x42 ] 66 | |
345 | (null) | |
346 | Item(Main ): Input, data= [ 0x00 ] 0 | |
347 | Data Array Absolute No_Wrap Linear | |
348 | Preferred_State No_Null_Position Non_Volatile Bitfield | |
349 | Item(Local ): Usage, data= [ 0x42 ] 66 | |
350 | (null) | |
351 | Item(Main ): Output, data= [ 0x00 ] 0 | |
352 | Data Array Absolute No_Wrap Linear | |
353 | Preferred_State No_Null_Position Non_Volatile Bitfield | |
354 | Item(Main ): End Collection, data=none | |
355 | Endpoint Descriptor: | |
356 | bLength 7 | |
357 | bDescriptorType 5 | |
358 | bEndpointAddress 0x83 EP 3 IN | |
359 | bmAttributes 3 | |
360 | Transfer Type Interrupt | |
361 | Synch Type None | |
362 | Usage Type Data | |
363 | wMaxPacketSize 0x0020 1x 32 bytes | |
364 | bInterval 2 | |
365 | Device Status: 0x0000 | |
366 | (Bus Powered) |
0 | Bus 003 Device 032: ID 046d:c52b Logitech, Inc. Unifying Receiver | |
1 | Device Descriptor: | |
2 | bLength 18 | |
3 | bDescriptorType 1 | |
4 | bcdUSB 2.00 | |
5 | bDeviceClass 0 | |
6 | bDeviceSubClass 0 | |
7 | bDeviceProtocol 0 | |
8 | bMaxPacketSize0 32 | |
9 | idVendor 0x046d Logitech, Inc. | |
10 | idProduct 0xc52b Unifying Receiver | |
11 | bcdDevice 24.05 | |
12 | iManufacturer 1 Logitech | |
13 | iProduct 2 USB Receiver | |
14 | iSerial 0 | |
15 | bNumConfigurations 1 | |
16 | Configuration Descriptor: | |
17 | bLength 9 | |
18 | bDescriptorType 2 | |
19 | wTotalLength 84 | |
20 | bNumInterfaces 3 | |
21 | bConfigurationValue 1 | |
22 | iConfiguration 4 RQR24.05_B0029 | |
23 | bmAttributes 0xa0 | |
24 | (Bus Powered) | |
25 | Remote Wakeup | |
26 | MaxPower 98mA | |
27 | Interface Descriptor: | |
28 | bLength 9 | |
29 | bDescriptorType 4 | |
30 | bInterfaceNumber 0 | |
31 | bAlternateSetting 0 | |
32 | bNumEndpoints 1 | |
33 | bInterfaceClass 3 Human Interface Device | |
34 | bInterfaceSubClass 1 Boot Interface Subclass | |
35 | bInterfaceProtocol 1 Keyboard | |
36 | iInterface 0 | |
37 | HID Device Descriptor: | |
38 | bLength 9 | |
39 | bDescriptorType 33 | |
40 | bcdHID 1.11 | |
41 | bCountryCode 0 Not supported | |
42 | bNumDescriptors 1 | |
43 | bDescriptorType 34 Report | |
44 | wDescriptorLength 59 | |
45 | Report Descriptor: (length is 59) | |
46 | Item(Global): Usage Page, data= [ 0x01 ] 1 | |
47 | Generic Desktop Controls | |
48 | Item(Local ): Usage, data= [ 0x06 ] 6 | |
49 | Keyboard | |
50 | Item(Main ): Collection, data= [ 0x01 ] 1 | |
51 | Application | |
52 | Item(Global): Usage Page, data= [ 0x07 ] 7 | |
53 | Keyboard | |
54 | Item(Local ): Usage Minimum, data= [ 0xe0 ] 224 | |
55 | Control Left | |
56 | Item(Local ): Usage Maximum, data= [ 0xe7 ] 231 | |
57 | GUI Right | |
58 | Item(Global): Logical Minimum, data= [ 0x00 ] 0 | |
59 | Item(Global): Logical Maximum, data= [ 0x01 ] 1 | |
60 | Item(Global): Report Size, data= [ 0x01 ] 1 | |
61 | Item(Global): Report Count, data= [ 0x08 ] 8 | |
62 | Item(Main ): Input, data= [ 0x02 ] 2 | |
63 | Data Variable Absolute No_Wrap Linear | |
64 | Preferred_State No_Null_Position Non_Volatile Bitfield | |
65 | Item(Main ): Input, data= [ 0x03 ] 3 | |
66 | Constant Variable Absolute No_Wrap Linear | |
67 | Preferred_State No_Null_Position Non_Volatile Bitfield | |
68 | Item(Global): Report Count, data= [ 0x05 ] 5 | |
69 | Item(Global): Usage Page, data= [ 0x08 ] 8 | |
70 | LEDs | |
71 | Item(Local ): Usage Minimum, data= [ 0x01 ] 1 | |
72 | NumLock | |
73 | Item(Local ): Usage Maximum, data= [ 0x05 ] 5 | |
74 | Kana | |
75 | Item(Main ): Output, data= [ 0x02 ] 2 | |
76 | Data Variable Absolute No_Wrap Linear | |
77 | Preferred_State No_Null_Position Non_Volatile Bitfield | |
78 | Item(Global): Report Count, data= [ 0x01 ] 1 | |
79 | Item(Global): Report Size, data= [ 0x03 ] 3 | |
80 | Item(Main ): Output, data= [ 0x01 ] 1 | |
81 | Constant Array Absolute No_Wrap Linear | |
82 | Preferred_State No_Null_Position Non_Volatile Bitfield | |
83 | Item(Global): Report Count, data= [ 0x06 ] 6 | |
84 | Item(Global): Report Size, data= [ 0x08 ] 8 | |
85 | Item(Global): Logical Minimum, data= [ 0x00 ] 0 | |
86 | Item(Global): Logical Maximum, data= [ 0xa4 0x00 ] 164 | |
87 | Item(Global): Usage Page, data= [ 0x07 ] 7 | |
88 | Keyboard | |
89 | Item(Local ): Usage Minimum, data= [ 0x00 ] 0 | |
90 | No Event | |
91 | Item(Local ): Usage Maximum, data= [ 0xa4 0x00 ] 164 | |
92 | ExSel | |
93 | Item(Main ): Input, data= [ 0x00 ] 0 | |
94 | Data Array Absolute No_Wrap Linear | |
95 | Preferred_State No_Null_Position Non_Volatile Bitfield | |
96 | Item(Main ): End Collection, data=none | |
97 | Endpoint Descriptor: | |
98 | bLength 7 | |
99 | bDescriptorType 5 | |
100 | bEndpointAddress 0x81 EP 1 IN | |
101 | bmAttributes 3 | |
102 | Transfer Type Interrupt | |
103 | Synch Type None | |
104 | Usage Type Data | |
105 | wMaxPacketSize 0x0008 1x 8 bytes | |
106 | bInterval 8 | |
107 | Interface Descriptor: | |
108 | bLength 9 | |
109 | bDescriptorType 4 | |
110 | bInterfaceNumber 1 | |
111 | bAlternateSetting 0 | |
112 | bNumEndpoints 1 | |
113 | bInterfaceClass 3 Human Interface Device | |
114 | bInterfaceSubClass 1 Boot Interface Subclass | |
115 | bInterfaceProtocol 2 Mouse | |
116 | iInterface 0 | |
117 | HID Device Descriptor: | |
118 | bLength 9 | |
119 | bDescriptorType 33 | |
120 | bcdHID 1.11 | |
121 | bCountryCode 0 Not supported | |
122 | bNumDescriptors 1 | |
123 | bDescriptorType 34 Report | |
124 | wDescriptorLength 148 | |
125 | Report Descriptor: (length is 148) | |
126 | Item(Global): Usage Page, data= [ 0x01 ] 1 | |
127 | Generic Desktop Controls | |
128 | Item(Local ): Usage, data= [ 0x02 ] 2 | |
129 | Mouse | |
130 | Item(Main ): Collection, data= [ 0x01 ] 1 | |
131 | Application | |
132 | Item(Global): Report ID, data= [ 0x02 ] 2 | |
133 | Item(Local ): Usage, data= [ 0x01 ] 1 | |
134 | Pointer | |
135 | Item(Main ): Collection, data= [ 0x00 ] 0 | |
136 | Physical | |
137 | Item(Global): Usage Page, data= [ 0x09 ] 9 | |
138 | Buttons | |
139 | Item(Local ): Usage Minimum, data= [ 0x01 ] 1 | |
140 | Button 1 (Primary) | |
141 | Item(Local ): Usage Maximum, data= [ 0x10 ] 16 | |
142 | (null) | |
143 | Item(Global): Logical Minimum, data= [ 0x00 ] 0 | |
144 | Item(Global): Logical Maximum, data= [ 0x01 ] 1 | |
145 | Item(Global): Report Count, data= [ 0x10 ] 16 | |
146 | Item(Global): Report Size, data= [ 0x01 ] 1 | |
147 | Item(Main ): Input, data= [ 0x02 ] 2 | |
148 | Data Variable Absolute No_Wrap Linear | |
149 | Preferred_State No_Null_Position Non_Volatile Bitfield | |
150 | Item(Global): Usage Page, data= [ 0x01 ] 1 | |
151 | Generic Desktop Controls | |
152 | Item(Global): Logical Minimum, data= [ 0x01 0xf8 ] 63489 | |
153 | Item(Global): Logical Maximum, data= [ 0xff 0x07 ] 2047 | |
154 | Item(Global): Report Size, data= [ 0x0c ] 12 | |
155 | Item(Global): Report Count, data= [ 0x02 ] 2 | |
156 | Item(Local ): Usage, data= [ 0x30 ] 48 | |
157 | Direction-X | |
158 | Item(Local ): Usage, data= [ 0x31 ] 49 | |
159 | Direction-Y | |
160 | Item(Main ): Input, data= [ 0x06 ] 6 | |
161 | Data Variable Relative No_Wrap Linear | |
162 | Preferred_State No_Null_Position Non_Volatile Bitfield | |
163 | Item(Global): Logical Minimum, data= [ 0x81 ] 129 | |
164 | Item(Global): Logical Maximum, data= [ 0x7f ] 127 | |
165 | Item(Global): Report Size, data= [ 0x08 ] 8 | |
166 | Item(Global): Report Count, data= [ 0x01 ] 1 | |
167 | Item(Local ): Usage, data= [ 0x38 ] 56 | |
168 | Wheel | |
169 | Item(Main ): Input, data= [ 0x06 ] 6 | |
170 | Data Variable Relative No_Wrap Linear | |
171 | Preferred_State No_Null_Position Non_Volatile Bitfield | |
172 | Item(Global): Usage Page, data= [ 0x0c ] 12 | |
173 | Consumer | |
174 | Item(Local ): Usage, data= [ 0x38 0x02 ] 568 | |
175 | AC Pan | |
176 | Item(Global): Report Count, data= [ 0x01 ] 1 | |
177 | Item(Main ): Input, data= [ 0x06 ] 6 | |
178 | Data Variable Relative No_Wrap Linear | |
179 | Preferred_State No_Null_Position Non_Volatile Bitfield | |
180 | Item(Main ): End Collection, data=none | |
181 | Item(Main ): End Collection, data=none | |
182 | Item(Global): Usage Page, data= [ 0x0c ] 12 | |
183 | Consumer | |
184 | Item(Local ): Usage, data= [ 0x01 ] 1 | |
185 | Consumer Control | |
186 | Item(Main ): Collection, data= [ 0x01 ] 1 | |
187 | Application | |
188 | Item(Global): Report ID, data= [ 0x03 ] 3 | |
189 | Item(Global): Report Size, data= [ 0x10 ] 16 | |
190 | Item(Global): Report Count, data= [ 0x02 ] 2 | |
191 | Item(Global): Logical Minimum, data= [ 0x01 ] 1 | |
192 | Item(Global): Logical Maximum, data= [ 0x8c 0x02 ] 652 | |
193 | Item(Local ): Usage Minimum, data= [ 0x01 ] 1 | |
194 | Consumer Control | |
195 | Item(Local ): Usage Maximum, data= [ 0x8c 0x02 ] 652 | |
196 | (null) | |
197 | Item(Main ): Input, data= [ 0x00 ] 0 | |
198 | Data Array Absolute No_Wrap Linear | |
199 | Preferred_State No_Null_Position Non_Volatile Bitfield | |
200 | Item(Main ): End Collection, data=none | |
201 | Item(Global): Usage Page, data= [ 0x01 ] 1 | |
202 | Generic Desktop Controls | |
203 | Item(Local ): Usage, data= [ 0x80 ] 128 | |
204 | System Control | |
205 | Item(Main ): Collection, data= [ 0x01 ] 1 | |
206 | Application | |
207 | Item(Global): Report ID, data= [ 0x04 ] 4 | |
208 | Item(Global): Report Size, data= [ 0x02 ] 2 | |
209 | Item(Global): Report Count, data= [ 0x01 ] 1 | |
210 | Item(Global): Logical Minimum, data= [ 0x01 ] 1 | |
211 | Item(Global): Logical Maximum, data= [ 0x03 ] 3 | |
212 | Item(Local ): Usage, data= [ 0x82 ] 130 | |
213 | System Sleep | |
214 | Item(Local ): Usage, data= [ 0x81 ] 129 | |
215 | System Power Down | |
216 | Item(Local ): Usage, data= [ 0x83 ] 131 | |
217 | System Wake Up | |
218 | Item(Main ): Input, data= [ 0x60 ] 96 | |
219 | Data Array Absolute No_Wrap Linear | |
220 | No_Preferred_State Null_State Non_Volatile Bitfield | |
221 | Item(Global): Report Size, data= [ 0x06 ] 6 | |
222 | Item(Main ): Input, data= [ 0x03 ] 3 | |
223 | Constant Variable Absolute No_Wrap Linear | |
224 | Preferred_State No_Null_Position Non_Volatile Bitfield | |
225 | Item(Main ): End Collection, data=none | |
226 | Item(Global): Usage Page, data= [ 0xbc 0xff ] 65468 | |
227 | (null) | |
228 | Item(Local ): Usage, data= [ 0x88 ] 136 | |
229 | (null) | |
230 | Item(Main ): Collection, data= [ 0x01 ] 1 | |
231 | Application | |
232 | Item(Global): Report ID, data= [ 0x08 ] 8 | |
233 | Item(Local ): Usage Minimum, data= [ 0x01 ] 1 | |
234 | (null) | |
235 | Item(Local ): Usage Maximum, data= [ 0xff ] 255 | |
236 | (null) | |
237 | Item(Global): Logical Minimum, data= [ 0x01 ] 1 | |
238 | Item(Global): Logical Maximum, data= [ 0xff 0x00 ] 255 | |
239 | Item(Global): Report Size, data= [ 0x08 ] 8 | |
240 | Item(Global): Report Count, data= [ 0x01 ] 1 | |
241 | Item(Main ): Input, data= [ 0x00 ] 0 | |
242 | Data Array Absolute No_Wrap Linear | |
243 | Preferred_State No_Null_Position Non_Volatile Bitfield | |
244 | Item(Main ): End Collection, data=none | |
245 | Endpoint Descriptor: | |
246 | bLength 7 | |
247 | bDescriptorType 5 | |
248 | bEndpointAddress 0x82 EP 2 IN | |
249 | bmAttributes 3 | |
250 | Transfer Type Interrupt | |
251 | Synch Type None | |
252 | Usage Type Data | |
253 | wMaxPacketSize 0x0008 1x 8 bytes | |
254 | bInterval 2 | |
255 | Interface Descriptor: | |
256 | bLength 9 | |
257 | bDescriptorType 4 | |
258 | bInterfaceNumber 2 | |
259 | bAlternateSetting 0 | |
260 | bNumEndpoints 1 | |
261 | bInterfaceClass 3 Human Interface Device | |
262 | bInterfaceSubClass 0 | |
263 | bInterfaceProtocol 0 | |
264 | iInterface 0 | |
265 | HID Device Descriptor: | |
266 | bLength 9 | |
267 | bDescriptorType 33 | |
268 | bcdHID 1.11 | |
269 | bCountryCode 0 Not supported | |
270 | bNumDescriptors 1 | |
271 | bDescriptorType 34 Report | |
272 | wDescriptorLength 98 | |
273 | Report Descriptor: (length is 98) | |
274 | Item(Global): Usage Page, data= [ 0x00 0xff ] 65280 | |
275 | (null) | |
276 | Item(Local ): Usage, data= [ 0x01 ] 1 | |
277 | (null) | |
278 | Item(Main ): Collection, data= [ 0x01 ] 1 | |
279 | Application | |
280 | Item(Global): Report ID, data= [ 0x10 ] 16 | |
281 | Item(Global): Report Size, data= [ 0x08 ] 8 | |
282 | Item(Global): Report Count, data= [ 0x06 ] 6 | |
283 | Item(Global): Logical Minimum, data= [ 0x00 ] 0 | |
284 | Item(Global): Logical Maximum, data= [ 0xff 0x00 ] 255 | |
285 | Item(Local ): Usage, data= [ 0x01 ] 1 | |
286 | (null) | |
287 | Item(Main ): Input, data= [ 0x00 ] 0 | |
288 | Data Array Absolute No_Wrap Linear | |
289 | Preferred_State No_Null_Position Non_Volatile Bitfield | |
290 | Item(Local ): Usage, data= [ 0x01 ] 1 | |
291 | (null) | |
292 | Item(Main ): Output, data= [ 0x00 ] 0 | |
293 | Data Array Absolute No_Wrap Linear | |
294 | Preferred_State No_Null_Position Non_Volatile Bitfield | |
295 | Item(Main ): End Collection, data=none | |
296 | Item(Global): Usage Page, data= [ 0x00 0xff ] 65280 | |
297 | (null) | |
298 | Item(Local ): Usage, data= [ 0x02 ] 2 | |
299 | (null) | |
300 | Item(Main ): Collection, data= [ 0x01 ] 1 | |
301 | Application | |
302 | Item(Global): Report ID, data= [ 0x11 ] 17 | |
303 | Item(Global): Report Size, data= [ 0x08 ] 8 | |
304 | Item(Global): Report Count, data= [ 0x13 ] 19 | |
305 | Item(Global): Logical Minimum, data= [ 0x00 ] 0 | |
306 | Item(Global): Logical Maximum, data= [ 0xff 0x00 ] 255 | |
307 | Item(Local ): Usage, data= [ 0x02 ] 2 | |
308 | (null) | |
309 | Item(Main ): Input, data= [ 0x00 ] 0 | |
310 | Data Array Absolute No_Wrap Linear | |
311 | Preferred_State No_Null_Position Non_Volatile Bitfield | |
312 | Item(Local ): Usage, data= [ 0x02 ] 2 | |
313 | (null) | |
314 | Item(Main ): Output, data= [ 0x00 ] 0 | |
315 | Data Array Absolute No_Wrap Linear | |
316 | Preferred_State No_Null_Position Non_Volatile Bitfield | |
317 | Item(Main ): End Collection, data=none | |
318 | Item(Global): Usage Page, data= [ 0x00 0xff ] 65280 | |
319 | (null) | |
320 | Item(Local ): Usage, data= [ 0x04 ] 4 | |
321 | (null) | |
322 | Item(Main ): Collection, data= [ 0x01 ] 1 | |
323 | Application | |
324 | Item(Global): Report ID, data= [ 0x20 ] 32 | |
325 | Item(Global): Report Size, data= [ 0x08 ] 8 | |
326 | Item(Global): Report Count, data= [ 0x0e ] 14 | |
327 | Item(Global): Logical Minimum, data= [ 0x00 ] 0 | |
328 | Item(Global): Logical Maximum, data= [ 0xff 0x00 ] 255 | |
329 | Item(Local ): Usage, data= [ 0x41 ] 65 | |
330 | (null) | |
331 | Item(Main ): Input, data= [ 0x00 ] 0 | |
332 | Data Array Absolute No_Wrap Linear | |
333 | Preferred_State No_Null_Position Non_Volatile Bitfield | |
334 | Item(Local ): Usage, data= [ 0x41 ] 65 | |
335 | (null) | |
336 | Item(Main ): Output, data= [ 0x00 ] 0 | |
337 | Data Array Absolute No_Wrap Linear | |
338 | Preferred_State No_Null_Position Non_Volatile Bitfield | |
339 | Item(Global): Report ID, data= [ 0x21 ] 33 | |
340 | Item(Global): Report Count, data= [ 0x1f ] 31 | |
341 | Item(Global): Logical Minimum, data= [ 0x00 ] 0 | |
342 | Item(Global): Logical Maximum, data= [ 0xff 0x00 ] 255 | |
343 | Item(Local ): Usage, data= [ 0x42 ] 66 | |
344 | (null) | |
345 | Item(Main ): Input, data= [ 0x00 ] 0 | |
346 | Data Array Absolute No_Wrap Linear | |
347 | Preferred_State No_Null_Position Non_Volatile Bitfield | |
348 | Item(Local ): Usage, data= [ 0x42 ] 66 | |
349 | (null) | |
350 | Item(Main ): Output, data= [ 0x00 ] 0 | |
351 | Data Array Absolute No_Wrap Linear | |
352 | Preferred_State No_Null_Position Non_Volatile Bitfield | |
353 | Item(Main ): End Collection, data=none | |
354 | Endpoint Descriptor: | |
355 | bLength 7 | |
356 | bDescriptorType 5 | |
357 | bEndpointAddress 0x83 EP 3 IN | |
358 | bmAttributes 3 | |
359 | Transfer Type Interrupt | |
360 | Synch Type None | |
361 | Usage Type Data | |
362 | wMaxPacketSize 0x0020 1x 32 bytes | |
363 | bInterval 2 | |
364 | Device Status: 0x0000 | |
365 | (Bus Powered) |
0 | /* | |
1 | * Copyright (C) 2016-2018 Richard Hughes <richard@hughsie.com> | |
2 | * | |
3 | * SPDX-License-Identifier: LGPL-2.1+ | |
4 | */ | |
5 | ||
6 | #include "config.h" | |
7 | ||
8 | #include <string.h> | |
9 | ||
10 | #include "fu-logitech-hidpp-common.h" | |
11 | #include "fu-logitech-hidpp-bootloader-nordic.h" | |
12 | ||
13 | struct _FuLogitechHidPpBootloaderNordic | |
14 | { | |
15 | FuLogitechHidPpBootloader parent_instance; | |
16 | }; | |
17 | ||
18 | G_DEFINE_TYPE (FuLogitechHidPpBootloaderNordic, fu_logitech_hidpp_bootloader_nordic, FU_TYPE_UNIFYING_BOOTLOADER) | |
19 | ||
20 | static gchar * | |
21 | fu_logitech_hidpp_bootloader_nordic_get_hw_platform_id (FuLogitechHidPpBootloader *self, GError **error) | |
22 | { | |
23 | g_autoptr(FuLogitechHidPpBootloaderRequest) req = fu_logitech_hidpp_bootloader_request_new (); | |
24 | req->cmd = FU_UNIFYING_BOOTLOADER_CMD_GET_HW_PLATFORM_ID; | |
25 | if (!fu_logitech_hidpp_bootloader_request (self, req, error)) { | |
26 | g_prefix_error (error, "failed to get HW ID: "); | |
27 | return NULL; | |
28 | } | |
29 | return g_strndup ((const gchar *) req->data, req->len); | |
30 | } | |
31 | ||
32 | static gchar * | |
33 | fu_logitech_hidpp_bootloader_nordic_get_fw_version (FuLogitechHidPpBootloader *self, GError **error) | |
34 | { | |
35 | guint16 micro; | |
36 | ||
37 | g_autoptr(FuLogitechHidPpBootloaderRequest) req = fu_logitech_hidpp_bootloader_request_new (); | |
38 | req->cmd = FU_UNIFYING_BOOTLOADER_CMD_GET_FW_VERSION; | |
39 | if (!fu_logitech_hidpp_bootloader_request (self, req, error)) { | |
40 | g_prefix_error (error, "failed to get firmware version: "); | |
41 | return NULL; | |
42 | } | |
43 | ||
44 | /* RRRxx.yy_Bzzzz | |
45 | * 012345678901234*/ | |
46 | micro = (guint16) fu_logitech_hidpp_buffer_read_uint8 ((const gchar *) req->data + 10) << 8; | |
47 | micro += fu_logitech_hidpp_buffer_read_uint8 ((const gchar *) req->data + 12); | |
48 | return fu_logitech_hidpp_format_version ("RQR", | |
49 | fu_logitech_hidpp_buffer_read_uint8 ((const gchar *) req->data + 3), | |
50 | fu_logitech_hidpp_buffer_read_uint8 ((const gchar *) req->data + 6), | |
51 | micro); | |
52 | } | |
53 | ||
54 | static gboolean | |
55 | fu_logitech_hidpp_bootloader_nordic_setup (FuLogitechHidPpBootloader *self, GError **error) | |
56 | { | |
57 | g_autofree gchar *hw_platform_id = NULL; | |
58 | g_autofree gchar *version_fw = NULL; | |
59 | g_autoptr(GError) error_local = NULL; | |
60 | ||
61 | /* get MCU */ | |
62 | hw_platform_id = fu_logitech_hidpp_bootloader_nordic_get_hw_platform_id (self, error); | |
63 | if (hw_platform_id == NULL) | |
64 | return FALSE; | |
65 | g_debug ("hw-platform-id=%s", hw_platform_id); | |
66 | ||
67 | /* get firmware version, which is not fatal */ | |
68 | version_fw = fu_logitech_hidpp_bootloader_nordic_get_fw_version (self, &error_local); | |
69 | if (version_fw == NULL) { | |
70 | g_warning ("failed to get firmware version: %s", | |
71 | error_local->message); | |
72 | fu_device_set_version (FU_DEVICE (self), "RQR12.00_B0000", | |
73 | FWUPD_VERSION_FORMAT_PLAIN); | |
74 | } else { | |
75 | fu_device_set_version (FU_DEVICE (self), version_fw, | |
76 | FWUPD_VERSION_FORMAT_PLAIN); | |
77 | } | |
78 | ||
79 | return TRUE; | |
80 | } | |
81 | ||
82 | static gboolean | |
83 | fu_logitech_hidpp_bootloader_nordic_write_signature (FuLogitechHidPpBootloader *self, | |
84 | guint16 addr, guint8 len, const guint8 *data, | |
85 | GError **error) | |
86 | { | |
87 | g_autoptr(FuLogitechHidPpBootloaderRequest) req = fu_logitech_hidpp_bootloader_request_new(); | |
88 | req->cmd = 0xC0; | |
89 | req->addr = addr; | |
90 | req->len = len; | |
91 | memcpy (req->data, data, req->len); | |
92 | if (!fu_logitech_hidpp_bootloader_request (self, req, error)) { | |
93 | g_prefix_error (error, "failed to write sig @0x%02x: ", addr); | |
94 | return FALSE; | |
95 | } | |
96 | if (req->cmd == FU_UNIFYING_BOOTLOADER_CMD_WRITE_RAM_BUFFER_INVALID_ADDR) { | |
97 | g_set_error (error, | |
98 | G_IO_ERROR, | |
99 | G_IO_ERROR_FAILED, | |
100 | "failed to write @%04x: signature is too big", | |
101 | addr); | |
102 | return FALSE; | |
103 | } | |
104 | return TRUE; | |
105 | } | |
106 | ||
107 | static gboolean | |
108 | fu_logitech_hidpp_bootloader_nordic_write (FuLogitechHidPpBootloader *self, | |
109 | guint16 addr, guint8 len, const guint8 *data, | |
110 | GError **error) | |
111 | { | |
112 | g_autoptr(FuLogitechHidPpBootloaderRequest) req = fu_logitech_hidpp_bootloader_request_new (); | |
113 | req->cmd = FU_UNIFYING_BOOTLOADER_CMD_WRITE; | |
114 | req->addr = addr; | |
115 | req->len = len; | |
116 | if (req->len > 28) { | |
117 | g_set_error (error, | |
118 | G_IO_ERROR, | |
119 | G_IO_ERROR_FAILED, | |
120 | "failed to write @%04x: data length too large %02x", | |
121 | addr, req->len); | |
122 | return FALSE; | |
123 | } | |
124 | memcpy (req->data, data, req->len); | |
125 | if (!fu_logitech_hidpp_bootloader_request (self, req, error)) { | |
126 | g_prefix_error (error, "failed to transfer fw @0x%02x: ", addr); | |
127 | return FALSE; | |
128 | } | |
129 | if (req->cmd == FU_UNIFYING_BOOTLOADER_CMD_WRITE_INVALID_ADDR) { | |
130 | g_set_error (error, | |
131 | G_IO_ERROR, | |
132 | G_IO_ERROR_FAILED, | |
133 | "failed to write @%04x: invalid address", | |
134 | addr); | |
135 | return FALSE; | |
136 | } | |
137 | if (req->cmd == FU_UNIFYING_BOOTLOADER_CMD_WRITE_VERIFY_FAIL) { | |
138 | g_set_error (error, | |
139 | G_IO_ERROR, | |
140 | G_IO_ERROR_FAILED, | |
141 | "failed to write @%04x: failed to verify flash content", | |
142 | addr); | |
143 | return FALSE; | |
144 | } | |
145 | if (req->cmd == FU_UNIFYING_BOOTLOADER_CMD_WRITE_NONZERO_START) { | |
146 | g_debug ("wrote %d bytes at address %04x, value %02x", req->len, | |
147 | req->addr, req->data[0]); | |
148 | g_set_error (error, | |
149 | G_IO_ERROR, | |
150 | G_IO_ERROR_FAILED, | |
151 | "failed to write @%04x: only 1 byte write of 0xff supported", | |
152 | addr); | |
153 | return FALSE; | |
154 | } | |
155 | if (req->cmd == FU_UNIFYING_BOOTLOADER_CMD_WRITE_INVALID_CRC) { | |
156 | g_set_error (error, | |
157 | G_IO_ERROR, | |
158 | G_IO_ERROR_FAILED, | |
159 | "failed to write @%04x: invalid CRC", | |
160 | addr); | |
161 | return FALSE; | |
162 | } | |
163 | return TRUE; | |
164 | } | |
165 | ||
166 | static gboolean | |
167 | fu_logitech_hidpp_bootloader_nordic_erase (FuLogitechHidPpBootloader *self, guint16 addr, GError **error) | |
168 | { | |
169 | g_autoptr(FuLogitechHidPpBootloaderRequest) req = fu_logitech_hidpp_bootloader_request_new (); | |
170 | req->cmd = FU_UNIFYING_BOOTLOADER_CMD_ERASE_PAGE; | |
171 | req->addr = addr; | |
172 | req->len = 0x01; | |
173 | if (!fu_logitech_hidpp_bootloader_request (self, req, error)) { | |
174 | g_prefix_error (error, "failed to erase fw @0x%02x: ", addr); | |
175 | return FALSE; | |
176 | } | |
177 | if (req->cmd == FU_UNIFYING_BOOTLOADER_CMD_ERASE_PAGE_INVALID_ADDR) { | |
178 | g_set_error (error, | |
179 | G_IO_ERROR, | |
180 | G_IO_ERROR_FAILED, | |
181 | "failed to erase @%04x: invalid page", | |
182 | addr); | |
183 | return FALSE; | |
184 | } | |
185 | if (req->cmd == FU_UNIFYING_BOOTLOADER_CMD_ERASE_PAGE_NONZERO_START) { | |
186 | g_set_error (error, | |
187 | G_IO_ERROR, | |
188 | G_IO_ERROR_FAILED, | |
189 | "failed to erase @%04x: byte 0x00 is not 0xff", | |
190 | addr); | |
191 | return FALSE; | |
192 | } | |
193 | return TRUE; | |
194 | } | |
195 | ||
196 | static gboolean | |
197 | fu_logitech_hidpp_bootloader_nordic_write_firmware (FuDevice *device, | |
198 | FuFirmware *firmware, | |
199 | FwupdInstallFlags flags, | |
200 | GError **error) | |
201 | { | |
202 | FuLogitechHidPpBootloader *self = FU_UNIFYING_BOOTLOADER (device); | |
203 | const FuLogitechHidPpBootloaderRequest *payload; | |
204 | guint16 addr; | |
205 | g_autoptr(GBytes) fw = NULL; | |
206 | g_autoptr(GPtrArray) reqs = NULL; | |
207 | ||
208 | /* get default image */ | |
209 | fw = fu_firmware_get_image_default_bytes (firmware, error); | |
210 | if (fw == NULL) | |
211 | return FALSE; | |
212 | ||
213 | /* erase firmware pages up to the bootloader */ | |
214 | fu_device_set_status (device, FWUPD_STATUS_DEVICE_ERASE); | |
215 | for (addr = fu_logitech_hidpp_bootloader_get_addr_lo (self); | |
216 | addr < fu_logitech_hidpp_bootloader_get_addr_hi (self); | |
217 | addr += fu_logitech_hidpp_bootloader_get_blocksize (self)) { | |
218 | if (!fu_logitech_hidpp_bootloader_nordic_erase (self, addr, error)) | |
219 | return FALSE; | |
220 | } | |
221 | ||
222 | /* transfer payload */ | |
223 | reqs = fu_logitech_hidpp_bootloader_parse_requests (self, fw, error); | |
224 | if (reqs == NULL) | |
225 | return FALSE; | |
226 | fu_device_set_status (device, FWUPD_STATUS_DEVICE_WRITE); | |
227 | for (guint i = 1; i < reqs->len; i++) { | |
228 | gboolean res; | |
229 | payload = g_ptr_array_index (reqs, i); | |
230 | ||
231 | if (payload->cmd == FU_UNIFYING_BOOTLOADER_CMD_WRITE_SIGNATURE) { | |
232 | res = fu_logitech_hidpp_bootloader_nordic_write_signature (self, | |
233 | payload->addr, | |
234 | payload->len, | |
235 | payload->data, | |
236 | error); | |
237 | } else { | |
238 | res = fu_logitech_hidpp_bootloader_nordic_write (self, | |
239 | payload->addr, | |
240 | payload->len, | |
241 | payload->data, | |
242 | error); | |
243 | } | |
244 | ||
245 | if (!res) | |
246 | return FALSE; | |
247 | fu_device_set_progress_full (device, i * 32, reqs->len * 32); | |
248 | } | |
249 | ||
250 | /* send the first managed packet last, excluding the reset vector */ | |
251 | payload = g_ptr_array_index (reqs, 0); | |
252 | if (!fu_logitech_hidpp_bootloader_nordic_write (self, | |
253 | payload->addr + 1, | |
254 | payload->len - 1, | |
255 | payload->data + 1, | |
256 | error)) | |
257 | return FALSE; | |
258 | ||
259 | if (!fu_logitech_hidpp_bootloader_nordic_write (self, | |
260 | 0x0000, | |
261 | 0x01, | |
262 | payload->data, | |
263 | error)) | |
264 | return FALSE; | |
265 | ||
266 | /* mark as complete */ | |
267 | fu_device_set_progress_full (device, reqs->len * 32, reqs->len * 32); | |
268 | ||
269 | /* success! */ | |
270 | return TRUE; | |
271 | } | |
272 | ||
273 | static void | |
274 | fu_logitech_hidpp_bootloader_nordic_class_init (FuLogitechHidPpBootloaderNordicClass *klass) | |
275 | { | |
276 | FuDeviceClass *klass_device = FU_DEVICE_CLASS (klass); | |
277 | FuLogitechHidPpBootloaderClass *klass_device_bootloader = FU_UNIFYING_BOOTLOADER_CLASS (klass); | |
278 | klass_device->write_firmware = fu_logitech_hidpp_bootloader_nordic_write_firmware; | |
279 | klass_device_bootloader->setup = fu_logitech_hidpp_bootloader_nordic_setup; | |
280 | } | |
281 | ||
282 | static void | |
283 | fu_logitech_hidpp_bootloader_nordic_init (FuLogitechHidPpBootloaderNordic *self) | |
284 | { | |
285 | } |
0 | /* | |
1 | * Copyright (C) 2016-2018 Richard Hughes <richard@hughsie.com> | |
2 | * | |
3 | * SPDX-License-Identifier: LGPL-2.1+ | |
4 | */ | |
5 | ||
6 | #pragma once | |
7 | ||
8 | #include "fu-logitech-hidpp-bootloader.h" | |
9 | ||
10 | #define FU_TYPE_UNIFYING_BOOTLOADER_NORDIC (fu_logitech_hidpp_bootloader_nordic_get_type ()) | |
11 | G_DECLARE_FINAL_TYPE (FuLogitechHidPpBootloaderNordic, fu_logitech_hidpp_bootloader_nordic, FU, UNIFYING_BOOTLOADER_NORDIC, FuLogitechHidPpBootloader) |
0 | /* | |
1 | * Copyright (C) 2016-2018 Richard Hughes <richard@hughsie.com> | |
2 | * | |
3 | * SPDX-License-Identifier: LGPL-2.1+ | |
4 | */ | |
5 | ||
6 | #include "config.h" | |
7 | ||
8 | #include <string.h> | |
9 | ||
10 | #include "fu-logitech-hidpp-common.h" | |
11 | #include "fu-logitech-hidpp-bootloader-texas.h" | |
12 | ||
13 | struct _FuLogitechHidPpBootloaderTexas | |
14 | { | |
15 | FuLogitechHidPpBootloader parent_instance; | |
16 | }; | |
17 | ||
18 | G_DEFINE_TYPE (FuLogitechHidPpBootloaderTexas, fu_logitech_hidpp_bootloader_texas, FU_TYPE_UNIFYING_BOOTLOADER) | |
19 | ||
20 | static gboolean | |
21 | fu_logitech_hidpp_bootloader_texas_erase_all (FuLogitechHidPpBootloader *self, GError **error) | |
22 | { | |
23 | g_autoptr(FuLogitechHidPpBootloaderRequest) req = fu_logitech_hidpp_bootloader_request_new (); | |
24 | req->cmd = FU_UNIFYING_BOOTLOADER_CMD_FLASH_RAM; | |
25 | req->len = 0x01; /* magic number */ | |
26 | req->data[0] = 0x00; /* magic number */ | |
27 | if (!fu_logitech_hidpp_bootloader_request (self, req, error)) { | |
28 | g_prefix_error (error, "failed to erase all pages: "); | |
29 | return FALSE; | |
30 | } | |
31 | return TRUE; | |
32 | } | |
33 | ||
34 | static gboolean | |
35 | fu_logitech_hidpp_bootloader_texas_compute_and_test_crc (FuLogitechHidPpBootloader *self, GError **error) | |
36 | { | |
37 | g_autoptr(FuLogitechHidPpBootloaderRequest) req = fu_logitech_hidpp_bootloader_request_new (); | |
38 | req->cmd = FU_UNIFYING_BOOTLOADER_CMD_FLASH_RAM; | |
39 | req->len = 0x01; /* magic number */ | |
40 | req->data[0] = 0x03; /* magic number */ | |
41 | if (!fu_logitech_hidpp_bootloader_request (self, req, error)) { | |
42 | g_prefix_error (error, "failed to compute and test CRC: "); | |
43 | return FALSE; | |
44 | } | |
45 | if (req->cmd == FU_UNIFYING_BOOTLOADER_CMD_FLASH_RAM_WRONG_CRC) { | |
46 | g_set_error_literal (error, | |
47 | G_IO_ERROR, | |
48 | G_IO_ERROR_FAILED, | |
49 | "CRC is incorrect"); | |
50 | return FALSE; | |
51 | } | |
52 | return TRUE; | |
53 | } | |
54 | ||
55 | static gboolean | |
56 | fu_logitech_hidpp_bootloader_texas_flash_ram_buffer (FuLogitechHidPpBootloader *self, guint16 addr, GError **error) | |
57 | { | |
58 | g_autoptr(FuLogitechHidPpBootloaderRequest) req = fu_logitech_hidpp_bootloader_request_new (); | |
59 | req->cmd = FU_UNIFYING_BOOTLOADER_CMD_FLASH_RAM; | |
60 | req->addr = addr; | |
61 | req->len = 0x01; /* magic number */ | |
62 | req->data[0] = 0x01; /* magic number */ | |
63 | if (!fu_logitech_hidpp_bootloader_request (self, req, error)) { | |
64 | g_prefix_error (error, "failed to flash ram buffer @%04x: ", addr); | |
65 | return FALSE; | |
66 | } | |
67 | if (req->cmd == FU_UNIFYING_BOOTLOADER_CMD_FLASH_RAM_INVALID_ADDR) { | |
68 | g_set_error (error, | |
69 | G_IO_ERROR, | |
70 | G_IO_ERROR_FAILED, | |
71 | "failed to flash ram buffer @%04x: invalid flash page", | |
72 | addr); | |
73 | return FALSE; | |
74 | } | |
75 | if (req->cmd == FU_UNIFYING_BOOTLOADER_CMD_FLASH_RAM_PAGE0_INVALID) { | |
76 | g_set_error (error, | |
77 | G_IO_ERROR, | |
78 | G_IO_ERROR_FAILED, | |
79 | "failed to flash ram buffer @%04x: invalid App JMP vector", | |
80 | addr); | |
81 | return FALSE; | |
82 | } | |
83 | if (req->cmd == FU_UNIFYING_BOOTLOADER_CMD_FLASH_RAM_INVALID_ORDER) { | |
84 | g_set_error (error, | |
85 | G_IO_ERROR, | |
86 | G_IO_ERROR_FAILED, | |
87 | "failed to flash ram buffer @%04x: page flashed before page 0", | |
88 | addr); | |
89 | return FALSE; | |
90 | } | |
91 | return TRUE; | |
92 | } | |
93 | ||
94 | static gboolean | |
95 | fu_logitech_hidpp_bootloader_texas_clear_ram_buffer (FuLogitechHidPpBootloader *self, GError **error) | |
96 | { | |
97 | g_autoptr(FuLogitechHidPpBootloaderRequest) req = fu_logitech_hidpp_bootloader_request_new (); | |
98 | req->cmd = FU_UNIFYING_BOOTLOADER_CMD_FLASH_RAM; | |
99 | req->addr = 0x0000; | |
100 | req->len = 0x01; /* magic number */ | |
101 | req->data[0] = 0x02; /* magic number */ | |
102 | if (!fu_logitech_hidpp_bootloader_request (self, req, error)) { | |
103 | g_prefix_error (error, "failed to clear ram buffer @%04x: ", req->addr); | |
104 | return FALSE; | |
105 | } | |
106 | return TRUE; | |
107 | } | |
108 | ||
109 | static gboolean | |
110 | fu_logitech_hidpp_bootloader_texas_write_firmware (FuDevice *device, | |
111 | FuFirmware *firmware, | |
112 | FwupdInstallFlags flags, | |
113 | GError **error) | |
114 | { | |
115 | FuLogitechHidPpBootloader *self = FU_UNIFYING_BOOTLOADER (device); | |
116 | const FuLogitechHidPpBootloaderRequest *payload; | |
117 | g_autoptr(GBytes) fw = NULL; | |
118 | g_autoptr(GPtrArray) reqs = NULL; | |
119 | g_autoptr(FuLogitechHidPpBootloaderRequest) req = fu_logitech_hidpp_bootloader_request_new (); | |
120 | ||
121 | /* get default image */ | |
122 | fw = fu_firmware_get_image_default_bytes (firmware, error); | |
123 | if (fw == NULL) | |
124 | return FALSE; | |
125 | ||
126 | /* transfer payload */ | |
127 | reqs = fu_logitech_hidpp_bootloader_parse_requests (self, fw, error); | |
128 | if (reqs == NULL) | |
129 | return FALSE; | |
130 | ||
131 | /* erase all flash pages */ | |
132 | fu_device_set_status (device, FWUPD_STATUS_DEVICE_ERASE); | |
133 | if (!fu_logitech_hidpp_bootloader_texas_erase_all (self, error)) | |
134 | return FALSE; | |
135 | ||
136 | /* set existing RAM buffer to 0xff's */ | |
137 | if (!fu_logitech_hidpp_bootloader_texas_clear_ram_buffer (self, error)) | |
138 | return FALSE; | |
139 | ||
140 | /* write to RAM buffer */ | |
141 | fu_device_set_status (device, FWUPD_STATUS_DEVICE_WRITE); | |
142 | for (guint i = 0; i < reqs->len; i++) { | |
143 | payload = g_ptr_array_index (reqs, i); | |
144 | ||
145 | /* check size */ | |
146 | if (payload->len != 16) { | |
147 | g_set_error (error, | |
148 | G_IO_ERROR, | |
149 | G_IO_ERROR_FAILED, | |
150 | "payload size invalid @%04x: got 0x%02x", | |
151 | payload->addr, payload->len); | |
152 | return FALSE; | |
153 | } | |
154 | ||
155 | /* build packet */ | |
156 | req->cmd = payload->cmd; | |
157 | ||
158 | /* signature addresses do not need to fit inside 128 bytes */ | |
159 | if (req->cmd == FU_UNIFYING_BOOTLOADER_CMD_WRITE_SIGNATURE) | |
160 | req->addr = payload->addr; | |
161 | else | |
162 | req->addr = payload->addr % 0x80; | |
163 | ||
164 | req->len = payload->len; | |
165 | memcpy (req->data, payload->data, payload->len); | |
166 | if (!fu_logitech_hidpp_bootloader_request (self, req, error)) { | |
167 | g_prefix_error (error, | |
168 | "failed to write ram buffer @0x%02x: ", | |
169 | req->addr); | |
170 | return FALSE; | |
171 | } | |
172 | if (req->cmd == FU_UNIFYING_BOOTLOADER_CMD_WRITE_RAM_BUFFER_INVALID_ADDR) { | |
173 | g_set_error (error, | |
174 | G_IO_ERROR, | |
175 | G_IO_ERROR_FAILED, | |
176 | "failed to write ram buffer @%04x: invalid location", | |
177 | req->addr); | |
178 | return FALSE; | |
179 | } | |
180 | if (req->cmd == FU_UNIFYING_BOOTLOADER_CMD_WRITE_RAM_BUFFER_OVERFLOW) { | |
181 | g_set_error (error, | |
182 | G_IO_ERROR, | |
183 | G_IO_ERROR_FAILED, | |
184 | "failed to write ram buffer @%04x: invalid size 0x%02x", | |
185 | req->addr, req->len); | |
186 | return FALSE; | |
187 | } | |
188 | ||
189 | /* flush RAM buffer to EEPROM */ | |
190 | if ((payload->addr + 0x10) % 0x80 == 0 && | |
191 | req->cmd != FU_UNIFYING_BOOTLOADER_CMD_WRITE_SIGNATURE) { | |
192 | guint16 addr_start = payload->addr - (7 * 0x10); | |
193 | g_debug ("addr flush @ 0x%04x for 0x%04x", | |
194 | payload->addr, addr_start); | |
195 | if (!fu_logitech_hidpp_bootloader_texas_flash_ram_buffer (self, | |
196 | addr_start, | |
197 | error)) { | |
198 | g_prefix_error (error, | |
199 | "failed to flash ram buffer @0x%04x: ", | |
200 | addr_start); | |
201 | return FALSE; | |
202 | } | |
203 | } | |
204 | ||
205 | /* update progress */ | |
206 | fu_device_set_progress_full (device, i * 32, reqs->len * 32); | |
207 | } | |
208 | ||
209 | /* check CRC */ | |
210 | if (!fu_logitech_hidpp_bootloader_texas_compute_and_test_crc (self, error)) | |
211 | return FALSE; | |
212 | ||
213 | /* mark as complete */ | |
214 | fu_device_set_progress_full (device, reqs->len * 32, reqs->len * 32); | |
215 | ||
216 | /* success! */ | |
217 | return TRUE; | |
218 | } | |
219 | ||
220 | static gboolean | |
221 | fu_logitech_hidpp_bootloader_texas_setup (FuLogitechHidPpBootloader *self, GError **error) | |
222 | { | |
223 | fu_device_set_version (FU_DEVICE (self), "RQR24.00_B0000", | |
224 | FWUPD_VERSION_FORMAT_PLAIN); | |
225 | return TRUE; | |
226 | } | |
227 | ||
228 | static void | |
229 | fu_logitech_hidpp_bootloader_texas_class_init (FuLogitechHidPpBootloaderTexasClass *klass) | |
230 | { | |
231 | FuDeviceClass *klass_device = FU_DEVICE_CLASS (klass); | |
232 | FuLogitechHidPpBootloaderClass *klass_device_bootloader = FU_UNIFYING_BOOTLOADER_CLASS (klass); | |
233 | klass_device->write_firmware = fu_logitech_hidpp_bootloader_texas_write_firmware; | |
234 | klass_device_bootloader->setup = fu_logitech_hidpp_bootloader_texas_setup; | |
235 | } | |
236 | ||
237 | static void | |
238 | fu_logitech_hidpp_bootloader_texas_init (FuLogitechHidPpBootloaderTexas *self) | |
239 | { | |
240 | } |
0 | /* | |
1 | * Copyright (C) 2016-2018 Richard Hughes <richard@hughsie.com> | |
2 | * | |
3 | * SPDX-License-Identifier: LGPL-2.1+ | |
4 | */ | |
5 | ||
6 | #pragma once | |
7 | ||
8 | #include "fu-logitech-hidpp-bootloader.h" | |
9 | ||
10 | #define FU_TYPE_UNIFYING_BOOTLOADER_TEXAS (fu_logitech_hidpp_bootloader_texas_get_type ()) | |
11 | G_DECLARE_FINAL_TYPE (FuLogitechHidPpBootloaderTexas, fu_logitech_hidpp_bootloader_texas, FU, UNIFYING_BOOTLOADER_TEXAS, FuLogitechHidPpBootloader) |
0 | /* | |
1 | * Copyright (C) 2016-2018 Richard Hughes <richard@hughsie.com> | |
2 | * | |
3 | * SPDX-License-Identifier: LGPL-2.1+ | |
4 | */ | |
5 | ||
6 | #include "config.h" | |
7 | ||
8 | #include <string.h> | |
9 | ||
10 | #include "fu-firmware-common.h" | |
11 | #include "fu-logitech-hidpp-common.h" | |
12 | #include "fu-logitech-hidpp-bootloader.h" | |
13 | #include "fu-logitech-hidpp-hidpp.h" | |
14 | ||
15 | typedef struct | |
16 | { | |
17 | guint16 flash_addr_lo; | |
18 | guint16 flash_addr_hi; | |
19 | guint16 flash_blocksize; | |
20 | } FuLogitechHidPpBootloaderPrivate; | |
21 | ||
22 | #define FU_UNIFYING_DEVICE_EP1 0x81 | |
23 | #define FU_UNIFYING_DEVICE_EP3 0x83 | |
24 | ||
25 | G_DEFINE_TYPE_WITH_PRIVATE (FuLogitechHidPpBootloader, fu_logitech_hidpp_bootloader, FU_TYPE_USB_DEVICE) | |
26 | ||
27 | #define GET_PRIVATE(o) (fu_logitech_hidpp_bootloader_get_instance_private (o)) | |
28 | ||
29 | static void | |
30 | fu_logitech_hidpp_bootloader_to_string (FuDevice *device, guint idt, GString *str) | |
31 | { | |
32 | FuLogitechHidPpBootloader *self = FU_UNIFYING_BOOTLOADER (device); | |
33 | FuLogitechHidPpBootloaderPrivate *priv = GET_PRIVATE (self); | |
34 | fu_common_string_append_kx (str, idt, "FlashAddrHigh", priv->flash_addr_hi); | |
35 | fu_common_string_append_kx (str, idt, "FlashAddrLow", priv->flash_addr_lo); | |
36 | fu_common_string_append_kx (str, idt, "FlashBlockSize", priv->flash_blocksize); | |
37 | } | |
38 | ||
39 | FuLogitechHidPpBootloaderRequest * | |
40 | fu_logitech_hidpp_bootloader_request_new (void) | |
41 | { | |
42 | FuLogitechHidPpBootloaderRequest *req = g_new0 (FuLogitechHidPpBootloaderRequest, 1); | |
43 | return req; | |
44 | } | |
45 | ||
46 | GPtrArray * | |
47 | fu_logitech_hidpp_bootloader_parse_requests (FuLogitechHidPpBootloader *self, GBytes *fw, GError **error) | |
48 | { | |
49 | const gchar *tmp; | |
50 | g_auto(GStrv) lines = NULL; | |
51 | g_autoptr(GPtrArray) reqs = NULL; | |
52 | guint32 last_addr = 0; | |
53 | ||
54 | reqs = g_ptr_array_new_with_free_func (g_free); | |
55 | tmp = g_bytes_get_data (fw, NULL); | |
56 | lines = g_strsplit_set (tmp, "\n\r", -1); | |
57 | for (guint i = 0; lines[i] != NULL; i++) { | |
58 | g_autoptr(FuLogitechHidPpBootloaderRequest) payload = NULL; | |
59 | guint8 rec_type = 0x00; | |
60 | guint16 offset = 0x0000; | |
61 | gboolean exit = FALSE; | |
62 | ||
63 | /* skip empty lines */ | |
64 | tmp = lines[i]; | |
65 | if (strlen (tmp) < 5) | |
66 | continue; | |
67 | ||
68 | payload = fu_logitech_hidpp_bootloader_request_new (); | |
69 | payload->len = fu_logitech_hidpp_buffer_read_uint8 (tmp + 0x01); | |
70 | if (payload->len > 28) { | |
71 | g_set_error (error, | |
72 | G_IO_ERROR, | |
73 | G_IO_ERROR_INVALID_DATA, | |
74 | "firmware data invalid: too large %u bytes", | |
75 | payload->len); | |
76 | return NULL; | |
77 | } | |
78 | payload->addr = fu_firmware_strparse_uint16 (tmp + 0x03); | |
79 | payload->cmd = FU_UNIFYING_BOOTLOADER_CMD_WRITE_RAM_BUFFER; | |
80 | ||
81 | rec_type = fu_logitech_hidpp_buffer_read_uint8 (tmp + 0x07); | |
82 | ||
83 | switch (rec_type) { | |
84 | case 0x00: /* data */ | |
85 | break; | |
86 | case 0x01: /* EOF */ | |
87 | exit = TRUE; | |
88 | break; | |
89 | case 0x03: /* start segment address */ | |
90 | /* this is used to specify the start address, | |
91 | it is doesn't mater in this context so we can | |
92 | safely ignore it */ | |
93 | continue; | |
94 | case 0x04: /* extended linear address */ | |
95 | offset = fu_firmware_strparse_uint16 (tmp + 0x09); | |
96 | if (offset != 0x0000) { | |
97 | g_set_error (error, | |
98 | G_IO_ERROR, | |
99 | G_IO_ERROR_INVALID_DATA, | |
100 | "extended linear addresses with offset different from 0 are not supported"); | |
101 | return NULL; | |
102 | } | |
103 | continue; | |
104 | case 0x05: /* start linear address */ | |
105 | /* this is used to specify the start address, | |
106 | it is doesn't mater in this context so we can | |
107 | safely ignore it */ | |
108 | continue; | |
109 | case 0xFD: /* custom - vendor */ | |
110 | /* record type of 0xFD indicates signature data */ | |
111 | payload->cmd = FU_UNIFYING_BOOTLOADER_CMD_WRITE_SIGNATURE; | |
112 | break; | |
113 | default: | |
114 | g_set_error (error, | |
115 | G_IO_ERROR, | |
116 | G_IO_ERROR_INVALID_DATA, | |
117 | "intel hex file record type %02x not supported", | |
118 | rec_type); | |
119 | return NULL; | |
120 | } | |
121 | ||
122 | if (exit) | |
123 | break; | |
124 | ||
125 | /* read the data, but skip the checksum byte */ | |
126 | for (guint j = 0; j < payload->len; j++) { | |
127 | const gchar *ptr = tmp + 0x09 + (j * 2); | |
128 | if (ptr[0] == '\0') { | |
129 | g_set_error (error, | |
130 | G_IO_ERROR, | |
131 | G_IO_ERROR_INVALID_DATA, | |
132 | "firmware data invalid: expected %u bytes", | |
133 | payload->len); | |
134 | return NULL; | |
135 | } | |
136 | payload->data[j] = fu_logitech_hidpp_buffer_read_uint8 (ptr); | |
137 | } | |
138 | ||
139 | /* no need to bound check signature addresses */ | |
140 | if (payload->cmd == FU_UNIFYING_BOOTLOADER_CMD_WRITE_SIGNATURE) { | |
141 | g_ptr_array_add (reqs, g_steal_pointer (&payload)); | |
142 | continue; | |
143 | } | |
144 | ||
145 | /* skip the bootloader */ | |
146 | if (payload->addr > fu_logitech_hidpp_bootloader_get_addr_hi (self)) { | |
147 | g_debug ("skipping write @ %04x", payload->addr); | |
148 | continue; | |
149 | } | |
150 | ||
151 | /* skip the header */ | |
152 | if (payload->addr < fu_logitech_hidpp_bootloader_get_addr_lo (self)) { | |
153 | g_debug ("skipping write @ %04x", payload->addr); | |
154 | continue; | |
155 | } | |
156 | ||
157 | /* make sure firmware addresses only go up */ | |
158 | if (payload->addr < last_addr) { | |
159 | g_debug ("skipping write @ %04x", payload->addr); | |
160 | continue; | |
161 | } | |
162 | last_addr = payload->addr; | |
163 | ||
164 | /* pending */ | |
165 | g_ptr_array_add (reqs, g_steal_pointer (&payload)); | |
166 | } | |
167 | if (reqs->len == 0) { | |
168 | g_set_error_literal (error, | |
169 | G_IO_ERROR, | |
170 | G_IO_ERROR_INVALID_DATA, | |
171 | "firmware data invalid: no payloads found"); | |
172 | return NULL; | |
173 | } | |
174 | return g_steal_pointer (&reqs); | |
175 | } | |
176 | ||
177 | guint16 | |
178 | fu_logitech_hidpp_bootloader_get_addr_lo (FuLogitechHidPpBootloader *self) | |
179 | { | |
180 | FuLogitechHidPpBootloaderPrivate *priv = GET_PRIVATE (self); | |
181 | g_return_val_if_fail (FU_IS_UNIFYING_BOOTLOADER (self), 0x0000); | |
182 | return priv->flash_addr_lo; | |
183 | } | |
184 | ||
185 | guint16 | |
186 | fu_logitech_hidpp_bootloader_get_addr_hi (FuLogitechHidPpBootloader *self) | |
187 | { | |
188 | FuLogitechHidPpBootloaderPrivate *priv = GET_PRIVATE (self); | |
189 | g_return_val_if_fail (FU_IS_UNIFYING_BOOTLOADER (self), 0x0000); | |
190 | return priv->flash_addr_hi; | |
191 | } | |
192 | ||
193 | guint16 | |
194 | fu_logitech_hidpp_bootloader_get_blocksize (FuLogitechHidPpBootloader *self) | |
195 | { | |
196 | FuLogitechHidPpBootloaderPrivate *priv = GET_PRIVATE (self); | |
197 | g_return_val_if_fail (FU_IS_UNIFYING_BOOTLOADER (self), 0x0000); | |
198 | return priv->flash_blocksize; | |
199 | } | |
200 | ||
201 | static gboolean | |
202 | fu_logitech_hidpp_bootloader_attach (FuDevice *device, GError **error) | |
203 | { | |
204 | FuLogitechHidPpBootloader *self = FU_UNIFYING_BOOTLOADER (device); | |
205 | g_autoptr(FuLogitechHidPpBootloaderRequest) req = fu_logitech_hidpp_bootloader_request_new (); | |
206 | req->cmd = FU_UNIFYING_BOOTLOADER_CMD_REBOOT; | |
207 | if (!fu_logitech_hidpp_bootloader_request (self, req, error)) { | |
208 | g_prefix_error (error, "failed to attach back to runtime: "); | |
209 | return FALSE; | |
210 | } | |
211 | fu_device_add_flag (device, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG); | |
212 | return TRUE; | |
213 | } | |
214 | ||
215 | static gboolean | |
216 | fu_logitech_hidpp_bootloader_set_bl_version (FuLogitechHidPpBootloader *self, GError **error) | |
217 | { | |
218 | guint16 build; | |
219 | g_autofree gchar *version = NULL; | |
220 | g_autoptr(FuLogitechHidPpBootloaderRequest) req = fu_logitech_hidpp_bootloader_request_new (); | |
221 | ||
222 | /* call into hardware */ | |
223 | req->cmd = FU_UNIFYING_BOOTLOADER_CMD_GET_BL_VERSION; | |
224 | if (!fu_logitech_hidpp_bootloader_request (self, req, error)) { | |
225 | g_prefix_error (error, "failed to get firmware version: "); | |
226 | return FALSE; | |
227 | } | |
228 | ||
229 | /* BOTxx.yy_Bzzzz | |
230 | * 012345678901234 */ | |
231 | build = (guint16) fu_logitech_hidpp_buffer_read_uint8 ((const gchar *) req->data + 10) << 8; | |
232 | build += fu_logitech_hidpp_buffer_read_uint8 ((const gchar *) req->data + 12); | |
233 | version = fu_logitech_hidpp_format_version ("BOT", | |
234 | fu_logitech_hidpp_buffer_read_uint8 ((const gchar *) req->data + 3), | |
235 | fu_logitech_hidpp_buffer_read_uint8 ((const gchar *) req->data + 6), | |
236 | build); | |
237 | if (version == NULL) { | |
238 | g_prefix_error (error, "failed to format firmware version: "); | |
239 | return FALSE; | |
240 | } | |
241 | fu_device_set_version_bootloader (FU_DEVICE (self), version); | |
242 | return TRUE; | |
243 | } | |
244 | ||
245 | static gboolean | |
246 | fu_logitech_hidpp_bootloader_open (FuUsbDevice *device, GError **error) | |
247 | { | |
248 | GUsbDevice *usb_device = fu_usb_device_get_dev (device); | |
249 | const guint idx = 0x00; | |
250 | ||
251 | /* claim the only interface */ | |
252 | if (!g_usb_device_claim_interface (usb_device, idx, | |
253 | G_USB_DEVICE_CLAIM_INTERFACE_BIND_KERNEL_DRIVER, | |
254 | error)) { | |
255 | g_prefix_error (error, "Failed to claim 0x%02x: ", idx); | |
256 | return FALSE; | |
257 | } | |
258 | ||
259 | /* success */ | |
260 | return TRUE; | |
261 | } | |
262 | ||
263 | static gboolean | |
264 | fu_logitech_hidpp_bootloader_setup (FuDevice *device, GError **error) | |
265 | { | |
266 | FuLogitechHidPpBootloaderClass *klass = FU_UNIFYING_BOOTLOADER_GET_CLASS (device); | |
267 | FuLogitechHidPpBootloader *self = FU_UNIFYING_BOOTLOADER (device); | |
268 | FuLogitechHidPpBootloaderPrivate *priv = GET_PRIVATE (self); | |
269 | g_autoptr(FuLogitechHidPpBootloaderRequest) req = fu_logitech_hidpp_bootloader_request_new (); | |
270 | ||
271 | /* get memory map */ | |
272 | req->cmd = FU_UNIFYING_BOOTLOADER_CMD_GET_MEMINFO; | |
273 | if (!fu_logitech_hidpp_bootloader_request (self, req, error)) { | |
274 | g_prefix_error (error, "failed to get meminfo: "); | |
275 | return FALSE; | |
276 | } | |
277 | if (req->len != 0x06) { | |
278 | g_set_error (error, | |
279 | G_IO_ERROR, | |
280 | G_IO_ERROR_FAILED, | |
281 | "failed to get meminfo: invalid size %02x", | |
282 | req->len); | |
283 | return FALSE; | |
284 | } | |
285 | ||
286 | /* parse values */ | |
287 | priv->flash_addr_lo = fu_common_read_uint16 (req->data + 0, G_BIG_ENDIAN); | |
288 | priv->flash_addr_hi = fu_common_read_uint16 (req->data + 2, G_BIG_ENDIAN); | |
289 | priv->flash_blocksize = fu_common_read_uint16 (req->data + 4, G_BIG_ENDIAN); | |
290 | ||
291 | /* get bootloader version */ | |
292 | if (!fu_logitech_hidpp_bootloader_set_bl_version (self, error)) | |
293 | return FALSE; | |
294 | ||
295 | /* subclassed further */ | |
296 | if (klass->setup != NULL) | |
297 | return klass->setup (self, error); | |
298 | ||
299 | /* success */ | |
300 | return TRUE; | |
301 | } | |
302 | ||
303 | static gboolean | |
304 | fu_logitech_hidpp_bootloader_close (FuUsbDevice *device, GError **error) | |
305 | { | |
306 | GUsbDevice *usb_device = fu_usb_device_get_dev (device); | |
307 | if (usb_device != NULL) { | |
308 | if (!g_usb_device_release_interface (usb_device, 0x00, | |
309 | G_USB_DEVICE_CLAIM_INTERFACE_BIND_KERNEL_DRIVER, | |
310 | error)) { | |
311 | return FALSE; | |
312 | } | |
313 | } | |
314 | return TRUE; | |
315 | } | |
316 | ||
317 | gboolean | |
318 | fu_logitech_hidpp_bootloader_request (FuLogitechHidPpBootloader *self, | |
319 | FuLogitechHidPpBootloaderRequest *req, | |
320 | GError **error) | |
321 | { | |
322 | GUsbDevice *usb_device = fu_usb_device_get_dev (FU_USB_DEVICE (self)); | |
323 | gsize actual_length = 0; | |
324 | guint8 buf_request[32]; | |
325 | guint8 buf_response[32]; | |
326 | ||
327 | /* build packet */ | |
328 | memset (buf_request, 0x00, sizeof (buf_request)); | |
329 | buf_request[0x00] = req->cmd; | |
330 | buf_request[0x01] = req->addr >> 8; | |
331 | buf_request[0x02] = req->addr & 0xff; | |
332 | buf_request[0x03] = req->len; | |
333 | if (!fu_memcpy_safe (buf_request, sizeof(buf_request), 0x04, /* dst */ | |
334 | req->data, sizeof(req->data), 0x0, /* src */ | |
335 | sizeof(req->data), error)) | |
336 | return FALSE; | |
337 | ||
338 | /* send request */ | |
339 | if (g_getenv ("FWUPD_UNIFYING_VERBOSE") != NULL) { | |
340 | fu_common_dump_raw (G_LOG_DOMAIN, "host->device", | |
341 | buf_request, sizeof (buf_request)); | |
342 | } | |
343 | if (usb_device != NULL) { | |
344 | if (!g_usb_device_control_transfer (usb_device, | |
345 | G_USB_DEVICE_DIRECTION_HOST_TO_DEVICE, | |
346 | G_USB_DEVICE_REQUEST_TYPE_CLASS, | |
347 | G_USB_DEVICE_RECIPIENT_INTERFACE, | |
348 | HID_REPORT_SET, | |
349 | 0x0200, 0x0000, | |
350 | buf_request, | |
351 | sizeof (buf_request), | |
352 | &actual_length, | |
353 | FU_UNIFYING_DEVICE_TIMEOUT_MS, | |
354 | NULL, | |
355 | error)) { | |
356 | g_prefix_error (error, "failed to send data: "); | |
357 | return FALSE; | |
358 | } | |
359 | } | |
360 | ||
361 | /* no response required when rebooting */ | |
362 | if (usb_device != NULL && | |
363 | req->cmd == FU_UNIFYING_BOOTLOADER_CMD_REBOOT) { | |
364 | g_autoptr(GError) error_ignore = NULL; | |
365 | if (!g_usb_device_interrupt_transfer (usb_device, | |
366 | FU_UNIFYING_DEVICE_EP1, | |
367 | buf_response, | |
368 | sizeof (buf_response), | |
369 | &actual_length, | |
370 | FU_UNIFYING_DEVICE_TIMEOUT_MS, | |
371 | NULL, | |
372 | &error_ignore)) { | |
373 | g_debug ("ignoring: %s", error_ignore->message); | |
374 | } else { | |
375 | if (g_getenv ("FWUPD_UNIFYING_VERBOSE") != NULL) { | |
376 | fu_common_dump_raw (G_LOG_DOMAIN, "device->host", | |
377 | buf_response, actual_length); | |
378 | } | |
379 | } | |
380 | return TRUE; | |
381 | } | |
382 | ||
383 | /* get response */ | |
384 | memset (buf_response, 0x00, sizeof (buf_response)); | |
385 | if (usb_device != NULL) { | |
386 | if (!g_usb_device_interrupt_transfer (usb_device, | |
387 | FU_UNIFYING_DEVICE_EP1, | |
388 | buf_response, | |
389 | sizeof (buf_response), | |
390 | &actual_length, | |
391 | FU_UNIFYING_DEVICE_TIMEOUT_MS, | |
392 | NULL, | |
393 | error)) { | |
394 | g_prefix_error (error, "failed to get data: "); | |
395 | return FALSE; | |
396 | } | |
397 | } else { | |
398 | /* emulated */ | |
399 | buf_response[0] = buf_request[0]; | |
400 | if (buf_response[0] == FU_UNIFYING_BOOTLOADER_CMD_GET_MEMINFO) { | |
401 | buf_response[3] = 0x06; /* len */ | |
402 | buf_response[4] = 0x40; /* lo MSB */ | |
403 | buf_response[5] = 0x00; /* lo LSB */ | |
404 | buf_response[6] = 0x6b; /* hi MSB */ | |
405 | buf_response[7] = 0xff; /* hi LSB */ | |
406 | buf_response[8] = 0x00; /* bs MSB */ | |
407 | buf_response[9] = 0x80; /* bs LSB */ | |
408 | } | |
409 | actual_length = sizeof (buf_response); | |
410 | } | |
411 | if (g_getenv ("FWUPD_UNIFYING_VERBOSE") != NULL) { | |
412 | fu_common_dump_raw (G_LOG_DOMAIN, "device->host", | |
413 | buf_response, actual_length); | |
414 | } | |
415 | ||
416 | /* parse response */ | |
417 | if ((buf_response[0x00] & 0xf0) != req->cmd) { | |
418 | g_set_error (error, | |
419 | G_IO_ERROR, | |
420 | G_IO_ERROR_FAILED, | |
421 | "invalid command response of %02x, expected %02x", | |
422 | buf_response[0x00], req->cmd); | |
423 | return FALSE; | |
424 | } | |
425 | req->cmd = buf_response[0x00]; | |
426 | req->addr = ((guint16) buf_response[0x01] << 8) + buf_response[0x02]; | |
427 | req->len = buf_response[0x03]; | |
428 | if (req->len > 28) { | |
429 | g_set_error (error, | |
430 | G_IO_ERROR, | |
431 | G_IO_ERROR_FAILED, | |
432 | "invalid data size of %02x", req->len); | |
433 | return FALSE; | |
434 | } | |
435 | memset (req->data, 0x00, 28); | |
436 | if (req->len > 0) | |
437 | memcpy (req->data, buf_response + 0x04, req->len); | |
438 | return TRUE; | |
439 | } | |
440 | ||
441 | static void | |
442 | fu_logitech_hidpp_bootloader_init (FuLogitechHidPpBootloader *self) | |
443 | { | |
444 | fu_device_add_flag (FU_DEVICE (self), FWUPD_DEVICE_FLAG_UPDATABLE); | |
445 | fu_device_add_flag (FU_DEVICE (self), FWUPD_DEVICE_FLAG_IS_BOOTLOADER); | |
446 | fu_device_add_icon (FU_DEVICE (self), "preferences-desktop-keyboard"); | |
447 | fu_device_set_name (FU_DEVICE (self), "Unifying Receiver"); | |
448 | fu_device_set_summary (FU_DEVICE (self), "A miniaturised USB wireless receiver (bootloader)"); | |
449 | fu_device_set_version_format (FU_DEVICE (self), FWUPD_VERSION_FORMAT_PLAIN); | |
450 | fu_device_set_remove_delay (FU_DEVICE (self), FU_UNIFYING_DEVICE_TIMEOUT_MS); | |
451 | } | |
452 | ||
453 | static void | |
454 | fu_logitech_hidpp_bootloader_class_init (FuLogitechHidPpBootloaderClass *klass) | |
455 | { | |
456 | FuDeviceClass *klass_device = FU_DEVICE_CLASS (klass); | |
457 | FuUsbDeviceClass *klass_usb_device = FU_USB_DEVICE_CLASS (klass); | |
458 | klass_device->to_string = fu_logitech_hidpp_bootloader_to_string; | |
459 | klass_device->attach = fu_logitech_hidpp_bootloader_attach; | |
460 | klass_device->setup = fu_logitech_hidpp_bootloader_setup; | |
461 | klass_usb_device->open = fu_logitech_hidpp_bootloader_open; | |
462 | klass_usb_device->close = fu_logitech_hidpp_bootloader_close; | |
463 | } |
0 | /* | |
1 | * Copyright (C) 2016-2018 Richard Hughes <richard@hughsie.com> | |
2 | * | |
3 | * SPDX-License-Identifier: LGPL-2.1+ | |
4 | */ | |
5 | ||
6 | #pragma once | |
7 | ||
8 | #include "fu-usb-device.h" | |
9 | ||
10 | #define FU_TYPE_UNIFYING_BOOTLOADER (fu_logitech_hidpp_bootloader_get_type ()) | |
11 | G_DECLARE_DERIVABLE_TYPE (FuLogitechHidPpBootloader, fu_logitech_hidpp_bootloader, FU, UNIFYING_BOOTLOADER, FuUsbDevice) | |
12 | ||
13 | struct _FuLogitechHidPpBootloaderClass | |
14 | { | |
15 | FuUsbDeviceClass parent_class; | |
16 | gboolean (*setup) (FuLogitechHidPpBootloader *self, | |
17 | GError **error); | |
18 | }; | |
19 | ||
20 | typedef enum { | |
21 | FU_UNIFYING_BOOTLOADER_CMD_GENERAL_ERROR = 0x01, | |
22 | FU_UNIFYING_BOOTLOADER_CMD_READ = 0x10, | |
23 | FU_UNIFYING_BOOTLOADER_CMD_WRITE = 0x20, | |
24 | FU_UNIFYING_BOOTLOADER_CMD_WRITE_INVALID_ADDR = 0x21, | |
25 | FU_UNIFYING_BOOTLOADER_CMD_WRITE_VERIFY_FAIL = 0x22, | |
26 | FU_UNIFYING_BOOTLOADER_CMD_WRITE_NONZERO_START = 0x23, | |
27 | FU_UNIFYING_BOOTLOADER_CMD_WRITE_INVALID_CRC = 0x24, | |
28 | FU_UNIFYING_BOOTLOADER_CMD_ERASE_PAGE = 0x30, | |
29 | FU_UNIFYING_BOOTLOADER_CMD_ERASE_PAGE_INVALID_ADDR = 0x31, | |
30 | FU_UNIFYING_BOOTLOADER_CMD_ERASE_PAGE_NONZERO_START = 0x33, | |
31 | FU_UNIFYING_BOOTLOADER_CMD_GET_HW_PLATFORM_ID = 0x40, | |
32 | FU_UNIFYING_BOOTLOADER_CMD_GET_FW_VERSION = 0x50, | |
33 | FU_UNIFYING_BOOTLOADER_CMD_GET_CHECKSUM = 0x60, | |
34 | FU_UNIFYING_BOOTLOADER_CMD_REBOOT = 0x70, | |
35 | FU_UNIFYING_BOOTLOADER_CMD_GET_MEMINFO = 0x80, | |
36 | FU_UNIFYING_BOOTLOADER_CMD_GET_BL_VERSION = 0x90, | |
37 | FU_UNIFYING_BOOTLOADER_CMD_GET_INIT_FW_VERSION = 0xa0, | |
38 | FU_UNIFYING_BOOTLOADER_CMD_READ_SIGNATURE = 0xb0, | |
39 | FU_UNIFYING_BOOTLOADER_CMD_WRITE_RAM_BUFFER = 0xc0, | |
40 | FU_UNIFYING_BOOTLOADER_CMD_WRITE_RAM_BUFFER_INVALID_ADDR= 0xc1, | |
41 | FU_UNIFYING_BOOTLOADER_CMD_WRITE_RAM_BUFFER_OVERFLOW = 0xc2, | |
42 | FU_UNIFYING_BOOTLOADER_CMD_FLASH_RAM = 0xd0, | |
43 | FU_UNIFYING_BOOTLOADER_CMD_FLASH_RAM_INVALID_ADDR = 0xd1, | |
44 | FU_UNIFYING_BOOTLOADER_CMD_FLASH_RAM_WRONG_CRC = 0xd2, | |
45 | FU_UNIFYING_BOOTLOADER_CMD_FLASH_RAM_PAGE0_INVALID = 0xd3, | |
46 | FU_UNIFYING_BOOTLOADER_CMD_FLASH_RAM_INVALID_ORDER = 0xd4, | |
47 | FU_UNIFYING_BOOTLOADER_CMD_WRITE_SIGNATURE = 0xe0, | |
48 | FU_UNIFYING_BOOTLOADER_CMD_LAST | |
49 | } FuLogitechHidPpBootloaderCmd; | |
50 | ||
51 | /* packet to and from device */ | |
52 | typedef struct __attribute__((packed)) { | |
53 | guint8 cmd; | |
54 | guint16 addr; | |
55 | guint8 len; | |
56 | guint8 data[28]; | |
57 | } FuLogitechHidPpBootloaderRequest; | |
58 | ||
59 | FuLogitechHidPpBootloaderRequest *fu_logitech_hidpp_bootloader_request_new (void); | |
60 | ||
61 | #pragma clang diagnostic push | |
62 | #pragma clang diagnostic ignored "-Wunused-function" | |
63 | G_DEFINE_AUTOPTR_CLEANUP_FUNC(FuLogitechHidPpBootloaderRequest, g_free); | |
64 | #pragma clang diagnostic pop | |
65 | ||
66 | GPtrArray *fu_logitech_hidpp_bootloader_parse_requests (FuLogitechHidPpBootloader *self, | |
67 | GBytes *fw, | |
68 | GError **error); | |
69 | gboolean fu_logitech_hidpp_bootloader_request (FuLogitechHidPpBootloader *self, | |
70 | FuLogitechHidPpBootloaderRequest *req, | |
71 | GError **error); | |
72 | ||
73 | guint16 fu_logitech_hidpp_bootloader_get_addr_lo (FuLogitechHidPpBootloader *self); | |
74 | guint16 fu_logitech_hidpp_bootloader_get_addr_hi (FuLogitechHidPpBootloader *self); | |
75 | guint16 fu_logitech_hidpp_bootloader_get_blocksize (FuLogitechHidPpBootloader *self); |
0 | /* | |
1 | * Copyright (C) 2016-2018 Richard Hughes <richard@hughsie.com> | |
2 | * | |
3 | * SPDX-License-Identifier: LGPL-2.1+ | |
4 | */ | |
5 | ||
6 | #include "config.h" | |
7 | ||
8 | #include <string.h> | |
9 | #include <fcntl.h> | |
10 | #include <errno.h> | |
11 | #include <gio/gio.h> | |
12 | ||
13 | #include "fu-logitech-hidpp-common.h" | |
14 | ||
15 | guint8 | |
16 | fu_logitech_hidpp_buffer_read_uint8 (const gchar *str) | |
17 | { | |
18 | guint64 tmp; | |
19 | gchar buf[3] = { 0x0, 0x0, 0x0 }; | |
20 | memcpy (buf, str, 2); | |
21 | tmp = g_ascii_strtoull (buf, NULL, 16); | |
22 | return tmp; | |
23 | } | |
24 | ||
25 | guint16 | |
26 | fu_logitech_hidpp_buffer_read_uint16 (const gchar *str) | |
27 | { | |
28 | guint64 tmp; | |
29 | gchar buf[5] = { 0x0, 0x0, 0x0, 0x0, 0x0 }; | |
30 | memcpy (buf, str, 4); | |
31 | tmp = g_ascii_strtoull (buf, NULL, 16); | |
32 | return tmp; | |
33 | } | |
34 | ||
35 | gchar * | |
36 | fu_logitech_hidpp_format_version (const gchar *name, guint8 major, guint8 minor, guint16 build) | |
37 | { | |
38 | GString *str = g_string_new (NULL); | |
39 | for (guint i = 0; i < 3; i++) { | |
40 | if (g_ascii_isspace (name[i])) | |
41 | continue; | |
42 | g_string_append_c (str, name[i]); | |
43 | } | |
44 | g_string_append_printf (str, "%02x.%02x_B%04x", major, minor, build); | |
45 | return g_string_free (str, FALSE); | |
46 | } |
0 | /* | |
1 | * Copyright (C) 2016-2018 Richard Hughes <richard@hughsie.com> | |
2 | * | |
3 | * SPDX-License-Identifier: LGPL-2.1+ | |
4 | */ | |
5 | ||
6 | #pragma once | |
7 | ||
8 | #include <glib.h> | |
9 | ||
10 | #define FU_UNIFYING_DEVICE_VID 0x046d | |
11 | ||
12 | #define FU_UNIFYING_DEVICE_PID_RUNTIME 0xc52b | |
13 | #define FU_UNIFYING_DEVICE_PID_BOOTLOADER_NORDIC 0xaaaa | |
14 | #define FU_UNIFYING_DEVICE_PID_BOOTLOADER_NORDIC_PICO 0xaaae | |
15 | #define FU_UNIFYING_DEVICE_PID_BOOTLOADER_TEXAS 0xaaac | |
16 | #define FU_UNIFYING_DEVICE_PID_BOOTLOADER_TEXAS_PICO 0xaaad | |
17 | ||
18 | /* Signed firmware are very long to verify on the device */ | |
19 | #define FU_UNIFYING_DEVICE_TIMEOUT_MS 30000 | |
20 | ||
21 | guint8 fu_logitech_hidpp_buffer_read_uint8 (const gchar *str); | |
22 | guint16 fu_logitech_hidpp_buffer_read_uint16 (const gchar *str); | |
23 | ||
24 | gchar *fu_logitech_hidpp_format_version (const gchar *name, | |
25 | guint8 major, | |
26 | guint8 minor, | |
27 | guint16 build); |
0 | /* | |
1 | * Copyright (C) 2017-2018 Richard Hughes <richard@hughsie.com> | |
2 | * | |
3 | * SPDX-License-Identifier: LGPL-2.1+ | |
4 | */ | |
5 | ||
6 | #include "config.h" | |
7 | ||
8 | #include <string.h> | |
9 | ||
10 | #include "fu-logitech-hidpp-hidpp.h" | |
11 | #include "fu-logitech-hidpp-hidpp-msg.h" | |
12 | ||
13 | FuLogitechHidPpHidppMsg * | |
14 | fu_logitech_hidpp_msg_new (void) | |
15 | { | |
16 | return g_new0 (FuLogitechHidPpHidppMsg, 1); | |
17 | } | |
18 | ||
19 | const gchar * | |
20 | fu_logitech_hidpp_msg_dev_id_to_string (FuLogitechHidPpHidppMsg *msg) | |
21 | { | |
22 | g_return_val_if_fail (msg != NULL, NULL); | |
23 | if (msg->device_id == HIDPP_DEVICE_ID_WIRED) | |
24 | return "wired"; | |
25 | if (msg->device_id == HIDPP_DEVICE_ID_RECEIVER) | |
26 | return "receiver"; | |
27 | if (msg->device_id == HIDPP_DEVICE_ID_UNSET) | |
28 | return "unset"; | |
29 | return NULL; | |
30 | } | |
31 | ||
32 | const gchar * | |
33 | fu_logitech_hidpp_msg_rpt_id_to_string (FuLogitechHidPpHidppMsg *msg) | |
34 | { | |
35 | g_return_val_if_fail (msg != NULL, NULL); | |
36 | if (msg->report_id == HIDPP_REPORT_ID_SHORT) | |
37 | return "short"; | |
38 | if (msg->report_id == HIDPP_REPORT_ID_LONG) | |
39 | return "long"; | |
40 | if (msg->report_id == HIDPP_REPORT_ID_VERY_LONG) | |
41 | return "very-long"; | |
42 | return NULL; | |
43 | } | |
44 | ||
45 | gsize | |
46 | fu_logitech_hidpp_msg_get_payload_length (FuLogitechHidPpHidppMsg *msg) | |
47 | { | |
48 | if (msg->report_id == HIDPP_REPORT_ID_SHORT) | |
49 | return 0x07; | |
50 | if (msg->report_id == HIDPP_REPORT_ID_LONG) | |
51 | return 0x14; | |
52 | if (msg->report_id == HIDPP_REPORT_ID_VERY_LONG) | |
53 | return 0x2f; | |
54 | if (msg->report_id == HIDPP_REPORT_NOTIFICATION) | |
55 | return 0x08; | |
56 | return 0x0; | |
57 | } | |
58 | ||
59 | const gchar * | |
60 | fu_logitech_hidpp_msg_fcn_id_to_string (FuLogitechHidPpHidppMsg *msg) | |
61 | { | |
62 | g_return_val_if_fail (msg != NULL, NULL); | |
63 | switch (msg->sub_id) { | |
64 | case HIDPP_SUBID_SET_REGISTER: | |
65 | case HIDPP_SUBID_GET_REGISTER: | |
66 | case HIDPP_SUBID_SET_LONG_REGISTER: | |
67 | case HIDPP_SUBID_GET_LONG_REGISTER: | |
68 | case HIDPP_SUBID_SET_VERY_LONG_REGISTER: | |
69 | case HIDPP_SUBID_GET_VERY_LONG_REGISTER: | |
70 | if (msg->function_id == HIDPP_REGISTER_HIDPP_NOTIFICATIONS) | |
71 | return "hidpp-notifications"; | |
72 | if (msg->function_id == HIDPP_REGISTER_ENABLE_INDIVIDUAL_FEATURES) | |
73 | return "individual-features"; | |
74 | if (msg->function_id == HIDPP_REGISTER_BATTERY_STATUS) | |
75 | return "battery-status"; | |
76 | if (msg->function_id == HIDPP_REGISTER_BATTERY_MILEAGE) | |
77 | return "battery-mileage"; | |
78 | if (msg->function_id == HIDPP_REGISTER_PROFILE) | |
79 | return "profile"; | |
80 | if (msg->function_id == HIDPP_REGISTER_LED_STATUS) | |
81 | return "led-status"; | |
82 | if (msg->function_id == HIDPP_REGISTER_LED_INTENSITY) | |
83 | return "led-intensity"; | |
84 | if (msg->function_id == HIDPP_REGISTER_LED_COLOR) | |
85 | return "led-color"; | |
86 | if (msg->function_id == HIDPP_REGISTER_OPTICAL_SENSOR_SETTINGS) | |
87 | return "optical-sensor-settings"; | |
88 | if (msg->function_id == HIDPP_REGISTER_CURRENT_RESOLUTION) | |
89 | return "current-resolution"; | |
90 | if (msg->function_id == HIDPP_REGISTER_USB_REFRESH_RATE) | |
91 | return "usb-refresh-rate"; | |
92 | if (msg->function_id == HIDPP_REGISTER_GENERIC_MEMORY_MANAGEMENT) | |
93 | return "generic-memory-management"; | |
94 | if (msg->function_id == HIDPP_REGISTER_HOT_CONTROL) | |
95 | return "hot-control"; | |
96 | if (msg->function_id == HIDPP_REGISTER_READ_MEMORY) | |
97 | return "read-memory"; | |
98 | if (msg->function_id == HIDPP_REGISTER_DEVICE_CONNECTION_DISCONNECTION) | |
99 | return "device-connection-disconnection"; | |
100 | if (msg->function_id == HIDPP_REGISTER_PAIRING_INFORMATION) | |
101 | return "pairing-information"; | |
102 | if (msg->function_id == HIDPP_REGISTER_DEVICE_FIRMWARE_UPDATE_MODE) | |
103 | return "device-firmware-update-mode"; | |
104 | if (msg->function_id == HIDPP_REGISTER_DEVICE_FIRMWARE_INFORMATION) | |
105 | return "device-firmware-information"; | |
106 | break; | |
107 | default: | |
108 | break; | |
109 | } | |
110 | return NULL; | |
111 | ||
112 | } | |
113 | ||
114 | const gchar * | |
115 | fu_logitech_hidpp_msg_sub_id_to_string (FuLogitechHidPpHidppMsg *msg) | |
116 | { | |
117 | g_return_val_if_fail (msg != NULL, NULL); | |
118 | if (msg->sub_id == HIDPP_SUBID_VENDOR_SPECIFIC_KEYS) | |
119 | return "vendor-specific-keys"; | |
120 | if (msg->sub_id == HIDPP_SUBID_POWER_KEYS) | |
121 | return "power-keys"; | |
122 | if (msg->sub_id == HIDPP_SUBID_ROLLER) | |
123 | return "roller"; | |
124 | if (msg->sub_id == HIDPP_SUBID_MOUSE_EXTRA_BUTTONS) | |
125 | return "mouse-extra-buttons"; | |
126 | if (msg->sub_id == HIDPP_SUBID_BATTERY_CHARGING_LEVEL) | |
127 | return "battery-charging-level"; | |
128 | if (msg->sub_id == HIDPP_SUBID_USER_INTERFACE_EVENT) | |
129 | return "user-interface-event"; | |
130 | if (msg->sub_id == HIDPP_SUBID_F_LOCK_STATUS) | |
131 | return "f-lock-status"; | |
132 | if (msg->sub_id == HIDPP_SUBID_CALCULATOR_RESULT) | |
133 | return "calculator-result"; | |
134 | if (msg->sub_id == HIDPP_SUBID_MENU_NAVIGATE) | |
135 | return "menu-navigate"; | |
136 | if (msg->sub_id == HIDPP_SUBID_FN_KEY) | |
137 | return "fn-key"; | |
138 | if (msg->sub_id == HIDPP_SUBID_BATTERY_MILEAGE) | |
139 | return "battery-mileage"; | |
140 | if (msg->sub_id == HIDPP_SUBID_UART_RX) | |
141 | return "uart-rx"; | |
142 | if (msg->sub_id == HIDPP_SUBID_BACKLIGHT_DURATION_UPDATE) | |
143 | return "backlight-duration-update"; | |
144 | if (msg->sub_id == HIDPP_SUBID_DEVICE_DISCONNECTION) | |
145 | return "device-disconnection"; | |
146 | if (msg->sub_id == HIDPP_SUBID_DEVICE_CONNECTION) | |
147 | return "device-connection"; | |
148 | if (msg->sub_id == HIDPP_SUBID_DEVICE_DISCOVERY) | |
149 | return "device-discovery"; | |
150 | if (msg->sub_id == HIDPP_SUBID_PIN_CODE_REQUEST) | |
151 | return "pin-code-request"; | |
152 | if (msg->sub_id == HIDPP_SUBID_RECEIVER_WORKING_MODE) | |
153 | return "receiver-working-mode"; | |
154 | if (msg->sub_id == HIDPP_SUBID_ERROR_MESSAGE) | |
155 | return "error-message"; | |
156 | if (msg->sub_id == HIDPP_SUBID_RF_LINK_CHANGE) | |
157 | return "rf-link-change"; | |
158 | if (msg->sub_id == HIDPP_SUBID_HCI) | |
159 | return "hci"; | |
160 | if (msg->sub_id == HIDPP_SUBID_LINK_QUALITY) | |
161 | return "link-quality"; | |
162 | if (msg->sub_id == HIDPP_SUBID_DEVICE_LOCKING_CHANGED) | |
163 | return "device-locking-changed"; | |
164 | if (msg->sub_id == HIDPP_SUBID_WIRELESS_DEVICE_CHANGE) | |
165 | return "wireless-device-change"; | |
166 | if (msg->sub_id == HIDPP_SUBID_ACL) | |
167 | return "acl"; | |
168 | if (msg->sub_id == HIDPP_SUBID_VOIP_TELEPHONY_EVENT) | |
169 | return "voip-telephony-event"; | |
170 | if (msg->sub_id == HIDPP_SUBID_LED) | |
171 | return "led"; | |
172 | if (msg->sub_id == HIDPP_SUBID_GESTURE_AND_AIR) | |
173 | return "gesture-and-air"; | |
174 | if (msg->sub_id == HIDPP_SUBID_TOUCHPAD_MULTI_TOUCH) | |
175 | return "touchpad-multi-touch"; | |
176 | if (msg->sub_id == HIDPP_SUBID_TRACEABILITY) | |
177 | return "traceability"; | |
178 | if (msg->sub_id == HIDPP_SUBID_SET_REGISTER) | |
179 | return "set-register"; | |
180 | if (msg->sub_id == HIDPP_SUBID_GET_REGISTER) | |
181 | return "get-register"; | |
182 | if (msg->sub_id == HIDPP_SUBID_SET_LONG_REGISTER) | |
183 | return "set-long-register"; | |
184 | if (msg->sub_id == HIDPP_SUBID_GET_LONG_REGISTER) | |
185 | return "get-long-register"; | |
186 | if (msg->sub_id == HIDPP_SUBID_SET_VERY_LONG_REGISTER) | |
187 | return "set-very-long-register"; | |
188 | if (msg->sub_id == HIDPP_SUBID_GET_VERY_LONG_REGISTER) | |
189 | return "get-very-long-register"; | |
190 | if (msg->sub_id == HIDPP_SUBID_ERROR_MSG) | |
191 | return "error-msg"; | |
192 | if (msg->sub_id == HIDPP_SUBID_ERROR_MSG_20) | |
193 | return "error-msg-v2"; | |
194 | return NULL; | |
195 | } | |
196 | ||
197 | gboolean | |
198 | fu_logitech_hidpp_msg_is_reply (FuLogitechHidPpHidppMsg *msg1, FuLogitechHidPpHidppMsg *msg2) | |
199 | { | |
200 | g_return_val_if_fail (msg1 != NULL, FALSE); | |
201 | g_return_val_if_fail (msg2 != NULL, FALSE); | |
202 | if (msg1->device_id != msg2->device_id && | |
203 | msg1->device_id != HIDPP_DEVICE_ID_UNSET && | |
204 | msg2->device_id != HIDPP_DEVICE_ID_UNSET) | |
205 | return FALSE; | |
206 | if (msg1->flags & FU_UNIFYING_HIDPP_MSG_FLAG_IGNORE_SUB_ID || | |
207 | msg2->flags & FU_UNIFYING_HIDPP_MSG_FLAG_IGNORE_SUB_ID) | |
208 | return TRUE; | |
209 | if (msg1->sub_id != msg2->sub_id) | |
210 | return FALSE; | |
211 | if (msg1->flags & FU_UNIFYING_HIDPP_MSG_FLAG_IGNORE_FNCT_ID || | |
212 | msg2->flags & FU_UNIFYING_HIDPP_MSG_FLAG_IGNORE_FNCT_ID) | |
213 | return TRUE; | |
214 | if (msg1->function_id != msg2->function_id) | |
215 | return FALSE; | |
216 | return TRUE; | |
217 | } | |
218 | ||
219 | /* HID++ error */ | |
220 | gboolean | |
221 | fu_logitech_hidpp_msg_is_error (FuLogitechHidPpHidppMsg *msg, GError **error) | |
222 | { | |
223 | g_return_val_if_fail (msg != NULL, FALSE); | |
224 | if (msg->sub_id == HIDPP_SUBID_ERROR_MSG) { | |
225 | switch (msg->data[1]) { | |
226 | case HIDPP_ERR_INVALID_SUBID: | |
227 | g_set_error_literal (error, | |
228 | G_IO_ERROR, | |
229 | G_IO_ERROR_NOT_SUPPORTED, | |
230 | "invalid SubID"); | |
231 | break; | |
232 | case HIDPP_ERR_INVALID_ADDRESS: | |
233 | g_set_error_literal (error, | |
234 | G_IO_ERROR, | |
235 | G_IO_ERROR_INVALID_DATA, | |
236 | "invalid address"); | |
237 | break; | |
238 | case HIDPP_ERR_INVALID_VALUE: | |
239 | g_set_error_literal (error, | |
240 | G_IO_ERROR, | |
241 | G_IO_ERROR_INVALID_DATA, | |
242 | "invalid value"); | |
243 | break; | |
244 | case HIDPP_ERR_CONNECT_FAIL: | |
245 | g_set_error_literal (error, | |
246 | G_IO_ERROR, | |
247 | G_IO_ERROR_FAILED, | |
248 | "connection request failed"); | |
249 | break; | |
250 | case HIDPP_ERR_TOO_MANY_DEVICES: | |
251 | g_set_error_literal (error, | |
252 | G_IO_ERROR, | |
253 | G_IO_ERROR_NO_SPACE, | |
254 | "too many devices connected"); | |
255 | break; | |
256 | case HIDPP_ERR_ALREADY_EXISTS: | |
257 | g_set_error_literal (error, | |
258 | G_IO_ERROR, | |
259 | G_IO_ERROR_EXISTS, | |
260 | "already exists"); | |
261 | break; | |
262 | case HIDPP_ERR_BUSY: | |
263 | g_set_error_literal (error, | |
264 | G_IO_ERROR, | |
265 | G_IO_ERROR_BUSY, | |
266 | "busy"); | |
267 | break; | |
268 | case HIDPP_ERR_UNKNOWN_DEVICE: | |
269 | g_set_error_literal (error, | |
270 | G_IO_ERROR, | |
271 | G_IO_ERROR_NOT_FOUND, | |
272 | "unknown device"); | |
273 | break; | |
274 | case HIDPP_ERR_RESOURCE_ERROR: | |
275 | g_set_error_literal (error, | |
276 | G_IO_ERROR, | |
277 | G_IO_ERROR_HOST_UNREACHABLE, | |
278 | "resource error"); | |
279 | break; | |
280 | case HIDPP_ERR_REQUEST_UNAVAILABLE: | |
281 | g_set_error_literal (error, | |
282 | G_IO_ERROR, | |
283 | G_IO_ERROR_EXISTS, | |
284 | "request not valid in current context"); | |
285 | break; | |
286 | case HIDPP_ERR_INVALID_PARAM_VALUE: | |
287 | g_set_error_literal (error, | |
288 | G_IO_ERROR, | |
289 | G_IO_ERROR_INVALID_DATA, | |
290 | "request parameter has unsupported value"); | |
291 | break; | |
292 | case HIDPP_ERR_WRONG_PIN_CODE: | |
293 | g_set_error_literal (error, | |
294 | G_IO_ERROR, | |
295 | G_IO_ERROR_CONNECTION_REFUSED, | |
296 | "the pin code was wrong"); | |
297 | break; | |
298 | default: | |
299 | g_set_error_literal (error, | |
300 | G_IO_ERROR, | |
301 | G_IO_ERROR_FAILED, | |
302 | "generic failure"); | |
303 | } | |
304 | return FALSE; | |
305 | } | |
306 | if (msg->sub_id == HIDPP_SUBID_ERROR_MSG_20) { | |
307 | switch (msg->data[1]) { | |
308 | case HIDPP_ERROR_CODE_INVALID_ARGUMENT: | |
309 | g_set_error (error, | |
310 | G_IO_ERROR, | |
311 | G_IO_ERROR_INVALID_ARGUMENT, | |
312 | "Invalid argument 0x%02x", | |
313 | msg->data[2]); | |
314 | break; | |
315 | case HIDPP_ERROR_CODE_OUT_OF_RANGE: | |
316 | g_set_error_literal (error, | |
317 | G_IO_ERROR, | |
318 | G_IO_ERROR_INVALID_DATA, | |
319 | "out of range"); | |
320 | break; | |
321 | case HIDPP_ERROR_CODE_HW_ERROR: | |
322 | g_set_error_literal (error, | |
323 | G_IO_ERROR, | |
324 | G_IO_ERROR_BROKEN_PIPE, | |
325 | "hardware error"); | |
326 | break; | |
327 | case HIDPP_ERROR_CODE_INVALID_FEATURE_INDEX: | |
328 | g_set_error_literal (error, | |
329 | G_IO_ERROR, | |
330 | G_IO_ERROR_INVALID_ARGUMENT, | |
331 | "invalid feature index"); | |
332 | break; | |
333 | case HIDPP_ERROR_CODE_INVALID_FUNCTION_ID: | |
334 | g_set_error_literal (error, | |
335 | G_IO_ERROR, | |
336 | G_IO_ERROR_INVALID_ARGUMENT, | |
337 | "invalid function ID"); | |
338 | break; | |
339 | case HIDPP_ERROR_CODE_BUSY: | |
340 | g_set_error_literal (error, | |
341 | G_IO_ERROR, | |
342 | G_IO_ERROR_BUSY, | |
343 | "busy"); | |
344 | break; | |
345 | case HIDPP_ERROR_CODE_UNSUPPORTED: | |
346 | g_set_error_literal (error, | |
347 | G_IO_ERROR, | |
348 | G_IO_ERROR_NOT_SUPPORTED, | |
349 | "unsupported"); | |
350 | break; | |
351 | default: | |
352 | g_set_error_literal (error, | |
353 | G_IO_ERROR, | |
354 | G_IO_ERROR_FAILED, | |
355 | "generic failure"); | |
356 | break; | |
357 | } | |
358 | return FALSE; | |
359 | } | |
360 | return TRUE; | |
361 | } | |
362 | ||
363 | void | |
364 | fu_logitech_hidpp_msg_copy (FuLogitechHidPpHidppMsg *msg_dst, const FuLogitechHidPpHidppMsg *msg_src) | |
365 | { | |
366 | g_return_if_fail (msg_dst != NULL); | |
367 | g_return_if_fail (msg_src != NULL); | |
368 | memset (msg_dst->data, 0x00, sizeof(msg_dst->data)); | |
369 | msg_dst->device_id = msg_src->device_id; | |
370 | msg_dst->sub_id = msg_src->sub_id; | |
371 | msg_dst->function_id = msg_src->function_id; | |
372 | memcpy (msg_dst->data, msg_src->data, sizeof(msg_dst->data)); | |
373 | } | |
374 | ||
375 | /* filter HID++1.0 messages */ | |
376 | gboolean | |
377 | fu_logitech_hidpp_msg_is_hidpp10_compat (FuLogitechHidPpHidppMsg *msg) | |
378 | { | |
379 | g_return_val_if_fail (msg != NULL, FALSE); | |
380 | if (msg->sub_id == 0x40 || | |
381 | msg->sub_id == 0x41 || | |
382 | msg->sub_id == 0x49 || | |
383 | msg->sub_id == 0x4b || | |
384 | msg->sub_id == 0x8f) { | |
385 | return TRUE; | |
386 | } | |
387 | return FALSE; | |
388 | } | |
389 | ||
390 | gboolean | |
391 | fu_logitech_hidpp_msg_verify_swid (FuLogitechHidPpHidppMsg *msg) | |
392 | { | |
393 | g_return_val_if_fail (msg != NULL, FALSE); | |
394 | if ((msg->function_id & 0x0f) != FU_UNIFYING_HIDPP_MSG_SW_ID) | |
395 | return FALSE; | |
396 | return TRUE; | |
397 | } |
0 | /* | |
1 | * Copyright (C) 2017-2018 Richard Hughes <richard@hughsie.com> | |
2 | * | |
3 | * SPDX-License-Identifier: LGPL-2.1+ | |
4 | */ | |
5 | ||
6 | #pragma once | |
7 | ||
8 | #include <glib.h> | |
9 | ||
10 | typedef enum { | |
11 | FU_UNIFYING_HIDPP_MSG_FLAG_NONE, | |
12 | FU_UNIFYING_HIDPP_MSG_FLAG_LONGER_TIMEOUT = 1 << 0, | |
13 | FU_UNIFYING_HIDPP_MSG_FLAG_IGNORE_SUB_ID = 1 << 1, | |
14 | FU_UNIFYING_HIDPP_MSG_FLAG_IGNORE_FNCT_ID = 1 << 2, | |
15 | FU_UNIFYING_HIDPP_MSG_FLAG_IGNORE_SWID = 1 << 3, | |
16 | /*< private >*/ | |
17 | FU_UNIFYING_HIDPP_MSG_FLAG_LAST | |
18 | } FuLogitechHidPpHidppMsgFlags; | |
19 | ||
20 | typedef struct __attribute__((packed)) { | |
21 | guint8 report_id; | |
22 | guint8 device_id; | |
23 | guint8 sub_id; | |
24 | guint8 function_id; /* funcId:software_id */ | |
25 | guint8 data[47]; /* maximum supported by Windows XP SP2 */ | |
26 | /* not included in the packet sent to the hardware */ | |
27 | guint32 flags; | |
28 | guint8 hidpp_version; | |
29 | } FuLogitechHidPpHidppMsg; | |
30 | ||
31 | /* this is specific to fwupd */ | |
32 | #define FU_UNIFYING_HIDPP_MSG_SW_ID 0x07 | |
33 | ||
34 | #pragma clang diagnostic push | |
35 | #pragma clang diagnostic ignored "-Wunused-function" | |
36 | G_DEFINE_AUTOPTR_CLEANUP_FUNC(FuLogitechHidPpHidppMsg, g_free); | |
37 | #pragma clang diagnostic pop | |
38 | ||
39 | FuLogitechHidPpHidppMsg *fu_logitech_hidpp_msg_new (void); | |
40 | void fu_logitech_hidpp_msg_copy (FuLogitechHidPpHidppMsg *msg_dst, | |
41 | const FuLogitechHidPpHidppMsg *msg_src); | |
42 | gsize fu_logitech_hidpp_msg_get_payload_length (FuLogitechHidPpHidppMsg *msg); | |
43 | gboolean fu_logitech_hidpp_msg_is_reply (FuLogitechHidPpHidppMsg *msg1, | |
44 | FuLogitechHidPpHidppMsg *msg2); | |
45 | gboolean fu_logitech_hidpp_msg_is_hidpp10_compat (FuLogitechHidPpHidppMsg *msg); | |
46 | gboolean fu_logitech_hidpp_msg_is_error (FuLogitechHidPpHidppMsg *msg, | |
47 | GError **error); | |
48 | gboolean fu_logitech_hidpp_msg_verify_swid (FuLogitechHidPpHidppMsg *msg); | |
49 | ||
50 | const gchar *fu_logitech_hidpp_msg_dev_id_to_string (FuLogitechHidPpHidppMsg *msg); | |
51 | const gchar *fu_logitech_hidpp_msg_rpt_id_to_string (FuLogitechHidPpHidppMsg *msg); | |
52 | const gchar *fu_logitech_hidpp_msg_sub_id_to_string (FuLogitechHidPpHidppMsg *msg); | |
53 | const gchar *fu_logitech_hidpp_msg_fcn_id_to_string (FuLogitechHidPpHidppMsg *msg); |
0 | /* | |
1 | * Copyright (C) 2016-2018 Richard Hughes <richard@hughsie.com> | |
2 | * | |
3 | * SPDX-License-Identifier: LGPL-2.1+ | |
4 | */ | |
5 | ||
6 | #include "config.h" | |
7 | ||
8 | #include "fu-common.h" | |
9 | #include "fu-logitech-hidpp-common.h" | |
10 | #include "fu-logitech-hidpp-hidpp.h" | |
11 | ||
12 | static gchar * | |
13 | fu_logitech_hidpp_msg_to_string (FuLogitechHidPpHidppMsg *msg) | |
14 | { | |
15 | GString *str = g_string_new (NULL); | |
16 | const gchar *tmp; | |
17 | g_autoptr(GError) error = NULL; | |
18 | g_autoptr(GString) flags_str = g_string_new (NULL); | |
19 | ||
20 | g_return_val_if_fail (msg != NULL, NULL); | |
21 | ||
22 | if (msg->flags == FU_UNIFYING_HIDPP_MSG_FLAG_NONE) { | |
23 | g_string_append (flags_str, "none"); | |
24 | } else { | |
25 | if (msg->flags & FU_UNIFYING_HIDPP_MSG_FLAG_LONGER_TIMEOUT) | |
26 | g_string_append (flags_str, "longer-timeout,"); | |
27 | if (msg->flags & FU_UNIFYING_HIDPP_MSG_FLAG_IGNORE_SUB_ID) | |
28 | g_string_append (flags_str, "ignore-sub-id,"); | |
29 | if (msg->flags & FU_UNIFYING_HIDPP_MSG_FLAG_IGNORE_FNCT_ID) | |
30 | g_string_append (flags_str, "ignore-fnct-id,"); | |
31 | if (msg->flags & FU_UNIFYING_HIDPP_MSG_FLAG_IGNORE_SWID) | |
32 | g_string_append (flags_str, "ignore-swid,"); | |
33 | if (str->len > 0) | |
34 | g_string_truncate (str, str->len - 1); | |
35 | } | |
36 | g_string_append_printf (str, "flags: %02x [%s]\n", | |
37 | msg->flags, | |
38 | flags_str->str); | |
39 | g_string_append_printf (str, "report-id: %02x [%s]\n", | |
40 | msg->report_id, | |
41 | fu_logitech_hidpp_msg_rpt_id_to_string (msg)); | |
42 | tmp = fu_logitech_hidpp_msg_dev_id_to_string (msg); | |
43 | g_string_append_printf (str, "device-id: %02x [%s]\n", | |
44 | msg->device_id, tmp ); | |
45 | g_string_append_printf (str, "sub-id: %02x [%s]\n", | |
46 | msg->sub_id, | |
47 | fu_logitech_hidpp_msg_sub_id_to_string (msg)); | |
48 | g_string_append_printf (str, "function-id: %02x [%s]\n", | |
49 | msg->function_id, | |
50 | fu_logitech_hidpp_msg_fcn_id_to_string (msg)); | |
51 | if (!fu_logitech_hidpp_msg_is_error (msg, &error)) { | |
52 | g_string_append_printf (str, "error: %s\n", | |
53 | error->message); | |
54 | } | |
55 | return g_string_free (str, FALSE); | |
56 | } | |
57 | ||
58 | gboolean | |
59 | fu_logitech_hidpp_send (FuIOChannel *io_channel, | |
60 | FuLogitechHidPpHidppMsg *msg, | |
61 | guint timeout, | |
62 | GError **error) | |
63 | { | |
64 | gsize len = fu_logitech_hidpp_msg_get_payload_length (msg); | |
65 | ||
66 | /* only for HID++2.0 */ | |
67 | if (msg->hidpp_version >= 2.f) | |
68 | msg->function_id |= FU_UNIFYING_HIDPP_MSG_SW_ID; | |
69 | ||
70 | /* detailed debugging */ | |
71 | if (g_getenv ("FWUPD_UNIFYING_VERBOSE") != NULL) { | |
72 | g_autofree gchar *str = fu_logitech_hidpp_msg_to_string (msg); | |
73 | fu_common_dump_raw (G_LOG_DOMAIN, "host->device", (guint8 *) msg, len); | |
74 | g_print ("%s", str); | |
75 | } | |
76 | ||
77 | /* HID */ | |
78 | if (!fu_io_channel_write_raw (io_channel, (guint8 *) msg, len, 1500, | |
79 | FU_IO_CHANNEL_FLAG_FLUSH_INPUT | | |
80 | FU_IO_CHANNEL_FLAG_USE_BLOCKING_IO, error)) { | |
81 | g_prefix_error (error, "failed to send: "); | |
82 | return FALSE; | |
83 | } | |
84 | ||
85 | /* success */ | |
86 | return TRUE; | |
87 | } | |
88 | ||
89 | gboolean | |
90 | fu_logitech_hidpp_receive (FuIOChannel *io_channel, | |
91 | FuLogitechHidPpHidppMsg *msg, | |
92 | guint timeout, | |
93 | GError **error) | |
94 | { | |
95 | gsize read_size = 0; | |
96 | ||
97 | if (!fu_io_channel_read_raw (io_channel, | |
98 | (guint8 *) msg, | |
99 | sizeof(FuLogitechHidPpHidppMsg), | |
100 | &read_size, | |
101 | timeout, | |
102 | FU_IO_CHANNEL_FLAG_SINGLE_SHOT, | |
103 | error)) { | |
104 | g_prefix_error (error, "failed to receive: "); | |
105 | return FALSE; | |
106 | } | |
107 | ||
108 | /* check long enough, but allow returning oversize packets */ | |
109 | if (g_getenv ("FWUPD_UNIFYING_VERBOSE") != NULL) | |
110 | fu_common_dump_raw (G_LOG_DOMAIN, "device->host", (guint8 *) msg, read_size); | |
111 | if (read_size < fu_logitech_hidpp_msg_get_payload_length (msg)) { | |
112 | g_set_error (error, | |
113 | G_IO_ERROR, | |
114 | G_IO_ERROR_FAILED, | |
115 | "message length too small, " | |
116 | "got %" G_GSIZE_FORMAT " expected %" G_GSIZE_FORMAT, | |
117 | read_size, fu_logitech_hidpp_msg_get_payload_length (msg)); | |
118 | return FALSE; | |
119 | } | |
120 | ||
121 | /* detailed debugging */ | |
122 | if (g_getenv ("FWUPD_UNIFYING_VERBOSE") != NULL) { | |
123 | g_autofree gchar *str = fu_logitech_hidpp_msg_to_string (msg); | |
124 | g_print ("%s", str); | |
125 | } | |
126 | ||
127 | /* success */ | |
128 | return TRUE; | |
129 | } | |
130 | ||
131 | gboolean | |
132 | fu_logitech_hidpp_transfer (FuIOChannel *io_channel, FuLogitechHidPpHidppMsg *msg, GError **error) | |
133 | { | |
134 | guint timeout = FU_UNIFYING_DEVICE_TIMEOUT_MS; | |
135 | guint ignore_cnt = 0; | |
136 | g_autoptr(FuLogitechHidPpHidppMsg) msg_tmp = fu_logitech_hidpp_msg_new (); | |
137 | ||
138 | /* increase timeout for some operations */ | |
139 | if (msg->flags & FU_UNIFYING_HIDPP_MSG_FLAG_LONGER_TIMEOUT) | |
140 | timeout *= 10; | |
141 | ||
142 | /* send request */ | |
143 | if (!fu_logitech_hidpp_send (io_channel, msg, timeout, error)) | |
144 | return FALSE; | |
145 | ||
146 | /* keep trying to receive until we get a valid reply */ | |
147 | while (1) { | |
148 | msg_tmp->hidpp_version = msg->hidpp_version; | |
149 | if (!fu_logitech_hidpp_receive (io_channel, msg_tmp, timeout, error)) { | |
150 | g_prefix_error (error, "failed to receive: "); | |
151 | return FALSE; | |
152 | } | |
153 | ||
154 | /* we don't know how to handle this report packet */ | |
155 | if (fu_logitech_hidpp_msg_get_payload_length (msg_tmp) == 0x0) { | |
156 | g_debug ("HID++1.0 report 0x%02x has unknown length, ignoring", | |
157 | msg_tmp->report_id); | |
158 | continue; | |
159 | } | |
160 | ||
161 | /* maybe something is also writing to the device? -- | |
162 | * we can't use the SwID as this is a HID++2.0 feature */ | |
163 | if (!fu_logitech_hidpp_msg_is_error (msg_tmp, error)) | |
164 | return FALSE; | |
165 | ||
166 | /* is valid reply */ | |
167 | if (fu_logitech_hidpp_msg_is_reply (msg, msg_tmp)) | |
168 | break; | |
169 | ||
170 | /* to ensure compatibility when an HID++ 2.0 device is | |
171 | * connected to an HID++ 1.0 receiver, any feature index | |
172 | * corresponding to an HID++ 1.0 sub-identifier which could be | |
173 | * sent by the receiver, must be assigned to a dummy feature */ | |
174 | if (msg->hidpp_version >= 2.f) { | |
175 | if (fu_logitech_hidpp_msg_is_hidpp10_compat (msg_tmp)) { | |
176 | g_debug ("ignoring HID++1.0 reply"); | |
177 | continue; | |
178 | } | |
179 | ||
180 | /* not us */ | |
181 | if ((msg->flags & FU_UNIFYING_HIDPP_MSG_FLAG_IGNORE_SWID) == 0) { | |
182 | if (!fu_logitech_hidpp_msg_verify_swid (msg_tmp)) { | |
183 | g_debug ("ignoring reply with SwId 0x%02i, expected 0x%02i", | |
184 | msg_tmp->function_id & 0x0f, | |
185 | FU_UNIFYING_HIDPP_MSG_SW_ID); | |
186 | continue; | |
187 | } | |
188 | } | |
189 | } | |
190 | ||
191 | /* hardware not responding */ | |
192 | if (ignore_cnt++ > 10) { | |
193 | g_set_error (error, | |
194 | G_IO_ERROR, | |
195 | G_IO_ERROR_FAILED, | |
196 | "too many messages to ignore"); | |
197 | return FALSE; | |
198 | } | |
199 | ||
200 | g_debug ("ignoring message %u", ignore_cnt); | |
201 | }; | |
202 | ||
203 | /* copy over data */ | |
204 | fu_logitech_hidpp_msg_copy (msg, msg_tmp); | |
205 | return TRUE; | |
206 | } |
0 | /* | |
1 | * Copyright (C) 2016-2018 Richard Hughes <richard@hughsie.com> | |
2 | * | |
3 | * SPDX-License-Identifier: LGPL-2.1+ | |
4 | */ | |
5 | ||
6 | #pragma once | |
7 | ||
8 | #include <gio/gio.h> | |
9 | ||
10 | #include "fu-io-channel.h" | |
11 | ||
12 | /* | |
13 | * Based on the HID++ documentation provided by Nestor Lopez Casado at: | |
14 | * https://drive.google.com/folderview?id=0BxbRzx7vEV7eWmgwazJ3NUFfQ28&usp=sharing | |
15 | */ | |
16 | #define HIDPP_DEVICE_ID_WIRED 0x00 | |
17 | #define HIDPP_DEVICE_ID_RECEIVER 0xFF | |
18 | #define HIDPP_DEVICE_ID_UNSET 0xFE | |
19 | ||
20 | #define HIDPP_REPORT_NOTIFICATION 0x01 | |
21 | #define HIDPP_REPORT_ID_SHORT 0x10 | |
22 | #define HIDPP_REPORT_ID_LONG 0x11 | |
23 | #define HIDPP_REPORT_ID_VERY_LONG 0x12 | |
24 | ||
25 | #define HIDPP_SUBID_VENDOR_SPECIFIC_KEYS 0x03 | |
26 | #define HIDPP_SUBID_POWER_KEYS 0x04 | |
27 | #define HIDPP_SUBID_ROLLER 0x05 | |
28 | #define HIDPP_SUBID_MOUSE_EXTRA_BUTTONS 0x06 | |
29 | #define HIDPP_SUBID_BATTERY_CHARGING_LEVEL 0x07 | |
30 | #define HIDPP_SUBID_USER_INTERFACE_EVENT 0x08 | |
31 | #define HIDPP_SUBID_F_LOCK_STATUS 0x09 | |
32 | #define HIDPP_SUBID_CALCULATOR_RESULT 0x0A | |
33 | #define HIDPP_SUBID_MENU_NAVIGATE 0x0B | |
34 | #define HIDPP_SUBID_FN_KEY 0x0C | |
35 | #define HIDPP_SUBID_BATTERY_MILEAGE 0x0D | |
36 | #define HIDPP_SUBID_UART_RX 0x0E | |
37 | #define HIDPP_SUBID_BACKLIGHT_DURATION_UPDATE 0x17 | |
38 | #define HIDPP_SUBID_DEVICE_DISCONNECTION 0x40 | |
39 | #define HIDPP_SUBID_DEVICE_CONNECTION 0x41 | |
40 | #define HIDPP_SUBID_DEVICE_DISCOVERY 0x42 | |
41 | #define HIDPP_SUBID_PIN_CODE_REQUEST 0x43 | |
42 | #define HIDPP_SUBID_RECEIVER_WORKING_MODE 0x44 | |
43 | #define HIDPP_SUBID_ERROR_MESSAGE 0x45 | |
44 | #define HIDPP_SUBID_RF_LINK_CHANGE 0x46 | |
45 | #define HIDPP_SUBID_HCI 0x48 | |
46 | #define HIDPP_SUBID_LINK_QUALITY 0x49 | |
47 | #define HIDPP_SUBID_DEVICE_LOCKING_CHANGED 0x4a | |
48 | #define HIDPP_SUBID_WIRELESS_DEVICE_CHANGE 0x4B | |
49 | #define HIDPP_SUBID_ACL 0x51 | |
50 | #define HIDPP_SUBID_VOIP_TELEPHONY_EVENT 0x5B | |
51 | #define HIDPP_SUBID_LED 0x60 | |
52 | #define HIDPP_SUBID_GESTURE_AND_AIR 0x65 | |
53 | #define HIDPP_SUBID_TOUCHPAD_MULTI_TOUCH 0x66 | |
54 | #define HIDPP_SUBID_TRACEABILITY 0x78 | |
55 | #define HIDPP_SUBID_SET_REGISTER 0x80 | |
56 | #define HIDPP_SUBID_GET_REGISTER 0x81 | |
57 | #define HIDPP_SUBID_SET_LONG_REGISTER 0x82 | |
58 | #define HIDPP_SUBID_GET_LONG_REGISTER 0x83 | |
59 | #define HIDPP_SUBID_SET_VERY_LONG_REGISTER 0x84 | |
60 | #define HIDPP_SUBID_GET_VERY_LONG_REGISTER 0x85 | |
61 | #define HIDPP_SUBID_ERROR_MSG 0x8F | |
62 | #define HIDPP_SUBID_ERROR_MSG_20 0xFF | |
63 | ||
64 | #define HIDPP_ERR_SUCCESS 0x00 | |
65 | #define HIDPP_ERR_INVALID_SUBID 0x01 | |
66 | #define HIDPP_ERR_INVALID_ADDRESS 0x02 | |
67 | #define HIDPP_ERR_INVALID_VALUE 0x03 | |
68 | #define HIDPP_ERR_CONNECT_FAIL 0x04 | |
69 | #define HIDPP_ERR_TOO_MANY_DEVICES 0x05 | |
70 | #define HIDPP_ERR_ALREADY_EXISTS 0x06 | |
71 | #define HIDPP_ERR_BUSY 0x07 | |
72 | #define HIDPP_ERR_UNKNOWN_DEVICE 0x08 | |
73 | #define HIDPP_ERR_RESOURCE_ERROR 0x09 | |
74 | #define HIDPP_ERR_REQUEST_UNAVAILABLE 0x0A | |
75 | #define HIDPP_ERR_INVALID_PARAM_VALUE 0x0B | |
76 | #define HIDPP_ERR_WRONG_PIN_CODE 0x0C | |
77 | ||
78 | /* | |
79 | * HID++1.0 registers | |
80 | */ | |
81 | ||
82 | #define HIDPP_REGISTER_HIDPP_NOTIFICATIONS 0x00 | |
83 | #define HIDPP_REGISTER_ENABLE_INDIVIDUAL_FEATURES 0x01 | |
84 | #define HIDPP_REGISTER_BATTERY_STATUS 0x07 | |
85 | #define HIDPP_REGISTER_BATTERY_MILEAGE 0x0D | |
86 | #define HIDPP_REGISTER_PROFILE 0x0F | |
87 | #define HIDPP_REGISTER_LED_STATUS 0x51 | |
88 | #define HIDPP_REGISTER_LED_INTENSITY 0x54 | |
89 | #define HIDPP_REGISTER_LED_COLOR 0x57 | |
90 | #define HIDPP_REGISTER_OPTICAL_SENSOR_SETTINGS 0x61 | |
91 | #define HIDPP_REGISTER_CURRENT_RESOLUTION 0x63 | |
92 | #define HIDPP_REGISTER_USB_REFRESH_RATE 0x64 | |
93 | #define HIDPP_REGISTER_GENERIC_MEMORY_MANAGEMENT 0xA0 | |
94 | #define HIDPP_REGISTER_HOT_CONTROL 0xA1 | |
95 | #define HIDPP_REGISTER_READ_MEMORY 0xA2 | |
96 | #define HIDPP_REGISTER_DEVICE_CONNECTION_DISCONNECTION 0xB2 | |
97 | #define HIDPP_REGISTER_PAIRING_INFORMATION 0xB5 | |
98 | #define HIDPP_REGISTER_DEVICE_FIRMWARE_UPDATE_MODE 0xF0 | |
99 | #define HIDPP_REGISTER_DEVICE_FIRMWARE_INFORMATION 0xF1 | |
100 | ||
101 | /* | |
102 | * HID++2.0 error codes | |
103 | */ | |
104 | #define HIDPP_ERROR_CODE_NO_ERROR 0x00 | |
105 | #define HIDPP_ERROR_CODE_UNKNOWN 0x01 | |
106 | #define HIDPP_ERROR_CODE_INVALID_ARGUMENT 0x02 | |
107 | #define HIDPP_ERROR_CODE_OUT_OF_RANGE 0x03 | |
108 | #define HIDPP_ERROR_CODE_HW_ERROR 0x04 | |
109 | #define HIDPP_ERROR_CODE_LOGITECH_INTERNAL 0x05 | |
110 | #define HIDPP_ERROR_CODE_INVALID_FEATURE_INDEX 0x06 | |
111 | #define HIDPP_ERROR_CODE_INVALID_FUNCTION_ID 0x07 | |
112 | #define HIDPP_ERROR_CODE_BUSY 0x08 | |
113 | #define HIDPP_ERROR_CODE_UNSUPPORTED 0x09 | |
114 | ||
115 | /* | |
116 | * HID++2.0 features | |
117 | */ | |
118 | #define HIDPP_FEATURE_ROOT 0x0000 | |
119 | #define HIDPP_FEATURE_I_FEATURE_SET 0x0001 | |
120 | #define HIDPP_FEATURE_I_FIRMWARE_INFO 0x0003 | |
121 | #define HIDPP_FEATURE_GET_DEVICE_NAME_TYPE 0x0005 | |
122 | #define HIDPP_FEATURE_DFU_CONTROL 0x00c1 | |
123 | #define HIDPP_FEATURE_DFU_CONTROL_SIGNED 0x00c2 | |
124 | #define HIDPP_FEATURE_DFU 0x00d0 | |
125 | #define HIDPP_FEATURE_BATTERY_LEVEL_STATUS 0x1000 | |
126 | #define HIDPP_FEATURE_KBD_REPROGRAMMABLE_KEYS 0x1b00 | |
127 | #define HIDPP_FEATURE_SPECIAL_KEYS_BUTTONS 0x1b04 | |
128 | #define HIDPP_FEATURE_MOUSE_POINTER_BASIC 0x2200 | |
129 | #define HIDPP_FEATURE_ADJUSTABLE_DPI 0x2201 | |
130 | #define HIDPP_FEATURE_ADJUSTABLE_REPORT_RATE 0x8060 | |
131 | #define HIDPP_FEATURE_COLOR_LED_EFFECTS 0x8070 | |
132 | #define HIDPP_FEATURE_ONBOARD_PROFILES 0x8100 | |
133 | #define HIDPP_FEATURE_MOUSE_BUTTON_SPY 0x8110 | |
134 | ||
135 | #include "fu-logitech-hidpp-hidpp-msg.h" | |
136 | ||
137 | gboolean fu_logitech_hidpp_send (FuIOChannel *self, | |
138 | FuLogitechHidPpHidppMsg *msg, | |
139 | guint timeout, | |
140 | GError **error); | |
141 | gboolean fu_logitech_hidpp_receive (FuIOChannel *self, | |
142 | FuLogitechHidPpHidppMsg *msg, | |
143 | guint timeout, | |
144 | GError **error); | |
145 | gboolean fu_logitech_hidpp_transfer (FuIOChannel *self, | |
146 | FuLogitechHidPpHidppMsg *msg, | |
147 | GError **error); |
0 | /* | |
1 | * Copyright (C) 2017-2018 Richard Hughes <richard@hughsie.com> | |
2 | * | |
3 | * SPDX-License-Identifier: LGPL-2.1+ | |
4 | */ | |
5 | ||
6 | #include "config.h" | |
7 | ||
8 | #include <string.h> | |
9 | ||
10 | #include "fu-logitech-hidpp-common.h" | |
11 | #include "fu-logitech-hidpp-peripheral.h" | |
12 | #include "fu-logitech-hidpp-hidpp.h" | |
13 | ||
14 | struct _FuLogitechHidPpPeripheral | |
15 | { | |
16 | FuUdevDevice parent_instance; | |
17 | guint8 battery_level; | |
18 | guint8 cached_fw_entity; | |
19 | guint8 hidpp_id; | |
20 | guint8 hidpp_version; | |
21 | gboolean is_updatable; | |
22 | gboolean is_active; | |
23 | FuIOChannel *io_channel; | |
24 | GPtrArray *feature_index; /* of FuLogitechHidPpHidppMap */ | |
25 | }; | |
26 | ||
27 | typedef struct { | |
28 | guint8 idx; | |
29 | guint16 feature; | |
30 | } FuLogitechHidPpHidppMap; | |
31 | ||
32 | G_DEFINE_TYPE (FuLogitechHidPpPeripheral, fu_logitech_hidpp_peripheral, FU_TYPE_UDEV_DEVICE) | |
33 | ||
34 | typedef enum { | |
35 | FU_UNIFYING_PERIPHERAL_KIND_KEYBOARD, | |
36 | FU_UNIFYING_PERIPHERAL_KIND_REMOTE_CONTROL, | |
37 | FU_UNIFYING_PERIPHERAL_KIND_NUMPAD, | |
38 | FU_UNIFYING_PERIPHERAL_KIND_MOUSE, | |
39 | FU_UNIFYING_PERIPHERAL_KIND_TOUCHPAD, | |
40 | FU_UNIFYING_PERIPHERAL_KIND_TRACKBALL, | |
41 | FU_UNIFYING_PERIPHERAL_KIND_PRESENTER, | |
42 | FU_UNIFYING_PERIPHERAL_KIND_RECEIVER, | |
43 | FU_UNIFYING_PERIPHERAL_KIND_LAST | |
44 | } FuLogitechHidPpPeripheralKind; | |
45 | ||
46 | static const gchar * | |
47 | fu_logitech_hidpp_peripheral_get_icon (FuLogitechHidPpPeripheralKind kind) | |
48 | { | |
49 | if (kind == FU_UNIFYING_PERIPHERAL_KIND_KEYBOARD) | |
50 | return "input-keyboard"; | |
51 | if (kind == FU_UNIFYING_PERIPHERAL_KIND_REMOTE_CONTROL) | |
52 | return "pda"; // ish | |
53 | if (kind == FU_UNIFYING_PERIPHERAL_KIND_NUMPAD) | |
54 | return "input-dialpad"; | |
55 | if (kind == FU_UNIFYING_PERIPHERAL_KIND_MOUSE) | |
56 | return "input-mouse"; | |
57 | if (kind == FU_UNIFYING_PERIPHERAL_KIND_TOUCHPAD) | |
58 | return "input-touchpad"; | |
59 | if (kind == FU_UNIFYING_PERIPHERAL_KIND_TRACKBALL) | |
60 | return "input-mouse"; // ish | |
61 | if (kind == FU_UNIFYING_PERIPHERAL_KIND_PRESENTER) | |
62 | return "pda"; // ish | |
63 | if (kind == FU_UNIFYING_PERIPHERAL_KIND_RECEIVER) | |
64 | return "preferences-desktop-keyboard"; | |
65 | return NULL; | |
66 | } | |
67 | ||
68 | static const gchar * | |
69 | fu_logitech_hidpp_peripheral_get_summary (FuLogitechHidPpPeripheralKind kind) | |
70 | { | |
71 | if (kind == FU_UNIFYING_PERIPHERAL_KIND_KEYBOARD) | |
72 | return "Unifying Keyboard"; | |
73 | if (kind == FU_UNIFYING_PERIPHERAL_KIND_REMOTE_CONTROL) | |
74 | return "Unifying Remote Control"; | |
75 | if (kind == FU_UNIFYING_PERIPHERAL_KIND_NUMPAD) | |
76 | return "Unifying Number Pad"; | |
77 | if (kind == FU_UNIFYING_PERIPHERAL_KIND_MOUSE) | |
78 | return "Unifying Mouse"; | |
79 | if (kind == FU_UNIFYING_PERIPHERAL_KIND_TOUCHPAD) | |
80 | return "Unifying Touchpad"; | |
81 | if (kind == FU_UNIFYING_PERIPHERAL_KIND_TRACKBALL) | |
82 | return "Unifying Trackball"; | |
83 | if (kind == FU_UNIFYING_PERIPHERAL_KIND_PRESENTER) | |
84 | return "Unifying Presenter"; | |
85 | if (kind == FU_UNIFYING_PERIPHERAL_KIND_RECEIVER) | |
86 | return "Unifying Receiver"; | |
87 | return NULL; | |
88 | } | |
89 | ||
90 | static const gchar * | |
91 | fu_logitech_hidpp_feature_to_string (guint16 feature) | |
92 | { | |
93 | if (feature == HIDPP_FEATURE_ROOT) | |
94 | return "Root"; | |
95 | if (feature == HIDPP_FEATURE_I_FIRMWARE_INFO) | |
96 | return "IFirmwareInfo"; | |
97 | if (feature == HIDPP_FEATURE_GET_DEVICE_NAME_TYPE) | |
98 | return "GetDevicenameType"; | |
99 | if (feature == HIDPP_FEATURE_BATTERY_LEVEL_STATUS) | |
100 | return "BatteryLevelStatus"; | |
101 | if (feature == HIDPP_FEATURE_DFU_CONTROL) | |
102 | return "DfuControl"; | |
103 | if (feature == HIDPP_FEATURE_DFU_CONTROL_SIGNED) | |
104 | return "DfuControlSigned"; | |
105 | if (feature == HIDPP_FEATURE_DFU) | |
106 | return "Dfu"; | |
107 | return NULL; | |
108 | } | |
109 | ||
110 | static void | |
111 | fu_logitech_hidpp_peripheral_refresh_updatable (FuLogitechHidPpPeripheral *self) | |
112 | { | |
113 | /* device can only be upgraded if it is capable, and active */ | |
114 | if (self->is_updatable && self->is_active) { | |
115 | fu_device_add_flag (FU_DEVICE (self), FWUPD_DEVICE_FLAG_UPDATABLE); | |
116 | return; | |
117 | } | |
118 | fu_device_remove_flag (FU_DEVICE (self), FWUPD_DEVICE_FLAG_UPDATABLE); | |
119 | } | |
120 | ||
121 | static gboolean | |
122 | fu_logitech_hidpp_peripheral_ping (FuLogitechHidPpPeripheral *self, GError **error) | |
123 | { | |
124 | gdouble version; | |
125 | g_autoptr(GError) error_local = NULL; | |
126 | g_autoptr(FuLogitechHidPpHidppMsg) msg = fu_logitech_hidpp_msg_new (); | |
127 | ||
128 | /* handle failure */ | |
129 | msg->report_id = HIDPP_REPORT_ID_SHORT; | |
130 | msg->device_id = self->hidpp_id; | |
131 | msg->sub_id = 0x00; /* rootIndex */ | |
132 | msg->function_id = 0x01 << 4; /* ping */ | |
133 | msg->data[0] = 0x00; | |
134 | msg->data[1] = 0x00; | |
135 | msg->data[2] = 0xaa; /* user-selected value */ | |
136 | msg->hidpp_version = self->hidpp_version; | |
137 | if (!fu_logitech_hidpp_transfer (self->io_channel, msg, &error_local)) { | |
138 | if (g_error_matches (error_local, | |
139 | G_IO_ERROR, | |
140 | G_IO_ERROR_NOT_SUPPORTED)) { | |
141 | self->hidpp_version = 1; | |
142 | return TRUE; | |
143 | } | |
144 | if (g_error_matches (error_local, | |
145 | G_IO_ERROR, | |
146 | G_IO_ERROR_HOST_UNREACHABLE)) { | |
147 | self->is_active = FALSE; | |
148 | fu_logitech_hidpp_peripheral_refresh_updatable (self); | |
149 | return TRUE; | |
150 | } | |
151 | g_set_error (error, | |
152 | G_IO_ERROR, | |
153 | G_IO_ERROR_FAILED, | |
154 | "failed to ping %s: %s", | |
155 | fu_device_get_name (FU_DEVICE (self)), | |
156 | error_local->message); | |
157 | return FALSE; | |
158 | } | |
159 | ||
160 | /* device no longer asleep */ | |
161 | self->is_active = TRUE; | |
162 | fu_logitech_hidpp_peripheral_refresh_updatable (self); | |
163 | ||
164 | /* if the HID++ ID is unset, grab it from the reply */ | |
165 | if (self->hidpp_id == HIDPP_DEVICE_ID_UNSET) { | |
166 | self->hidpp_id = msg->device_id; | |
167 | g_debug ("HID++ ID is %02x", self->hidpp_id); | |
168 | } | |
169 | ||
170 | /* format version in BCD format */ | |
171 | version = (gdouble) msg->data[0] + ((gdouble) msg->data[1]) / 100.f; | |
172 | self->hidpp_version = (guint) version; | |
173 | ||
174 | /* success */ | |
175 | return TRUE; | |
176 | } | |
177 | ||
178 | static gboolean | |
179 | fu_logitech_hidpp_peripheral_close (FuDevice *device, GError **error) | |
180 | { | |
181 | FuLogitechHidPpPeripheral *self = FU_UNIFYING_PERIPHERAL (device); | |
182 | if (!fu_io_channel_shutdown (self->io_channel, error)) | |
183 | return FALSE; | |
184 | g_clear_object (&self->io_channel); | |
185 | return TRUE; | |
186 | } | |
187 | ||
188 | static gboolean | |
189 | fu_logitech_hidpp_peripheral_poll (FuDevice *device, GError **error) | |
190 | { | |
191 | FuLogitechHidPpPeripheral *self = FU_UNIFYING_PERIPHERAL (device); | |
192 | const guint timeout = 1; /* ms */ | |
193 | g_autoptr(GError) error_local = NULL; | |
194 | g_autoptr(FuLogitechHidPpHidppMsg) msg = fu_logitech_hidpp_msg_new (); | |
195 | g_autoptr(FuDeviceLocker) locker = NULL; | |
196 | ||
197 | /* open */ | |
198 | locker = fu_device_locker_new (self, error); | |
199 | if (locker == NULL) | |
200 | return FALSE; | |
201 | ||
202 | /* flush pending data */ | |
203 | msg->device_id = self->hidpp_id; | |
204 | msg->hidpp_version = self->hidpp_version; | |
205 | if (!fu_logitech_hidpp_receive (self->io_channel, msg, timeout, &error_local)) { | |
206 | if (!g_error_matches (error_local, | |
207 | G_IO_ERROR, | |
208 | G_IO_ERROR_TIMED_OUT)) { | |
209 | g_warning ("failed to get pending read: %s", error_local->message); | |
210 | return TRUE; | |
211 | } | |
212 | /* no data to receive */ | |
213 | g_clear_error (&error_local); | |
214 | } | |
215 | ||
216 | /* just ping */ | |
217 | if (!fu_logitech_hidpp_peripheral_ping (self, &error_local)) { | |
218 | g_warning ("failed to ping device: %s", error_local->message); | |
219 | return TRUE; | |
220 | } | |
221 | ||
222 | /* this is the first time the device has been active */ | |
223 | if (self->feature_index->len == 0) { | |
224 | fu_device_probe_invalidate (FU_DEVICE (self)); | |
225 | if (!fu_device_setup (FU_DEVICE (self), error)) | |
226 | return FALSE; | |
227 | } | |
228 | ||
229 | /* success */ | |
230 | return TRUE; | |
231 | } | |
232 | ||
233 | static gboolean | |
234 | fu_logitech_hidpp_peripheral_open (FuDevice *device, GError **error) | |
235 | { | |
236 | FuLogitechHidPpPeripheral *self = FU_UNIFYING_PERIPHERAL (device); | |
237 | GUdevDevice *udev_device = fu_udev_device_get_dev (FU_UDEV_DEVICE (device)); | |
238 | const gchar *devpath = g_udev_device_get_device_file (udev_device); | |
239 | ||
240 | /* open */ | |
241 | self->io_channel = fu_io_channel_new_file (devpath, error); | |
242 | if (self->io_channel == NULL) | |
243 | return FALSE; | |
244 | ||
245 | return TRUE; | |
246 | } | |
247 | ||
248 | static void | |
249 | fu_logitech_hidpp_map_to_string (FuLogitechHidPpHidppMap *map, guint idt, GString *str) | |
250 | { | |
251 | g_autofree gchar *title = g_strdup_printf ("Feature%02x", map->idx); | |
252 | g_autofree gchar *tmp = g_strdup_printf ("%s [0x%04x]", | |
253 | fu_logitech_hidpp_feature_to_string (map->feature), | |
254 | map->feature); | |
255 | fu_common_string_append_kv (str, idt, title, tmp); | |
256 | } | |
257 | ||
258 | static void | |
259 | fu_logitech_hidpp_peripheral_to_string (FuDevice *device, guint idt, GString *str) | |
260 | { | |
261 | FuLogitechHidPpPeripheral *self = FU_UNIFYING_PERIPHERAL (device); | |
262 | fu_common_string_append_ku (str, idt, "HidppVersion", self->hidpp_version); | |
263 | fu_common_string_append_kx (str, idt, "HidppId", self->hidpp_id); | |
264 | fu_common_string_append_ku (str, idt, "BatteryLevel", self->battery_level); | |
265 | fu_common_string_append_kb (str, idt, "IsUpdatable", self->is_updatable); | |
266 | fu_common_string_append_kb (str, idt, "IsActive", self->is_active); | |
267 | for (guint i = 0; i < self->feature_index->len; i++) { | |
268 | FuLogitechHidPpHidppMap *map = g_ptr_array_index (self->feature_index, i); | |
269 | fu_logitech_hidpp_map_to_string (map, idt, str); | |
270 | } | |
271 | } | |
272 | ||
273 | static guint8 | |
274 | fu_logitech_hidpp_peripheral_feature_get_idx (FuLogitechHidPpPeripheral *self, guint16 feature) | |
275 | { | |
276 | for (guint i = 0; i < self->feature_index->len; i++) { | |
277 | FuLogitechHidPpHidppMap *map = g_ptr_array_index (self->feature_index, i); | |
278 | if (map->feature == feature) | |
279 | return map->idx; | |
280 | } | |
281 | return 0x00; | |
282 | } | |
283 | ||
284 | static gboolean | |
285 | fu_logitech_hidpp_peripheral_fetch_firmware_info (FuLogitechHidPpPeripheral *self, GError **error) | |
286 | { | |
287 | guint8 idx; | |
288 | guint8 entity_count; | |
289 | g_autoptr(FuLogitechHidPpHidppMsg) msg = fu_logitech_hidpp_msg_new (); | |
290 | ||
291 | /* get the feature index */ | |
292 | idx = fu_logitech_hidpp_peripheral_feature_get_idx (self, HIDPP_FEATURE_I_FIRMWARE_INFO); | |
293 | if (idx == 0x00) | |
294 | return TRUE; | |
295 | ||
296 | /* get the entity count */ | |
297 | msg->report_id = HIDPP_REPORT_ID_SHORT; | |
298 | msg->device_id = self->hidpp_id; | |
299 | msg->sub_id = idx; | |
300 | msg->function_id = 0x00 << 4; /* getCount */ | |
301 | msg->hidpp_version = self->hidpp_version; | |
302 | if (!fu_logitech_hidpp_transfer (self->io_channel, msg, error)) { | |
303 | g_prefix_error (error, "failed to get firmware count: "); | |
304 | return FALSE; | |
305 | } | |
306 | entity_count = msg->data[0]; | |
307 | g_debug ("firmware entity count is %u", entity_count); | |
308 | ||
309 | /* get firmware, bootloader, hardware versions */ | |
310 | for (guint8 i = 0; i < entity_count; i++) { | |
311 | guint16 build; | |
312 | g_autofree gchar *version = NULL; | |
313 | g_autofree gchar *name = NULL; | |
314 | ||
315 | msg->report_id = HIDPP_REPORT_ID_SHORT; | |
316 | msg->device_id = self->hidpp_id; | |
317 | msg->sub_id = idx; | |
318 | msg->function_id = 0x01 << 4; /* getInfo */ | |
319 | msg->data[0] = i; | |
320 | if (!fu_logitech_hidpp_transfer (self->io_channel, msg, error)) { | |
321 | g_prefix_error (error, "failed to get firmware info: "); | |
322 | return FALSE; | |
323 | } | |
324 | if (msg->data[1] == 0x00 && | |
325 | msg->data[2] == 0x00 && | |
326 | msg->data[3] == 0x00 && | |
327 | msg->data[4] == 0x00 && | |
328 | msg->data[5] == 0x00 && | |
329 | msg->data[6] == 0x00 && | |
330 | msg->data[7] == 0x00) { | |
331 | g_debug ("no version set for entity %u", i); | |
332 | continue; | |
333 | } | |
334 | name = g_strdup_printf ("%c%c%c", | |
335 | msg->data[1], | |
336 | msg->data[2], | |
337 | msg->data[3]); | |
338 | build = ((guint16) msg->data[6]) << 8 | msg->data[7]; | |
339 | version = fu_logitech_hidpp_format_version (name, | |
340 | msg->data[4], | |
341 | msg->data[5], | |
342 | build); | |
343 | g_debug ("firmware entity 0x%02x version is %s", i, version); | |
344 | if (msg->data[0] == 0) { | |
345 | fu_device_set_version (FU_DEVICE (self), version, | |
346 | FWUPD_VERSION_FORMAT_PLAIN); | |
347 | self->cached_fw_entity = i; | |
348 | } else if (msg->data[0] == 1) { | |
349 | fu_device_set_version_bootloader (FU_DEVICE (self), version); | |
350 | } else if (msg->data[0] == 2) { | |
351 | fu_device_set_metadata (FU_DEVICE (self), "version-hw", version); | |
352 | } | |
353 | } | |
354 | ||
355 | /* not an error, the device just doesn't support this */ | |
356 | return TRUE; | |
357 | } | |
358 | ||
359 | static gboolean | |
360 | fu_logitech_hidpp_peripheral_fetch_battery_level (FuLogitechHidPpPeripheral *self, GError **error) | |
361 | { | |
362 | /* try using HID++2.0 */ | |
363 | if (self->hidpp_version >= 2.f) { | |
364 | guint8 idx; | |
365 | idx = fu_logitech_hidpp_peripheral_feature_get_idx (self, HIDPP_FEATURE_BATTERY_LEVEL_STATUS); | |
366 | if (idx != 0x00) { | |
367 | g_autoptr(FuLogitechHidPpHidppMsg) msg = fu_logitech_hidpp_msg_new (); | |
368 | msg->report_id = HIDPP_REPORT_ID_SHORT; | |
369 | msg->device_id = self->hidpp_id; | |
370 | msg->sub_id = idx; | |
371 | msg->function_id = 0x00; /* GetBatteryLevelStatus */ | |
372 | msg->hidpp_version = self->hidpp_version; | |
373 | if (!fu_logitech_hidpp_transfer (self->io_channel, msg, error)) { | |
374 | g_prefix_error (error, "failed to get battery info: "); | |
375 | return FALSE; | |
376 | } | |
377 | if (msg->data[0] != 0x00) | |
378 | self->battery_level = msg->data[0]; | |
379 | return TRUE; | |
380 | } | |
381 | } | |
382 | ||
383 | /* try HID++1.0 battery mileage */ | |
384 | if (self->hidpp_version == 1.f) { | |
385 | g_autoptr(FuLogitechHidPpHidppMsg) msg = fu_logitech_hidpp_msg_new (); | |
386 | msg->report_id = HIDPP_REPORT_ID_SHORT; | |
387 | msg->device_id = self->hidpp_id; | |
388 | msg->sub_id = HIDPP_SUBID_GET_REGISTER; | |
389 | msg->function_id = HIDPP_REGISTER_BATTERY_MILEAGE; | |
390 | msg->hidpp_version = self->hidpp_version; | |
391 | if (fu_logitech_hidpp_transfer (self->io_channel, msg, NULL)) { | |
392 | if (msg->data[0] != 0x00) | |
393 | self->battery_level = msg->data[0]; | |
394 | return TRUE; | |
395 | } | |
396 | ||
397 | /* try HID++1.0 battery status instead */ | |
398 | msg->function_id = HIDPP_REGISTER_BATTERY_STATUS; | |
399 | if (fu_logitech_hidpp_transfer (self->io_channel, msg, NULL)) { | |
400 | switch (msg->data[0]) { | |
401 | case 1: /* 0 - 10 */ | |
402 | self->battery_level = 5; | |
403 | break; | |
404 | case 3: /* 11 - 30 */ | |
405 | self->battery_level = 20; | |
406 | break; | |
407 | case 5: /* 31 - 80 */ | |
408 | self->battery_level = 55; | |
409 | break; | |
410 | case 7: /* 81 - 100 */ | |
411 | self->battery_level = 90; | |
412 | break; | |
413 | default: | |
414 | g_warning ("unknown battery percentage: 0x%02x", | |
415 | msg->data[0]); | |
416 | break; | |
417 | } | |
418 | return TRUE; | |
419 | } | |
420 | } | |
421 | ||
422 | /* not an error, the device just doesn't support any of the methods */ | |
423 | return TRUE; | |
424 | } | |
425 | ||
426 | static gboolean | |
427 | fu_logitech_hidpp_feature_search (FuDevice *device, guint16 feature, GError **error) | |
428 | { | |
429 | FuLogitechHidPpPeripheral *self = FU_UNIFYING_PERIPHERAL (device); | |
430 | FuLogitechHidPpHidppMap *map; | |
431 | g_autoptr(FuLogitechHidPpHidppMsg) msg = fu_logitech_hidpp_msg_new (); | |
432 | ||
433 | /* find the idx for the feature */ | |
434 | msg->report_id = HIDPP_REPORT_ID_SHORT; | |
435 | msg->device_id = self->hidpp_id; | |
436 | msg->sub_id = 0x00; /* rootIndex */ | |
437 | msg->function_id = 0x00 << 4; /* getFeature */ | |
438 | msg->data[0] = feature >> 8; | |
439 | msg->data[1] = feature; | |
440 | msg->data[2] = 0x00; | |
441 | msg->hidpp_version = self->hidpp_version; | |
442 | if (!fu_logitech_hidpp_transfer (self->io_channel, msg, error)) { | |
443 | g_prefix_error (error, | |
444 | "failed to get idx for feature %s [0x%04x]: ", | |
445 | fu_logitech_hidpp_feature_to_string (feature), feature); | |
446 | return FALSE; | |
447 | } | |
448 | ||
449 | /* zero index */ | |
450 | if (msg->data[0] == 0x00) { | |
451 | g_set_error (error, | |
452 | G_IO_ERROR, | |
453 | G_IO_ERROR_NOT_SUPPORTED, | |
454 | "feature %s [0x%04x] not found", | |
455 | fu_logitech_hidpp_feature_to_string (feature), feature); | |
456 | return FALSE; | |
457 | } | |
458 | ||
459 | /* add to map */ | |
460 | map = g_new0 (FuLogitechHidPpHidppMap, 1); | |
461 | map->idx = msg->data[0]; | |
462 | map->feature = feature; | |
463 | g_ptr_array_add (self->feature_index, map); | |
464 | g_debug ("added feature %s [0x%04x] as idx %02x", | |
465 | fu_logitech_hidpp_feature_to_string (feature), feature, map->idx); | |
466 | return TRUE; | |
467 | } | |
468 | ||
469 | static gboolean | |
470 | fu_logitech_hidpp_peripheral_probe (FuUdevDevice *device, GError **error) | |
471 | { | |
472 | g_autofree gchar *devid = NULL; | |
473 | ||
474 | /* set the physical ID */ | |
475 | if (!fu_udev_device_set_physical_id (device, "hid", error)) | |
476 | return FALSE; | |
477 | ||
478 | /* nearly... */ | |
479 | fu_device_set_vendor_id (FU_DEVICE (device), "USB:0x046D"); | |
480 | ||
481 | /* this is a non-standard extension */ | |
482 | devid = g_strdup_printf ("UFY\\VID_%04X&PID_%04X", | |
483 | fu_udev_device_get_vendor (device), | |
484 | fu_udev_device_get_model (device)); | |
485 | fu_device_add_instance_id (FU_DEVICE (device), devid); | |
486 | return TRUE; | |
487 | } | |
488 | ||
489 | static gboolean | |
490 | fu_logitech_hidpp_peripheral_setup (FuDevice *device, GError **error) | |
491 | { | |
492 | FuLogitechHidPpPeripheral *self = FU_UNIFYING_PERIPHERAL (device); | |
493 | guint8 idx; | |
494 | const guint16 map_features[] = { | |
495 | HIDPP_FEATURE_GET_DEVICE_NAME_TYPE, | |
496 | HIDPP_FEATURE_I_FIRMWARE_INFO, | |
497 | HIDPP_FEATURE_BATTERY_LEVEL_STATUS, | |
498 | HIDPP_FEATURE_DFU_CONTROL, | |
499 | HIDPP_FEATURE_DFU_CONTROL_SIGNED, | |
500 | HIDPP_FEATURE_DFU, | |
501 | HIDPP_FEATURE_ROOT }; | |
502 | ||
503 | /* ping device to get HID++ version */ | |
504 | if (!fu_logitech_hidpp_peripheral_ping (self, error)) | |
505 | return FALSE; | |
506 | ||
507 | /* add known root for HID++2.0 */ | |
508 | g_ptr_array_set_size (self->feature_index, 0); | |
509 | if (self->hidpp_version >= 2.f) { | |
510 | FuLogitechHidPpHidppMap *map = g_new0 (FuLogitechHidPpHidppMap, 1); | |
511 | map->idx = 0x00; | |
512 | map->feature = HIDPP_FEATURE_ROOT; | |
513 | g_ptr_array_add (self->feature_index, map); | |
514 | } | |
515 | ||
516 | /* map some *optional* HID++2.0 features we might use */ | |
517 | for (guint i = 0; map_features[i] != HIDPP_FEATURE_ROOT; i++) { | |
518 | g_autoptr(GError) error_local = NULL; | |
519 | if (!fu_logitech_hidpp_feature_search (device, | |
520 | map_features[i], | |
521 | &error_local)) { | |
522 | g_debug ("%s", error_local->message); | |
523 | if (g_error_matches (error_local, | |
524 | G_IO_ERROR, | |
525 | G_IO_ERROR_TIMED_OUT) || | |
526 | g_error_matches (error_local, | |
527 | G_IO_ERROR, | |
528 | G_IO_ERROR_HOST_UNREACHABLE)) { | |
529 | /* timed out, so not trying any more */ | |
530 | break; | |
531 | } | |
532 | } | |
533 | } | |
534 | ||
535 | /* get the firmware information */ | |
536 | if (!fu_logitech_hidpp_peripheral_fetch_firmware_info (self, error)) | |
537 | return FALSE; | |
538 | ||
539 | /* get the battery level */ | |
540 | if (!fu_logitech_hidpp_peripheral_fetch_battery_level (self, error)) | |
541 | return FALSE; | |
542 | ||
543 | /* try using HID++2.0 */ | |
544 | idx = fu_logitech_hidpp_peripheral_feature_get_idx (self, HIDPP_FEATURE_GET_DEVICE_NAME_TYPE); | |
545 | if (idx != 0x00) { | |
546 | const gchar *tmp; | |
547 | g_autoptr(FuLogitechHidPpHidppMsg) msg = fu_logitech_hidpp_msg_new (); | |
548 | msg->report_id = HIDPP_REPORT_ID_SHORT; | |
549 | msg->device_id = self->hidpp_id; | |
550 | msg->sub_id = idx; | |
551 | msg->function_id = 0x02 << 4; /* getDeviceType */ | |
552 | msg->hidpp_version = self->hidpp_version; | |
553 | if (!fu_logitech_hidpp_transfer (self->io_channel, msg, error)) { | |
554 | g_prefix_error (error, "failed to get device type: "); | |
555 | return FALSE; | |
556 | } | |
557 | ||
558 | /* add nice-to-have data */ | |
559 | tmp = fu_logitech_hidpp_peripheral_get_summary (msg->data[0]); | |
560 | if (tmp != NULL) | |
561 | fu_device_set_summary (FU_DEVICE (device), tmp); | |
562 | tmp = fu_logitech_hidpp_peripheral_get_icon (msg->data[0]); | |
563 | if (tmp != NULL) | |
564 | fu_device_add_icon (FU_DEVICE (device), tmp); | |
565 | } | |
566 | idx = fu_logitech_hidpp_peripheral_feature_get_idx (self, HIDPP_FEATURE_DFU_CONTROL); | |
567 | if (idx != 0x00) { | |
568 | self->is_updatable = TRUE; | |
569 | fu_device_remove_flag (FU_DEVICE (device), FWUPD_DEVICE_FLAG_IS_BOOTLOADER); | |
570 | } | |
571 | idx = fu_logitech_hidpp_peripheral_feature_get_idx (self, HIDPP_FEATURE_DFU_CONTROL_SIGNED); | |
572 | if (idx != 0x00) { | |
573 | /* check the feature is available */ | |
574 | g_autoptr(FuLogitechHidPpHidppMsg) msg = fu_logitech_hidpp_msg_new (); | |
575 | msg->report_id = HIDPP_REPORT_ID_SHORT; | |
576 | msg->device_id = self->hidpp_id; | |
577 | msg->sub_id = idx; | |
578 | msg->function_id = 0x00 << 4; /* getDfuStatus */ | |
579 | msg->hidpp_version = self->hidpp_version; | |
580 | if (!fu_logitech_hidpp_transfer (self->io_channel, msg, error)) { | |
581 | g_prefix_error (error, "failed to get DFU status: "); | |
582 | return FALSE; | |
583 | } | |
584 | if ((msg->data[2] & 0x01) > 0) { | |
585 | g_warning ("DFU mode not available"); | |
586 | } else { | |
587 | self->is_updatable = TRUE; | |
588 | fu_device_remove_flag (FU_DEVICE (device), FWUPD_DEVICE_FLAG_IS_BOOTLOADER); | |
589 | } | |
590 | } | |
591 | idx = fu_logitech_hidpp_peripheral_feature_get_idx (self, HIDPP_FEATURE_DFU); | |
592 | if (idx != 0x00) { | |
593 | self->is_updatable = TRUE; | |
594 | fu_device_add_flag (FU_DEVICE (device), FWUPD_DEVICE_FLAG_IS_BOOTLOADER); | |
595 | if (fu_device_get_version (device) == NULL) { | |
596 | g_debug ("repairing device in bootloader mode"); | |
597 | fu_device_set_version (FU_DEVICE (device), | |
598 | "MPK00.00_B0000", | |
599 | FWUPD_VERSION_FORMAT_PLAIN); | |
600 | } | |
601 | } | |
602 | ||
603 | /* this device may have changed state */ | |
604 | fu_logitech_hidpp_peripheral_refresh_updatable (self); | |
605 | ||
606 | /* poll for pings to track active state */ | |
607 | fu_device_set_poll_interval (device, 30000); | |
608 | return TRUE; | |
609 | } | |
610 | ||
611 | static gboolean | |
612 | fu_logitech_hidpp_peripheral_detach (FuDevice *device, GError **error) | |
613 | { | |
614 | FuLogitechHidPpPeripheral *self = FU_UNIFYING_PERIPHERAL (device); | |
615 | guint8 idx; | |
616 | g_autoptr(FuLogitechHidPpHidppMsg) msg = fu_logitech_hidpp_msg_new (); | |
617 | ||
618 | /* this requires user action */ | |
619 | idx = fu_logitech_hidpp_peripheral_feature_get_idx (self, HIDPP_FEATURE_DFU_CONTROL); | |
620 | if (idx != 0x00) { | |
621 | msg->report_id = HIDPP_REPORT_ID_LONG; | |
622 | msg->device_id = self->hidpp_id; | |
623 | msg->sub_id = idx; | |
624 | msg->function_id = 0x01 << 4; /* setDfuControl */ | |
625 | msg->data[0] = 0x01; /* enterDfu */ | |
626 | msg->data[1] = 0x00; /* dfuControlParam */ | |
627 | msg->data[2] = 0x00; /* unused */ | |
628 | msg->data[3] = 0x00; /* unused */ | |
629 | msg->data[4] = 'D'; | |
630 | msg->data[5] = 'F'; | |
631 | msg->data[6] = 'U'; | |
632 | msg->hidpp_version = self->hidpp_version; | |
633 | msg->flags = FU_UNIFYING_HIDPP_MSG_FLAG_IGNORE_SUB_ID | | |
634 | FU_UNIFYING_HIDPP_MSG_FLAG_LONGER_TIMEOUT; | |
635 | if (!fu_logitech_hidpp_transfer (self->io_channel, msg, error)) { | |
636 | g_prefix_error (error, "failed to put device into DFU mode: "); | |
637 | return FALSE; | |
638 | } | |
639 | fu_device_add_flag (device, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG); | |
640 | g_set_error (error, | |
641 | FWUPD_ERROR, | |
642 | FWUPD_ERROR_NEEDS_USER_ACTION, | |
643 | "%s needs to be manually restarted to complete the update." | |
644 | "Please unplug and reconnect the device and re-run the update", | |
645 | fu_device_get_name (device)); | |
646 | return FALSE; | |
647 | } | |
648 | ||
649 | /* this can reboot all by itself */ | |
650 | idx = fu_logitech_hidpp_peripheral_feature_get_idx (self, HIDPP_FEATURE_DFU_CONTROL_SIGNED); | |
651 | if (idx != 0x00) { | |
652 | msg->report_id = HIDPP_REPORT_ID_LONG; | |
653 | msg->device_id = self->hidpp_id; | |
654 | msg->sub_id = idx; | |
655 | msg->function_id = 0x01 << 4; /* setDfuControl */ | |
656 | msg->data[0] = 0x01; /* startDfu */ | |
657 | msg->data[1] = 0x00; /* dfuControlParam */ | |
658 | msg->data[2] = 0x00; /* unused */ | |
659 | msg->data[3] = 0x00; /* unused */ | |
660 | msg->data[4] = 'D'; | |
661 | msg->data[5] = 'F'; | |
662 | msg->data[6] = 'U'; | |
663 | msg->flags = FU_UNIFYING_HIDPP_MSG_FLAG_IGNORE_SUB_ID; | |
664 | if (!fu_logitech_hidpp_transfer (self->io_channel, msg, error)) { | |
665 | g_prefix_error (error, "failed to put device into DFU mode: "); | |
666 | return FALSE; | |
667 | } | |
668 | return fu_logitech_hidpp_peripheral_setup (FU_DEVICE (self), error); | |
669 | } | |
670 | ||
671 | /* we don't know how */ | |
672 | g_set_error (error, | |
673 | G_IO_ERROR, | |
674 | G_IO_ERROR_FAILED, | |
675 | "no method to detach"); | |
676 | return FALSE; | |
677 | } | |
678 | ||
679 | static gboolean | |
680 | fu_logitech_hidpp_peripheral_check_status (guint8 status, GError **error) | |
681 | { | |
682 | switch (status & 0x7f) { | |
683 | case 0x00: | |
684 | g_set_error (error, | |
685 | G_IO_ERROR, | |
686 | G_IO_ERROR_FAILED, | |
687 | "invalid status value 0x%02x", | |
688 | status); | |
689 | break; | |
690 | case 0x01: /* packet success */ | |
691 | case 0x02: /* DFU success */ | |
692 | case 0x05: /* DFU success: entity restart required */ | |
693 | case 0x06: /* DFU success: system restart required */ | |
694 | /* success */ | |
695 | return TRUE; | |
696 | break; | |
697 | case 0x03: | |
698 | g_set_error_literal (error, | |
699 | G_IO_ERROR, | |
700 | G_IO_ERROR_PENDING, | |
701 | "wait for event (command in progress)"); | |
702 | break; | |
703 | case 0x04: | |
704 | case 0x10: /* unknown */ | |
705 | g_set_error_literal (error, | |
706 | G_IO_ERROR, | |
707 | G_IO_ERROR_FAILED, | |
708 | "generic error"); | |
709 | break; | |
710 | case 0x11: | |
711 | g_set_error_literal (error, | |
712 | G_IO_ERROR, | |
713 | G_IO_ERROR_FAILED, | |
714 | "bad voltage (power too low?)"); | |
715 | break; | |
716 | case 0x12: | |
717 | case 0x14: /* bad magic string */ | |
718 | case 0x21: /* bad firmware */ | |
719 | g_set_error_literal (error, | |
720 | G_IO_ERROR, | |
721 | G_IO_ERROR_FAILED, | |
722 | "unsupported firmware"); | |
723 | break; | |
724 | case 0x13: | |
725 | g_set_error_literal (error, | |
726 | G_IO_ERROR, | |
727 | G_IO_ERROR_FAILED, | |
728 | "unsupported encryption mode"); | |
729 | break; | |
730 | case 0x15: | |
731 | g_set_error_literal (error, | |
732 | G_IO_ERROR, | |
733 | G_IO_ERROR_FAILED, | |
734 | "erase failure"); | |
735 | break; | |
736 | case 0x16: | |
737 | g_set_error_literal (error, | |
738 | G_IO_ERROR, | |
739 | G_IO_ERROR_FAILED, | |
740 | "DFU not started"); | |
741 | break; | |
742 | case 0x17: | |
743 | g_set_error_literal (error, | |
744 | G_IO_ERROR, | |
745 | G_IO_ERROR_FAILED, | |
746 | "bad sequence number"); | |
747 | break; | |
748 | case 0x18: | |
749 | g_set_error_literal (error, | |
750 | G_IO_ERROR, | |
751 | G_IO_ERROR_FAILED, | |
752 | "unsupported command"); | |
753 | break; | |
754 | case 0x19: | |
755 | g_set_error_literal (error, | |
756 | G_IO_ERROR, | |
757 | G_IO_ERROR_FAILED, | |
758 | "command in progress"); | |
759 | break; | |
760 | case 0x1a: | |
761 | g_set_error_literal (error, | |
762 | G_IO_ERROR, | |
763 | G_IO_ERROR_FAILED, | |
764 | "address out of range"); | |
765 | break; | |
766 | case 0x1b: | |
767 | g_set_error_literal (error, | |
768 | G_IO_ERROR, | |
769 | G_IO_ERROR_FAILED, | |
770 | "unaligned address"); | |
771 | break; | |
772 | case 0x1c: | |
773 | g_set_error_literal (error, | |
774 | G_IO_ERROR, | |
775 | G_IO_ERROR_FAILED, | |
776 | "bad size"); | |
777 | break; | |
778 | case 0x1d: | |
779 | g_set_error_literal (error, | |
780 | G_IO_ERROR, | |
781 | G_IO_ERROR_FAILED, | |
782 | "missing program data"); | |
783 | break; | |
784 | case 0x1e: | |
785 | g_set_error_literal (error, | |
786 | G_IO_ERROR, | |
787 | G_IO_ERROR_FAILED, | |
788 | "missing check data"); | |
789 | break; | |
790 | case 0x1f: | |
791 | g_set_error_literal (error, | |
792 | G_IO_ERROR, | |
793 | G_IO_ERROR_FAILED, | |
794 | "program failed to write"); | |
795 | break; | |
796 | case 0x20: | |
797 | g_set_error_literal (error, | |
798 | G_IO_ERROR, | |
799 | G_IO_ERROR_FAILED, | |
800 | "program failed to verify"); | |
801 | break; | |
802 | case 0x22: | |
803 | g_set_error_literal (error, | |
804 | G_IO_ERROR, | |
805 | G_IO_ERROR_FAILED, | |
806 | "firmware check failure"); | |
807 | break; | |
808 | case 0x23: | |
809 | g_set_error_literal (error, | |
810 | G_IO_ERROR, | |
811 | G_IO_ERROR_FAILED, | |
812 | "blocked command (restart required)"); | |
813 | break; | |
814 | default: | |
815 | g_set_error (error, | |
816 | G_IO_ERROR, | |
817 | G_IO_ERROR_FAILED, | |
818 | "unhandled status value 0x%02x", | |
819 | status); | |
820 | break; | |
821 | } | |
822 | return FALSE; | |
823 | } | |
824 | ||
825 | static gboolean | |
826 | fu_logitech_hidpp_peripheral_write_firmware_pkt (FuLogitechHidPpPeripheral *self, | |
827 | guint8 idx, | |
828 | guint8 cmd, | |
829 | const guint8 *data, | |
830 | GError **error) | |
831 | { | |
832 | guint32 packet_cnt; | |
833 | g_autoptr(FuLogitechHidPpHidppMsg) msg = fu_logitech_hidpp_msg_new (); | |
834 | g_autoptr(GError) error_local = NULL; | |
835 | ||
836 | /* send firmware data */ | |
837 | msg->report_id = HIDPP_REPORT_ID_LONG; | |
838 | msg->device_id = self->hidpp_id; | |
839 | msg->sub_id = idx; | |
840 | msg->function_id = cmd << 4; /* dfuStart or dfuCmdDataX */ | |
841 | msg->hidpp_version = self->hidpp_version; | |
842 | memcpy (msg->data, data, 16); | |
843 | if (!fu_logitech_hidpp_transfer (self->io_channel, msg, &error_local)) { | |
844 | g_prefix_error (error, "failed to supply program data: "); | |
845 | return FALSE; | |
846 | } | |
847 | ||
848 | /* check error */ | |
849 | packet_cnt = fu_common_read_uint32 (msg->data, G_BIG_ENDIAN); | |
850 | g_debug ("packet_cnt=0x%04x", packet_cnt); | |
851 | if (fu_logitech_hidpp_peripheral_check_status (msg->data[4], &error_local)) | |
852 | return TRUE; | |
853 | ||
854 | /* fatal error */ | |
855 | if (!g_error_matches (error_local, | |
856 | G_IO_ERROR, | |
857 | G_IO_ERROR_PENDING)) { | |
858 | g_set_error_literal (error, | |
859 | G_IO_ERROR, | |
860 | G_IO_ERROR_FAILED, | |
861 | error_local->message); | |
862 | return FALSE; | |
863 | } | |
864 | ||
865 | /* wait for the HID++ notification */ | |
866 | g_debug ("ignoring: %s", error_local->message); | |
867 | for (guint retry = 0; retry < 10; retry++) { | |
868 | g_autoptr(FuLogitechHidPpHidppMsg) msg2 = fu_logitech_hidpp_msg_new (); | |
869 | msg2->flags = FU_UNIFYING_HIDPP_MSG_FLAG_IGNORE_FNCT_ID; | |
870 | if (!fu_logitech_hidpp_receive (self->io_channel, msg2, 15000, error)) | |
871 | return FALSE; | |
872 | if (fu_logitech_hidpp_msg_is_reply (msg, msg2)) { | |
873 | g_autoptr(GError) error2 = NULL; | |
874 | if (!fu_logitech_hidpp_peripheral_check_status (msg2->data[4], &error2)) { | |
875 | g_debug ("got %s, waiting a bit longer", error2->message); | |
876 | continue; | |
877 | } | |
878 | return TRUE; | |
879 | } else { | |
880 | g_debug ("got wrong packet, continue to wait..."); | |
881 | } | |
882 | } | |
883 | ||
884 | /* nothing in the queue */ | |
885 | g_set_error_literal (error, | |
886 | G_IO_ERROR, | |
887 | G_IO_ERROR_FAILED, | |
888 | "failed to get event after timeout"); | |
889 | return FALSE; | |
890 | } | |
891 | ||
892 | static gboolean | |
893 | fu_logitech_hidpp_peripheral_write_firmware (FuDevice *device, | |
894 | FuFirmware *firmware, | |
895 | FwupdInstallFlags flags, | |
896 | GError **error) | |
897 | { | |
898 | FuLogitechHidPpPeripheral *self = FU_UNIFYING_PERIPHERAL (device); | |
899 | gsize sz = 0; | |
900 | const guint8 *data; | |
901 | guint8 cmd = 0x04; | |
902 | guint8 idx; | |
903 | g_autoptr(GBytes) fw = NULL; | |
904 | ||
905 | /* if we're in bootloader mode, we should be able to get this feature */ | |
906 | idx = fu_logitech_hidpp_peripheral_feature_get_idx (self, HIDPP_FEATURE_DFU); | |
907 | if (idx == 0x00) { | |
908 | g_set_error (error, | |
909 | G_IO_ERROR, | |
910 | G_IO_ERROR_FAILED, | |
911 | "no DFU feature available"); | |
912 | return FALSE; | |
913 | } | |
914 | ||
915 | /* get default image */ | |
916 | fw = fu_firmware_get_image_default_bytes (firmware, error); | |
917 | if (fw == NULL) | |
918 | return FALSE; | |
919 | ||
920 | /* flash hardware */ | |
921 | data = g_bytes_get_data (fw, &sz); | |
922 | fu_device_set_status (device, FWUPD_STATUS_DEVICE_WRITE); | |
923 | for (gsize i = 0; i < sz / 16; i++) { | |
924 | ||
925 | /* send packet and wait for reply */ | |
926 | g_debug ("send data at addr=0x%04x", (guint) i * 16); | |
927 | if (!fu_logitech_hidpp_peripheral_write_firmware_pkt (self, | |
928 | idx, | |
929 | cmd, | |
930 | data + (i * 16), | |
931 | error)) { | |
932 | g_prefix_error (error, | |
933 | "failed to write @0x%04x: ", | |
934 | (guint) i * 16); | |
935 | return FALSE; | |
936 | } | |
937 | ||
938 | /* use sliding window */ | |
939 | cmd = (cmd + 1) % 4; | |
940 | ||
941 | /* update progress-bar */ | |
942 | fu_device_set_progress_full (device, i * 16, sz); | |
943 | } | |
944 | ||
945 | return TRUE; | |
946 | } | |
947 | ||
948 | static gboolean | |
949 | fu_logitech_hidpp_peripheral_attach (FuDevice *device, GError **error) | |
950 | { | |
951 | FuLogitechHidPpPeripheral *self = FU_UNIFYING_PERIPHERAL (device); | |
952 | guint8 idx; | |
953 | g_autoptr(FuLogitechHidPpHidppMsg) msg = fu_logitech_hidpp_msg_new (); | |
954 | ||
955 | /* if we're in bootloader mode, we should be able to get this feature */ | |
956 | idx = fu_logitech_hidpp_peripheral_feature_get_idx (self, HIDPP_FEATURE_DFU); | |
957 | if (idx == 0x00) { | |
958 | g_set_error (error, | |
959 | G_IO_ERROR, | |
960 | G_IO_ERROR_FAILED, | |
961 | "no DFU feature available"); | |
962 | return FALSE; | |
963 | } | |
964 | ||
965 | /* reboot back into firmware mode */ | |
966 | msg->report_id = HIDPP_REPORT_ID_SHORT; | |
967 | msg->device_id = self->hidpp_id; | |
968 | msg->sub_id = idx; | |
969 | msg->function_id = 0x05 << 4; /* restart */ | |
970 | msg->data[0] = self->cached_fw_entity; /* fwEntity */ | |
971 | msg->hidpp_version = self->hidpp_version; | |
972 | msg->flags = FU_UNIFYING_HIDPP_MSG_FLAG_IGNORE_SUB_ID | | |
973 | FU_UNIFYING_HIDPP_MSG_FLAG_IGNORE_SWID | // inferred? | |
974 | FU_UNIFYING_HIDPP_MSG_FLAG_LONGER_TIMEOUT; | |
975 | if (!fu_logitech_hidpp_transfer (self->io_channel, msg, error)) { | |
976 | g_prefix_error (error, "failed to restart device: "); | |
977 | return FALSE; | |
978 | } | |
979 | ||
980 | /* reprobe */ | |
981 | if (!fu_logitech_hidpp_peripheral_setup (device, error)) | |
982 | return FALSE; | |
983 | ||
984 | /* success */ | |
985 | return TRUE; | |
986 | } | |
987 | ||
988 | static void | |
989 | fu_logitech_hidpp_peripheral_finalize (GObject *object) | |
990 | { | |
991 | FuLogitechHidPpPeripheral *self = FU_UNIFYING_PERIPHERAL (object); | |
992 | g_ptr_array_unref (self->feature_index); | |
993 | G_OBJECT_CLASS (fu_logitech_hidpp_peripheral_parent_class)->finalize (object); | |
994 | } | |
995 | ||
996 | static void | |
997 | fu_logitech_hidpp_peripheral_class_init (FuLogitechHidPpPeripheralClass *klass) | |
998 | { | |
999 | FuDeviceClass *klass_device = FU_DEVICE_CLASS (klass); | |
1000 | FuUdevDeviceClass *klass_device_udev = FU_UDEV_DEVICE_CLASS (klass); | |
1001 | GObjectClass *object_class = G_OBJECT_CLASS (klass); | |
1002 | ||
1003 | object_class->finalize = fu_logitech_hidpp_peripheral_finalize; | |
1004 | klass_device->setup = fu_logitech_hidpp_peripheral_setup; | |
1005 | klass_device->open = fu_logitech_hidpp_peripheral_open; | |
1006 | klass_device->close = fu_logitech_hidpp_peripheral_close; | |
1007 | klass_device->write_firmware = fu_logitech_hidpp_peripheral_write_firmware; | |
1008 | klass_device->attach = fu_logitech_hidpp_peripheral_attach; | |
1009 | klass_device->detach = fu_logitech_hidpp_peripheral_detach; | |
1010 | klass_device->poll = fu_logitech_hidpp_peripheral_poll; | |
1011 | klass_device->to_string = fu_logitech_hidpp_peripheral_to_string; | |
1012 | klass_device_udev->probe = fu_logitech_hidpp_peripheral_probe; | |
1013 | } | |
1014 | ||
1015 | static void | |
1016 | fu_logitech_hidpp_peripheral_init (FuLogitechHidPpPeripheral *self) | |
1017 | { | |
1018 | self->hidpp_id = HIDPP_DEVICE_ID_UNSET; | |
1019 | self->feature_index = g_ptr_array_new_with_free_func (g_free); | |
1020 | fu_device_add_parent_guid (FU_DEVICE (self), "HIDRAW\\VEN_046D&DEV_C52B"); | |
1021 | fu_device_set_remove_delay (FU_DEVICE (self), FU_DEVICE_REMOVE_DELAY_RE_ENUMERATE); | |
1022 | ||
1023 | /* there are a lot of unifying peripherals, but not all respond | |
1024 | * well to opening -- so limit to ones with issued updates */ | |
1025 | fu_device_add_flag (FU_DEVICE (self), FWUPD_DEVICE_FLAG_ONLY_SUPPORTED); | |
1026 | } |
0 | /* | |
1 | * Copyright (C) 2017-2018 Richard Hughes <richard@hughsie.com> | |
2 | * | |
3 | * SPDX-License-Identifier: LGPL-2.1+ | |
4 | */ | |
5 | ||
6 | #pragma once | |
7 | ||
8 | #include "fu-udev-device.h" | |
9 | ||
10 | #define FU_TYPE_UNIFYING_PERIPHERAL (fu_logitech_hidpp_peripheral_get_type ()) | |
11 | G_DECLARE_FINAL_TYPE (FuLogitechHidPpPeripheral, fu_logitech_hidpp_peripheral, FU, UNIFYING_PERIPHERAL, FuUdevDevice) |
0 | /* | |
1 | * Copyright (C) 2016-2018 Richard Hughes <richard@hughsie.com> | |
2 | * | |
3 | * SPDX-License-Identifier: LGPL-2.1+ | |
4 | */ | |
5 | ||
6 | #include "config.h" | |
7 | ||
8 | #include <string.h> | |
9 | ||
10 | #include "fu-logitech-hidpp-common.h" | |
11 | #include "fu-logitech-hidpp-runtime.h" | |
12 | #include "fu-logitech-hidpp-hidpp.h" | |
13 | ||
14 | struct _FuLogitechHidPpRuntime | |
15 | { | |
16 | FuUdevDevice parent_instance; | |
17 | guint8 version_bl_major; | |
18 | gboolean signed_firmware; | |
19 | FuIOChannel *io_channel; | |
20 | }; | |
21 | ||
22 | G_DEFINE_TYPE (FuLogitechHidPpRuntime, fu_logitech_hidpp_runtime, FU_TYPE_UDEV_DEVICE) | |
23 | ||
24 | static void | |
25 | fu_logitech_hidpp_runtime_to_string (FuDevice *device, guint idt, GString *str) | |
26 | { | |
27 | FuLogitechHidPpRuntime *self = FU_UNIFYING_RUNTIME (device); | |
28 | fu_common_string_append_kb (str, idt, "SignedFirmware", self->signed_firmware); | |
29 | } | |
30 | ||
31 | static gboolean | |
32 | fu_logitech_hidpp_runtime_enable_notifications (FuLogitechHidPpRuntime *self, GError **error) | |
33 | { | |
34 | g_autoptr(FuLogitechHidPpHidppMsg) msg = fu_logitech_hidpp_msg_new (); | |
35 | msg->report_id = HIDPP_REPORT_ID_SHORT; | |
36 | msg->device_id = HIDPP_DEVICE_ID_RECEIVER; | |
37 | msg->sub_id = HIDPP_SUBID_SET_REGISTER; | |
38 | msg->function_id = HIDPP_REGISTER_HIDPP_NOTIFICATIONS; | |
39 | msg->data[0] = 0x00; | |
40 | msg->data[1] = 0x05; /* Wireless + SoftwarePresent */ | |
41 | msg->data[2] = 0x00; | |
42 | msg->hidpp_version = 1; | |
43 | return fu_logitech_hidpp_transfer (self->io_channel, msg, error); | |
44 | } | |
45 | ||
46 | static gboolean | |
47 | fu_logitech_hidpp_runtime_close (FuDevice *device, GError **error) | |
48 | { | |
49 | FuLogitechHidPpRuntime *self = FU_UNIFYING_RUNTIME (device); | |
50 | if (!fu_io_channel_shutdown (self->io_channel, error)) | |
51 | return FALSE; | |
52 | g_clear_object (&self->io_channel); | |
53 | return TRUE; | |
54 | } | |
55 | ||
56 | static gboolean | |
57 | fu_logitech_hidpp_runtime_poll (FuDevice *device, GError **error) | |
58 | { | |
59 | FuLogitechHidPpRuntime *self = FU_UNIFYING_RUNTIME (device); | |
60 | const guint timeout = 1; /* ms */ | |
61 | g_autoptr(GError) error_local = NULL; | |
62 | g_autoptr(FuLogitechHidPpHidppMsg) msg = fu_logitech_hidpp_msg_new (); | |
63 | g_autoptr(FuDeviceLocker) locker = NULL; | |
64 | ||
65 | /* open */ | |
66 | locker = fu_device_locker_new (self, error); | |
67 | if (locker == NULL) | |
68 | return FALSE; | |
69 | ||
70 | /* is there any pending data to read */ | |
71 | msg->hidpp_version = 1; | |
72 | if (!fu_logitech_hidpp_receive (self->io_channel, msg, timeout, &error_local)) { | |
73 | if (g_error_matches (error_local, | |
74 | G_IO_ERROR, | |
75 | G_IO_ERROR_TIMED_OUT)) { | |
76 | return TRUE; | |
77 | } | |
78 | g_warning ("failed to get pending read: %s", error_local->message); | |
79 | return TRUE; | |
80 | } | |
81 | ||
82 | /* HID++1.0 error */ | |
83 | if (!fu_logitech_hidpp_msg_is_error (msg, &error_local)) { | |
84 | g_warning ("failed to get pending read: %s", error_local->message); | |
85 | return TRUE; | |
86 | } | |
87 | ||
88 | /* unifying receiver notification */ | |
89 | if (msg->report_id == HIDPP_REPORT_ID_SHORT) { | |
90 | switch (msg->sub_id) { | |
91 | case HIDPP_SUBID_DEVICE_CONNECTION: | |
92 | case HIDPP_SUBID_DEVICE_DISCONNECTION: | |
93 | case HIDPP_SUBID_DEVICE_LOCKING_CHANGED: | |
94 | g_debug ("device connection event, do something"); | |
95 | break; | |
96 | case HIDPP_SUBID_LINK_QUALITY: | |
97 | g_debug ("ignoring link quality message"); | |
98 | break; | |
99 | case HIDPP_SUBID_ERROR_MSG: | |
100 | g_debug ("ignoring link quality message"); | |
101 | break; | |
102 | default: | |
103 | g_debug ("unknown SubID %02x", msg->sub_id); | |
104 | break; | |
105 | } | |
106 | } | |
107 | return TRUE; | |
108 | } | |
109 | ||
110 | static gboolean | |
111 | fu_logitech_hidpp_runtime_open (FuDevice *device, GError **error) | |
112 | { | |
113 | FuLogitechHidPpRuntime *self = FU_UNIFYING_RUNTIME (device); | |
114 | GUdevDevice *udev_device = fu_udev_device_get_dev (FU_UDEV_DEVICE (device)); | |
115 | const gchar *devpath = g_udev_device_get_device_file (udev_device); | |
116 | ||
117 | /* open, but don't block */ | |
118 | self->io_channel = fu_io_channel_new_file (devpath, error); | |
119 | if (self->io_channel == NULL) | |
120 | return FALSE; | |
121 | ||
122 | /* poll for notifications */ | |
123 | fu_device_set_poll_interval (device, 5000); | |
124 | ||
125 | /* success */ | |
126 | return TRUE; | |
127 | } | |
128 | ||
129 | static gboolean | |
130 | fu_logitech_hidpp_runtime_probe (FuUdevDevice *device, GError **error) | |
131 | { | |
132 | FuLogitechHidPpRuntime *self = FU_UNIFYING_RUNTIME (device); | |
133 | GUdevDevice *udev_device = fu_udev_device_get_dev (FU_UDEV_DEVICE (device)); | |
134 | guint16 release = 0xffff; | |
135 | g_autoptr(GUdevDevice) udev_parent = NULL; | |
136 | ||
137 | /* set the physical ID */ | |
138 | if (!fu_udev_device_set_physical_id (device, "usb", error)) | |
139 | return FALSE; | |
140 | ||
141 | /* generate bootloader-specific GUID */ | |
142 | udev_parent = g_udev_device_get_parent_with_subsystem (udev_device, | |
143 | "usb", "usb_device"); | |
144 | if (udev_parent != NULL) { | |
145 | const gchar *release_str; | |
146 | release_str = g_udev_device_get_property (udev_parent, "ID_REVISION"); | |
147 | if (release_str != NULL) | |
148 | release = g_ascii_strtoull (release_str, NULL, 16); | |
149 | } | |
150 | if (release != 0xffff) { | |
151 | g_autofree gchar *devid2 = NULL; | |
152 | switch (release &= 0xff00) { | |
153 | case 0x1200: | |
154 | /* Nordic */ | |
155 | devid2 = g_strdup_printf ("USB\\VID_%04X&PID_%04X", | |
156 | (guint) FU_UNIFYING_DEVICE_VID, | |
157 | (guint) FU_UNIFYING_DEVICE_PID_BOOTLOADER_NORDIC); | |
158 | fu_device_add_counterpart_guid (FU_DEVICE (device), devid2); | |
159 | self->version_bl_major = 0x01; | |
160 | break; | |
161 | case 0x2400: | |
162 | /* Texas */ | |
163 | devid2 = g_strdup_printf ("USB\\VID_%04X&PID_%04X", | |
164 | (guint) FU_UNIFYING_DEVICE_VID, | |
165 | (guint) FU_UNIFYING_DEVICE_PID_BOOTLOADER_TEXAS); | |
166 | fu_device_add_counterpart_guid (FU_DEVICE (device), devid2); | |
167 | self->version_bl_major = 0x03; | |
168 | break; | |
169 | default: | |
170 | g_warning ("bootloader release %04x invalid", release); | |
171 | break; | |
172 | } | |
173 | } | |
174 | return TRUE; | |
175 | } | |
176 | ||
177 | static gboolean | |
178 | fu_logitech_hidpp_runtime_setup_internal (FuDevice *device, GError **error) | |
179 | { | |
180 | FuLogitechHidPpRuntime *self = FU_UNIFYING_RUNTIME (device); | |
181 | guint8 config[10]; | |
182 | g_autofree gchar *version_fw = NULL; | |
183 | ||
184 | /* read all 10 bytes of the version register */ | |
185 | memset (config, 0x00, sizeof (config)); | |
186 | for (guint i = 0x01; i < 0x05; i++) { | |
187 | g_autoptr(FuLogitechHidPpHidppMsg) msg = fu_logitech_hidpp_msg_new (); | |
188 | ||
189 | /* workaround a bug in the 12.01 firmware, which fails with | |
190 | * INVALID_VALUE when reading MCU1_HW_VERSION */ | |
191 | if (i == 0x03) | |
192 | continue; | |
193 | ||
194 | msg->report_id = HIDPP_REPORT_ID_SHORT; | |
195 | msg->device_id = HIDPP_DEVICE_ID_RECEIVER; | |
196 | msg->sub_id = HIDPP_SUBID_GET_REGISTER; | |
197 | msg->function_id = HIDPP_REGISTER_DEVICE_FIRMWARE_INFORMATION; | |
198 | msg->data[0] = i; | |
199 | msg->hidpp_version = 1; | |
200 | if (!fu_logitech_hidpp_transfer (self->io_channel, msg, error)) { | |
201 | g_prefix_error (error, "failed to read device config: "); | |
202 | return FALSE; | |
203 | } | |
204 | if (!fu_memcpy_safe (config, sizeof(config), i * 2, /* dst */ | |
205 | msg->data, sizeof(msg->data), 0x1, /* src */ | |
206 | 2, error)) | |
207 | return FALSE; | |
208 | } | |
209 | ||
210 | /* get firmware version */ | |
211 | version_fw = fu_logitech_hidpp_format_version ("RQR", | |
212 | config[2], | |
213 | config[3], | |
214 | (guint16) config[4] << 8 | | |
215 | config[5]); | |
216 | fu_device_set_version (device, version_fw, FWUPD_VERSION_FORMAT_PLAIN); | |
217 | ||
218 | /* get bootloader version */ | |
219 | if (self->version_bl_major > 0) { | |
220 | g_autofree gchar *version_bl = NULL; | |
221 | version_bl = fu_logitech_hidpp_format_version ("BOT", | |
222 | self->version_bl_major, | |
223 | config[8], | |
224 | config[9]); | |
225 | fu_device_set_version_bootloader (FU_DEVICE (device), version_bl); | |
226 | ||
227 | /* is the dongle expecting signed firmware */ | |
228 | if ((self->version_bl_major == 0x01 && config[8] >= 0x04) || | |
229 | (self->version_bl_major == 0x03 && config[8] >= 0x02)) { | |
230 | self->signed_firmware = TRUE; | |
231 | } | |
232 | } | |
233 | ||
234 | /* enable HID++ notifications */ | |
235 | if (!fu_logitech_hidpp_runtime_enable_notifications (self, error)) { | |
236 | g_prefix_error (error, "failed to enable notifications: "); | |
237 | return FALSE; | |
238 | } | |
239 | ||
240 | /* success */ | |
241 | return TRUE; | |
242 | } | |
243 | ||
244 | static gboolean | |
245 | fu_logitech_hidpp_runtime_setup (FuDevice *device, GError **error) | |
246 | { | |
247 | g_autoptr(GError) error_local = NULL; | |
248 | for (guint i = 0; i < 5; i++) { | |
249 | /* HID++1.0 devices have to sleep to allow Solaar to talk to | |
250 | * the device first -- we can't use the SwID as this is a | |
251 | * HID++2.0 feature */ | |
252 | g_usleep (200*1000); | |
253 | if (fu_logitech_hidpp_runtime_setup_internal (device, &error_local)) | |
254 | return TRUE; | |
255 | if (!g_error_matches (error_local, | |
256 | G_IO_ERROR, | |
257 | G_IO_ERROR_INVALID_DATA)) { | |
258 | g_propagate_error (error, g_steal_pointer (&error_local)); | |
259 | return FALSE; | |
260 | } | |
261 | g_clear_error (&error_local); | |
262 | } | |
263 | g_propagate_error (error, g_steal_pointer (&error_local)); | |
264 | return FALSE; | |
265 | } | |
266 | ||
267 | static gboolean | |
268 | fu_logitech_hidpp_runtime_detach (FuDevice *device, GError **error) | |
269 | { | |
270 | FuLogitechHidPpRuntime *self = FU_UNIFYING_RUNTIME (device); | |
271 | g_autoptr(FuLogitechHidPpHidppMsg) msg = fu_logitech_hidpp_msg_new (); | |
272 | msg->report_id = HIDPP_REPORT_ID_SHORT; | |
273 | msg->device_id = HIDPP_DEVICE_ID_RECEIVER; | |
274 | msg->sub_id = HIDPP_SUBID_SET_REGISTER; | |
275 | msg->function_id = HIDPP_REGISTER_DEVICE_FIRMWARE_UPDATE_MODE; | |
276 | msg->data[0] = 'I'; | |
277 | msg->data[1] = 'C'; | |
278 | msg->data[2] = 'P'; | |
279 | msg->hidpp_version = 1; | |
280 | msg->flags = FU_UNIFYING_HIDPP_MSG_FLAG_LONGER_TIMEOUT; | |
281 | if (!fu_logitech_hidpp_send (self->io_channel, msg, FU_UNIFYING_DEVICE_TIMEOUT_MS, error)) { | |
282 | g_prefix_error (error, "failed to detach to bootloader: "); | |
283 | return FALSE; | |
284 | } | |
285 | fu_device_add_flag (device, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG); | |
286 | return TRUE; | |
287 | } | |
288 | ||
289 | static void | |
290 | fu_logitech_hidpp_runtime_finalize (GObject *object) | |
291 | { | |
292 | G_OBJECT_CLASS (fu_logitech_hidpp_runtime_parent_class)->finalize (object); | |
293 | } | |
294 | ||
295 | static void | |
296 | fu_logitech_hidpp_runtime_class_init (FuLogitechHidPpRuntimeClass *klass) | |
297 | { | |
298 | FuDeviceClass *klass_device = FU_DEVICE_CLASS (klass); | |
299 | FuUdevDeviceClass *klass_device_udev = FU_UDEV_DEVICE_CLASS (klass); | |
300 | GObjectClass *object_class = G_OBJECT_CLASS (klass); | |
301 | ||
302 | object_class->finalize = fu_logitech_hidpp_runtime_finalize; | |
303 | klass_device->open = fu_logitech_hidpp_runtime_open; | |
304 | klass_device_udev->probe = fu_logitech_hidpp_runtime_probe; | |
305 | klass_device->setup = fu_logitech_hidpp_runtime_setup; | |
306 | klass_device->close = fu_logitech_hidpp_runtime_close; | |
307 | klass_device->detach = fu_logitech_hidpp_runtime_detach; | |
308 | klass_device->poll = fu_logitech_hidpp_runtime_poll; | |
309 | klass_device->to_string = fu_logitech_hidpp_runtime_to_string; | |
310 | } | |
311 | ||
312 | static void | |
313 | fu_logitech_hidpp_runtime_init (FuLogitechHidPpRuntime *self) | |
314 | { | |
315 | fu_device_add_flag (FU_DEVICE (self), FWUPD_DEVICE_FLAG_UPDATABLE); | |
316 | fu_device_add_icon (FU_DEVICE (self), "preferences-desktop-keyboard"); | |
317 | fu_device_set_name (FU_DEVICE (self), "Unifying Receiver"); | |
318 | fu_device_set_summary (FU_DEVICE (self), "A miniaturised USB wireless receiver"); | |
319 | fu_device_set_remove_delay (FU_DEVICE (self), FU_DEVICE_REMOVE_DELAY_RE_ENUMERATE); | |
320 | } |
0 | /* | |
1 | * Copyright (C) 2016-2018 Richard Hughes <richard@hughsie.com> | |
2 | * | |
3 | * SPDX-License-Identifier: LGPL-2.1+ | |
4 | */ | |
5 | ||
6 | #pragma once | |
7 | ||
8 | #include "fu-udev-device.h" | |
9 | ||
10 | #define FU_TYPE_UNIFYING_RUNTIME (fu_logitech_hidpp_runtime_get_type ()) | |
11 | G_DECLARE_FINAL_TYPE (FuLogitechHidPpRuntime, fu_logitech_hidpp_runtime, FU, UNIFYING_RUNTIME, FuUdevDevice) |
0 | /* | |
1 | * Copyright (C) 2017-2018 Richard Hughes <richard@hughsie.com> | |
2 | * | |
3 | * SPDX-License-Identifier: LGPL-2.1+ | |
4 | */ | |
5 | ||
6 | #include "config.h" | |
7 | ||
8 | #include <fwupd.h> | |
9 | #include <glib-object.h> | |
10 | ||
11 | #include "fu-logitech-hidpp-common.h" | |
12 | ||
13 | static void | |
14 | fu_logitech_hidpp_common (void) | |
15 | { | |
16 | guint8 u8; | |
17 | guint16 u16; | |
18 | g_autofree gchar *ver1 = NULL; | |
19 | ||
20 | u8 = fu_logitech_hidpp_buffer_read_uint8 ("12"); | |
21 | g_assert_cmpint (u8, ==, 0x12); | |
22 | u16 = fu_logitech_hidpp_buffer_read_uint16 ("1234"); | |
23 | g_assert_cmpint (u16, ==, 0x1234); | |
24 | ||
25 | ver1 = fu_logitech_hidpp_format_version (" A ", 0x87, 0x65, 0x4321); | |
26 | g_assert_cmpstr (ver1, ==, "A87.65_B4321"); | |
27 | } | |
28 | ||
29 | int | |
30 | main (int argc, char **argv) | |
31 | { | |
32 | g_test_init (&argc, &argv, NULL); | |
33 | ||
34 | /* only critical and error are fatal */ | |
35 | g_log_set_fatal_mask (NULL, G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL); | |
36 | ||
37 | /* tests go here */ | |
38 | g_test_add_func ("/unifying/common", fu_logitech_hidpp_common); | |
39 | return g_test_run (); | |
40 | } |
0 | /* | |
1 | * Copyright (C) 2016-2018 Richard Hughes <richard@hughsie.com> | |
2 | * | |
3 | * SPDX-License-Identifier: LGPL-2.1+ | |
4 | */ | |
5 | ||
6 | #include "config.h" | |
7 | ||
8 | #include <fwupd.h> | |
9 | ||
10 | #include "fu-plugin-vfuncs.h" | |
11 | ||
12 | #include "fu-logitech-hidpp-bootloader-nordic.h" | |
13 | #include "fu-logitech-hidpp-bootloader-texas.h" | |
14 | #include "fu-logitech-hidpp-common.h" | |
15 | #include "fu-logitech-hidpp-peripheral.h" | |
16 | #include "fu-logitech-hidpp-runtime.h" | |
17 | ||
18 | gboolean | |
19 | fu_plugin_startup (FuPlugin *plugin, GError **error) | |
20 | { | |
21 | /* check the kernel has CONFIG_HIDRAW */ | |
22 | if (!g_file_test ("/sys/class/hidraw", G_FILE_TEST_IS_DIR)) { | |
23 | g_set_error_literal (error, | |
24 | FWUPD_ERROR, | |
25 | FWUPD_ERROR_NOT_SUPPORTED, | |
26 | "no kernel support for CONFIG_HIDRAW"); | |
27 | return FALSE; | |
28 | } | |
29 | return TRUE; | |
30 | } | |
31 | ||
32 | void | |
33 | fu_plugin_init (FuPlugin *plugin) | |
34 | { | |
35 | fu_plugin_set_build_hash (plugin, FU_BUILD_HASH); | |
36 | fu_plugin_add_rule (plugin, FU_PLUGIN_RULE_SUPPORTS_PROTOCOL, "com.logitech.unifying"); | |
37 | fu_plugin_add_rule (plugin, FU_PLUGIN_RULE_SUPPORTS_PROTOCOL, "com.logitech.unifyingsigned"); | |
38 | fu_plugin_add_udev_subsystem (plugin, "hidraw"); | |
39 | fu_plugin_add_rule (plugin, FU_PLUGIN_RULE_CONFLICTS, "unifying"); | |
40 | ||
41 | /* register the custom types */ | |
42 | g_type_ensure (FU_TYPE_UNIFYING_BOOTLOADER_NORDIC); | |
43 | g_type_ensure (FU_TYPE_UNIFYING_BOOTLOADER_TEXAS); | |
44 | g_type_ensure (FU_TYPE_UNIFYING_PERIPHERAL); | |
45 | g_type_ensure (FU_TYPE_UNIFYING_RUNTIME); | |
46 | } |
0 | # Unifying Receiver | |
1 | [DeviceInstanceId=HIDRAW\VEN_046D&DEV_C52B] | |
2 | Plugin = logitech_hidpp | |
3 | GType = FuLogitechHidPpRuntime | |
4 | VendorId=USB:0x046D | |
5 | InstallDuration = 30 | |
6 | ||
7 | # Nordic | |
8 | [DeviceInstanceId=USB\VID_046D&PID_AAAA] | |
9 | Plugin = logitech_hidpp | |
10 | GType = FuLogitechHidPpBootloaderNordic | |
11 | FirmwareSizeMin = 0x4000 | |
12 | CounterpartGuid = HIDRAW\VEN_046D&DEV_C52B | |
13 | InstallDuration = 30 | |
14 | ||
15 | # Nordic Pico | |
16 | [DeviceInstanceId=USB\VID_046D&PID_AAAE] | |
17 | Plugin = logitech_hidpp | |
18 | GType = FuLogitechHidPpBootloaderNordic | |
19 | FirmwareSizeMin = 0x4000 | |
20 | CounterpartGuid = HIDRAW\VEN_046D&DEV_C52B | |
21 | InstallDuration = 30 | |
22 | ||
23 | # Texas | |
24 | [DeviceInstanceId=USB\VID_046D&PID_AAAC] | |
25 | Plugin = logitech_hidpp | |
26 | GType = FuLogitechHidPpBootloaderTexas | |
27 | FirmwareSizeMin = 0x4000 | |
28 | CounterpartGuid = HIDRAW\VEN_046D&DEV_C52B | |
29 | InstallDuration = 18 | |
30 | ||
31 | # Texas Pico | |
32 | [DeviceInstanceId=USB\VID_046D&PID_AAAD] | |
33 | Plugin = logitech_hidpp | |
34 | GType = FuLogitechHidPpBootloaderTexas | |
35 | FirmwareSizeMin = 0x4000 | |
36 | CounterpartGuid = HIDRAW\VEN_046D&DEV_C52B | |
37 | InstallDuration = 18 | |
38 | ||
39 | # Possible HID++ v2.0 peripheral device | |
40 | [DeviceInstanceId=HIDRAW\VEN_046D] | |
41 | Plugin = logitech_hidpp | |
42 | GType = FuLogitechHidPpPeripheral |
0 | cargs = ['-DG_LOG_DOMAIN="FuPluginLogitechHidPp"'] | |
1 | ||
2 | install_data([ | |
3 | 'logitech-hidpp.quirk', | |
4 | ], | |
5 | install_dir: join_paths(datadir, 'fwupd', 'quirks.d') | |
6 | ) | |
7 | ||
8 | ||
9 | shared_module('fu_plugin_logitech_hidpp', | |
10 | fu_hash, | |
11 | sources : [ | |
12 | 'fu-plugin-logitech-hidpp.c', | |
13 | 'fu-logitech-hidpp-bootloader.c', | |
14 | 'fu-logitech-hidpp-bootloader-nordic.c', | |
15 | 'fu-logitech-hidpp-bootloader-texas.c', | |
16 | 'fu-logitech-hidpp-common.c', | |
17 | 'fu-logitech-hidpp-hidpp.c', | |
18 | 'fu-logitech-hidpp-hidpp-msg.c', | |
19 | 'fu-logitech-hidpp-peripheral.c', | |
20 | 'fu-logitech-hidpp-runtime.c', | |
21 | ], | |
22 | include_directories : [ | |
23 | include_directories('../..'), | |
24 | include_directories('../../src'), | |
25 | include_directories('../../libfwupd'), | |
26 | ], | |
27 | install : true, | |
28 | install_dir: plugin_dir, | |
29 | link_with : [ | |
30 | libfwupdprivate, | |
31 | ], | |
32 | c_args : cargs, | |
33 | dependencies : [ | |
34 | plugin_deps, | |
35 | ], | |
36 | ) | |
37 | ||
38 | if get_option('tests') | |
39 | e = executable( | |
40 | 'logitech-hidpp-self-test', | |
41 | fu_hash, | |
42 | sources : [ | |
43 | 'fu-logitech-hidpp-self-test.c', | |
44 | 'fu-logitech-hidpp-common.c', | |
45 | ], | |
46 | include_directories : [ | |
47 | include_directories('../..'), | |
48 | include_directories('../../libfwupd'), | |
49 | ], | |
50 | dependencies : [ | |
51 | plugin_deps, | |
52 | ], | |
53 | link_with : [ | |
54 | libfwupdprivate, | |
55 | ], | |
56 | c_args : cargs, | |
57 | ) | |
58 | test('logitech-hidpp-self-test', e) | |
59 | endif |
6 | 6 | subdir('jabra') |
7 | 7 | subdir('steelseries') |
8 | 8 | subdir('dell-dock') |
9 | subdir('logitech-hidpp') | |
9 | 10 | subdir('nitrokey') |
10 | 11 | subdir('optionrom') |
11 | 12 | subdir('rts54hid') |
15 | 16 | subdir('synaptics-prometheus') |
16 | 17 | subdir('test') |
17 | 18 | subdir('thelio-io') |
18 | subdir('unifying') | |
19 | 19 | subdir('upower') |
20 | 20 | subdir('wacom-raw') |
21 | 21 | subdir('wacom-usb') |
13 | 13 | #include "fu-device-private.h" |
14 | 14 | #include "fu-mm-utils.h" |
15 | 15 | #include "fu-qmi-pdc-updater.h" |
16 | ||
17 | /* Amount of time for the modem to boot in fastboot mode. */ | |
18 | #define FU_MM_DEVICE_REMOVE_DELAY_RE_ENUMERATE 20000 /* ms */ | |
16 | 19 | |
17 | 20 | /* Amount of time for the modem to be re-probed and exposed in MM after being |
18 | 21 | * uninhibited. The timeout is long enough to cover the worst case, where the |
232 | 235 | fu_device_set_vendor (device, mm_modem_get_manufacturer (modem)); |
233 | 236 | if (mm_modem_get_model (modem) != NULL) |
234 | 237 | fu_device_set_name (device, mm_modem_get_model (modem)); |
235 | fu_device_set_version (device, version, FWUPD_VERSION_FORMAT_UNKNOWN); | |
238 | fu_device_set_version (device, version, FWUPD_VERSION_FORMAT_PLAIN); | |
236 | 239 | for (guint i = 0; device_ids[i] != NULL; i++) |
237 | 240 | fu_device_add_instance_id (device, device_ids[i]); |
238 | 241 | |
372 | 375 | } |
373 | 376 | |
374 | 377 | /* success */ |
375 | fu_device_set_remove_delay (device, FU_DEVICE_REMOVE_DELAY_RE_ENUMERATE); | |
378 | fu_device_set_remove_delay (device, FU_MM_DEVICE_REMOVE_DELAY_RE_ENUMERATE); | |
376 | 379 | fu_device_add_flag (device, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG); |
377 | 380 | return TRUE; |
378 | 381 | } |
482 | 485 | return (g_strstr_len (version, -1, carrier_id) != NULL); |
483 | 486 | } |
484 | 487 | |
485 | static void | |
488 | static gboolean | |
486 | 489 | fu_mm_qmi_pdc_archive_iterate_mcfg (FuArchive *archive, |
487 | 490 | const gchar *filename, |
488 | 491 | GBytes *bytes, |
489 | gpointer user_data) | |
492 | gpointer user_data, | |
493 | GError **error) | |
490 | 494 | { |
491 | 495 | FuMmArchiveIterateCtx *ctx = user_data; |
492 | 496 | FuMmFileInfo *file_info; |
493 | 497 | |
494 | 498 | /* filenames should be named as 'mcfg.*.mbn', e.g.: mcfg.A2.018.mbn */ |
495 | 499 | if (!g_str_has_prefix (filename, "mcfg.") || !g_str_has_suffix (filename, ".mbn")) |
496 | return; | |
500 | return TRUE; | |
497 | 501 | |
498 | 502 | file_info = g_new0 (FuMmFileInfo, 1); |
499 | 503 | file_info->filename = g_strdup (filename); |
501 | 505 | file_info->active = fu_mm_should_be_active (fu_device_get_version (FU_DEVICE (ctx->device)), filename); |
502 | 506 | g_ptr_array_add (ctx->file_infos, file_info); |
503 | 507 | ctx->total_bytes += g_bytes_get_size (file_info->bytes); |
508 | return TRUE; | |
504 | 509 | } |
505 | 510 | |
506 | 511 | static gboolean |
558 | 563 | return FALSE; |
559 | 564 | |
560 | 565 | /* process the list of MCFG files to write */ |
561 | fu_archive_iterate (archive, fu_mm_qmi_pdc_archive_iterate_mcfg, &archive_context); | |
566 | if (!fu_archive_iterate (archive, | |
567 | fu_mm_qmi_pdc_archive_iterate_mcfg, | |
568 | &archive_context, | |
569 | error)) | |
570 | return FALSE; | |
562 | 571 | |
563 | 572 | for (guint i = 0; i < file_infos->len; i++) { |
564 | 573 | FuMmFileInfo *file_info = g_ptr_array_index (file_infos, i); |
794 | 803 | fu_device_set_physical_id (FU_DEVICE (self), info->physical_id); |
795 | 804 | fu_device_set_vendor (FU_DEVICE (self), info->vendor); |
796 | 805 | fu_device_set_name (FU_DEVICE (self), info->name); |
797 | fu_device_set_version (FU_DEVICE (self), info->version, FWUPD_VERSION_FORMAT_UNKNOWN); | |
806 | fu_device_set_version (FU_DEVICE (self), info->version, FWUPD_VERSION_FORMAT_PLAIN); | |
798 | 807 | self->update_methods = info->update_methods; |
799 | 808 | self->detach_fastboot_at = g_strdup (info->detach_fastboot_at); |
800 | 809 | self->port_at_ifnum = info->port_at_ifnum; |
6 | 6 | #include "config.h" |
7 | 7 | |
8 | 8 | #include <gio/gio.h> |
9 | ||
10 | #include "fu-udev-device.h" | |
11 | ||
9 | 12 | #include "fu-mm-utils.h" |
10 | 13 | |
11 | 14 | gboolean |
9 | 9 | #include "config.h" |
10 | 10 | #include <gudev/gudev.h> |
11 | 11 | |
12 | #ifndef HAVE_GUDEV_232 | |
13 | #pragma clang diagnostic push | |
14 | #pragma clang diagnostic ignored "-Wunused-function" | |
15 | G_DEFINE_AUTOPTR_CLEANUP_FUNC(GUdevClient, g_object_unref) | |
16 | G_DEFINE_AUTOPTR_CLEANUP_FUNC(GUdevDevice, g_object_unref) | |
17 | #pragma clang diagnostic pop | |
18 | #endif | |
19 | ||
20 | 12 | gboolean fu_mm_utils_get_udev_port_info (GUdevDevice *dev, |
21 | 13 | gchar **device_sysfs_path, |
22 | 14 | gint *port_ifnum, |
8 | 8 | Summary = Dell DW5821e LTE modem (fastboot) |
9 | 9 | CounterpartGuid = USB\VID_413C&PID_81D7 |
10 | 10 | |
11 | # DW5821e/eSIM | |
12 | [DeviceInstanceId=USB\VID_413C&PID_81E0] | |
13 | Summary = Dell DW5821e/eSIM LTE modem | |
14 | CounterpartGuid = USB\VID_413C&PID_81E1 | |
15 | ||
16 | # DW5821e/eSIM in fastboot mode | |
17 | [DeviceInstanceId=USB\VID_413C&PID_81E1] | |
18 | Summary = Dell DW5821e/eSIM LTE modem (fastboot) | |
19 | CounterpartGuid = USB\VID_413C&PID_81E0 | |
20 | ||
21 | # T77W968 | |
22 | [DeviceInstanceId=USB\VID_0489&PID_E0B4] | |
23 | Summary = Foxconn T77w968 LTE modem | |
24 | CounterpartGuid = USB\VID_0489&PID_E0B7 | |
25 | ||
26 | # T77W968 in fastboot mode | |
27 | [DeviceInstanceId=USB\VID_0489&PID_E0B7] | |
28 | Summary = Foxconn T77w968 LTE modem (fastboot) | |
29 | CounterpartGuid = USB\VID_0489&PID_E0B4 | |
30 | ||
31 | # T77W968/eSIM | |
32 | [DeviceInstanceId=USB\VID_0489&PID_E0B5] | |
33 | Summary = Foxconn T77w968/eSIM LTE modem | |
34 | CounterpartGuid = USB\VID_0489&PID_E0B8 | |
35 | ||
36 | # T77W968/eSIM in fastboot mode | |
37 | [DeviceInstanceId=USB\VID_0489&PID_E0B8] | |
38 | Summary = Foxconn T77w968/eSIM LTE modem (fastboot) | |
39 | CounterpartGuid = USB\VID_0489&PID_E0B5 |
21 | 21 | }; |
22 | 22 | |
23 | 23 | G_DEFINE_TYPE (FuNvmeDevice, fu_nvme_device, FU_TYPE_UDEV_DEVICE) |
24 | ||
25 | #ifndef HAVE_GUDEV_232 | |
26 | #pragma clang diagnostic push | |
27 | #pragma clang diagnostic ignored "-Wunused-function" | |
28 | G_DEFINE_AUTOPTR_CLEANUP_FUNC(GUdevDevice, g_object_unref) | |
29 | #pragma clang diagnostic pop | |
30 | #endif | |
31 | 24 | |
32 | 25 | static void |
33 | 26 | fu_nvme_device_to_string (FuDevice *device, guint idt, GString *str) |
469 | 469 | } |
470 | 470 | |
471 | 471 | /* verify the signature and reboot back to runtime */ |
472 | fw_sig = fu_firmware_get_image_by_id_bytes (firmware, "signature", error); | |
472 | fw_sig = fu_firmware_get_image_by_id_bytes (firmware, | |
473 | FU_FIRMWARE_IMAGE_ID_SIGNATURE, | |
474 | error); | |
473 | 475 | if (fw_sig == NULL) |
474 | 476 | return FALSE; |
475 | 477 | return fu_solokey_device_verify (self, fw_sig, error); |
87 | 87 | g_string_append (base64_websafe, "=="); |
88 | 88 | fw_sig = _g_base64_decode_to_bytes (base64_websafe->str); |
89 | 89 | fu_firmware_image_set_bytes (img_sig, fw_sig); |
90 | fu_firmware_image_set_id (img_sig, "signature"); | |
90 | fu_firmware_image_set_id (img_sig, FU_FIRMWARE_IMAGE_ID_SIGNATURE); | |
91 | 91 | fu_firmware_add_image (firmware, img_sig); |
92 | 92 | return TRUE; |
93 | 93 | } |
44 | 44 | /* create IT89xx or IT89xx */ |
45 | 45 | if (id >> 8 == 0x85) { |
46 | 46 | dev = g_object_new (FU_TYPE_SUPERIO_IT85_DEVICE, |
47 | "device-file", "/dev/port", | |
47 | 48 | "chipset", chipset, |
48 | 49 | "id", id, |
49 | 50 | "port", port, |
50 | 51 | NULL); |
51 | 52 | } else if (id >> 8 == 0x89) { |
52 | 53 | dev = g_object_new (FU_TYPE_SUPERIO_IT89_DEVICE, |
54 | "device-file", "/dev/port", | |
53 | 55 | "chipset", chipset, |
54 | 56 | "id", id, |
55 | 57 | "port", port, |
19 | 19 | guint16 id; |
20 | 20 | } FuSuperioDevicePrivate; |
21 | 21 | |
22 | G_DEFINE_TYPE_WITH_PRIVATE (FuSuperioDevice, fu_superio_device, FU_TYPE_DEVICE) | |
22 | G_DEFINE_TYPE_WITH_PRIVATE (FuSuperioDevice, fu_superio_device, FU_TYPE_UDEV_DEVICE) | |
23 | 23 | |
24 | 24 | #define GET_PRIVATE(o) (fu_superio_device_get_instance_private (o)) |
25 | 25 | |
262 | 262 | FuSuperioDeviceClass *klass = FU_SUPERIO_DEVICE_GET_CLASS (device); |
263 | 263 | FuSuperioDevice *self = FU_SUPERIO_DEVICE (device); |
264 | 264 | FuSuperioDevicePrivate *priv = GET_PRIVATE (self); |
265 | guint8 tmp = 0x0; | |
266 | ||
267 | /* check port is valid */ | |
268 | if (!fu_udev_device_pread (FU_UDEV_DEVICE (self), priv->pm1_iobad0, &tmp, error)) | |
269 | return FALSE; | |
270 | if (tmp != 0xff) { | |
271 | g_set_error_literal (error, | |
272 | FWUPD_ERROR, | |
273 | FWUPD_ERROR_NOT_SUPPORTED, | |
274 | "check port!"); | |
275 | return FALSE; | |
276 | } | |
277 | 265 | |
278 | 266 | /* check ID is correct */ |
279 | 267 | if (!fu_superio_device_check_id (self, error)) { |
8 | 8 | #include "fu-plugin.h" |
9 | 9 | |
10 | 10 | #define FU_TYPE_SUPERIO_DEVICE (fu_superio_device_get_type ()) |
11 | G_DECLARE_DERIVABLE_TYPE (FuSuperioDevice, fu_superio_device, FU, SUPERIO_DEVICE, FuDevice) | |
11 | G_DECLARE_DERIVABLE_TYPE (FuSuperioDevice, fu_superio_device, FU, SUPERIO_DEVICE, FuUdevDevice) | |
12 | 12 | |
13 | 13 | struct _FuSuperioDeviceClass |
14 | 14 | { |
15 | FuDeviceClass parent_class; | |
15 | FuUdevDeviceClass parent_class; | |
16 | 16 | gboolean (*setup) (FuSuperioDevice *self, |
17 | 17 | GError **error); |
18 | 18 | }; |
619 | 619 | g_debug ("ignoring: %s", error_local->message); |
620 | 620 | } |
621 | 621 | |
622 | fu_udev_device_set_fd (device, -1); | |
622 | 623 | g_clear_object (&priv->io_channel); |
623 | 624 | return TRUE; |
624 | 625 | } |
26 | 26 | gsize bufsz = 0; |
27 | 27 | g_autofree gchar *buf = NULL; |
28 | 28 | g_auto(GStrv) lines = NULL; |
29 | ||
30 | /* no module support in the kernel, we can't test for amdgpu module */ | |
31 | if (!g_file_test ("/proc/modules", G_FILE_TEST_EXISTS)) | |
32 | return TRUE; | |
29 | 33 | |
30 | 34 | if (!g_file_get_contents ("/proc/modules", &buf, &bufsz, error)) |
31 | 35 | return FALSE; |
1046 | 1046 | return FALSE; |
1047 | 1047 | } |
1048 | 1048 | self->chip_id = (buf_ver[0] << 8) | (buf_ver[1]); |
1049 | if (self->chip_id == 0) { | |
1050 | g_set_error_literal (error, | |
1051 | G_IO_ERROR, | |
1052 | G_IO_ERROR_INVALID_DATA, | |
1053 | "invalid chip ID"); | |
1054 | return FALSE; | |
1055 | } | |
1049 | 1056 | self->family = fu_synapticsmst_family_from_chip_id (self->chip_id); |
1050 | 1057 | |
1051 | 1058 | /* check the active bank for debugging */ |
18 | 18 | #include "fu-device-metadata.h" |
19 | 19 | #include "fu-thunderbolt-image.h" |
20 | 20 | |
21 | #ifndef HAVE_GUDEV_232 | |
22 | #pragma clang diagnostic push | |
23 | #pragma clang diagnostic ignored "-Wunused-function" | |
24 | G_DEFINE_AUTOPTR_CLEANUP_FUNC(GUdevDevice, g_object_unref) | |
25 | #pragma clang diagnostic pop | |
26 | #endif | |
27 | ||
28 | 21 | #define TBT_NVM_RETRY_TIMEOUT 200 /* ms */ |
29 | 22 | #define FU_PLUGIN_THUNDERBOLT_UPDATE_TIMEOUT 60000 /* ms */ |
30 | 23 | |
98 | 91 | g_set_error (error, |
99 | 92 | FWUPD_ERROR, |
100 | 93 | FWUPD_ERROR_INTERNAL, |
101 | "failed get id %s for %s", name, sysfs); | |
94 | "missing sysfs attribute %s", name); | |
102 | 95 | return FALSE; |
103 | 96 | } |
104 | 97 |
16 | 16 | #define BOLT_DBUS_SERVICE "org.freedesktop.bolt" |
17 | 17 | #define BOLT_DBUS_PATH "/org/freedesktop/bolt" |
18 | 18 | #define BOLT_DBUS_INTERFACE "org.freedesktop.bolt1.Power" |
19 | ||
20 | #ifndef HAVE_GUDEV_232 | |
21 | #pragma clang diagnostic push | |
22 | #pragma clang diagnostic ignored "-Wunused-function" | |
23 | G_DEFINE_AUTOPTR_CLEANUP_FUNC(GUdevDevice, g_object_unref) | |
24 | #pragma clang diagnostic pop | |
25 | #endif | |
26 | 19 | |
27 | 20 | /* empirically measured amount of time for the TBT device to come and go */ |
28 | 21 | #define TBT_NEW_DEVICE_TIMEOUT 2 /* s */ |
0 | 0 | #!/bin/sh |
1 | 1 | output=$2 |
2 | objcopy_cmd=$(which objcopy) | |
3 | genpeimg_cmd=$(which genpeimg) | |
2 | objcopy_cmd=$(command -v objcopy) | |
3 | genpeimg_cmd=$(command -v genpeimg) | |
4 | 4 | |
5 | $objcopy_cmd -j .text \ | |
5 | "$objcopy_cmd" -j .text \ | |
6 | 6 | -j .sdata \ |
7 | 7 | -j .data \ |
8 | 8 | -j .dynamic \ |
9 | 9 | -j .dynsym \ |
10 | -j .rel \ | |
11 | -j .rela \ | |
12 | -j .reloc \ | |
13 | $* | |
10 | -j '.rel*' \ | |
11 | "$@" | |
14 | 12 | |
15 | 13 | if [ -n "${genpeimg_cmd}" ]; then |
16 | 14 | $genpeimg_cmd -d \ |
19 | 17 | +n \ |
20 | 18 | -d \ |
21 | 19 | +s \ |
22 | $output | |
20 | "$output" | |
23 | 21 | fi |
94 | 94 | efi_load_option *loadopt = NULL; |
95 | 95 | gchar *name = NULL; |
96 | 96 | gint rc; |
97 | gint set_entries[0x10000 / sizeof(gint)] = {0,}; | |
98 | 97 | gsize var_data_size = 0; |
99 | guint16 real_boot16; | |
100 | 98 | guint32 attr; |
101 | guint32 boot_next = 0x10000; | |
99 | guint16 boot_next = G_MAXUINT16; | |
102 | 100 | g_autofree guint8 *var_data = NULL; |
101 | g_autofree guint8 *set_entries = g_malloc0 (G_MAXUINT16); | |
103 | 102 | |
104 | 103 | while ((rc = efi_get_next_variable_name (&guid, &name)) > 0) { |
105 | 104 | const gchar *desc; |
106 | gint div, mod; | |
107 | 105 | gint scanned = 0; |
108 | 106 | guint16 entry = 0; |
109 | 107 | g_autofree guint8 *var_data_tmp = NULL; |
123 | 121 | if (scanned != 8) |
124 | 122 | continue; |
125 | 123 | |
126 | div = entry / (sizeof(set_entries[0]) * 8); | |
127 | mod = entry % (sizeof(set_entries[0]) * 8); | |
128 | ||
129 | set_entries[div] |= 1 << mod; | |
124 | /* mark this as used */ | |
125 | set_entries[entry] = 1; | |
130 | 126 | |
131 | 127 | rc = efi_get_variable (*guid, name, &var_data_tmp, &var_data_size, &attr); |
132 | 128 | if (rc < 0) { |
179 | 175 | /* create a new one */ |
180 | 176 | } else { |
181 | 177 | g_autofree gchar *boot_next_name = NULL; |
182 | for (guint32 value = 0; value < 0x10000; value++) { | |
183 | gint div = value / (sizeof(set_entries[0]) * 8); | |
184 | gint mod = value % (sizeof(set_entries[0]) * 8); | |
185 | if (set_entries[div] & (1 << mod)) | |
178 | for (guint16 value = 0; value < G_MAXUINT16; value++) { | |
179 | if (set_entries[value]) | |
186 | 180 | continue; |
187 | 181 | boot_next = value; |
188 | 182 | break; |
189 | 183 | } |
190 | if (boot_next >= 0x10000) { | |
184 | if (boot_next == G_MAXUINT16) { | |
191 | 185 | g_set_error (error, |
192 | 186 | G_IO_ERROR, |
193 | 187 | G_IO_ERROR_FAILED, |
195 | 189 | boot_next); |
196 | 190 | return FALSE; |
197 | 191 | } |
198 | boot_next_name = g_strdup_printf ("Boot%04X", | |
199 | (guint) (boot_next & 0xffff)); | |
192 | boot_next_name = g_strdup_printf ("Boot%04X", (guint) boot_next); | |
200 | 193 | rc = efi_set_variable (efi_guid_global, boot_next_name, opt, opt_size, |
201 | 194 | EFI_VARIABLE_NON_VOLATILE | |
202 | 195 | EFI_VARIABLE_BOOTSERVICE_ACCESS | |
217 | 210 | return FALSE; |
218 | 211 | |
219 | 212 | /* set the boot next */ |
220 | real_boot16 = boot_next; | |
221 | rc = efi_set_variable (efi_guid_global, "BootNext", (guint8 *)&real_boot16, 2, | |
213 | rc = efi_set_variable (efi_guid_global, "BootNext", (guint8 *)&boot_next, 2, | |
222 | 214 | EFI_VARIABLE_NON_VOLATILE | |
223 | 215 | EFI_VARIABLE_BOOTSERVICE_ACCESS | |
224 | 216 | EFI_VARIABLE_RUNTIME_ACCESS, |
228 | 220 | G_IO_ERROR, |
229 | 221 | G_IO_ERROR_FAILED, |
230 | 222 | "could not set BootNext(%" G_GUINT16_FORMAT ")", |
231 | real_boot16); | |
223 | boot_next); | |
232 | 224 | return FALSE; |
233 | 225 | } |
234 | 226 | return TRUE; |
241 | 241 | } |
242 | 242 | |
243 | 243 | gchar * |
244 | fu_uefi_get_esp_path_for_os (const gchar *esp_path) | |
245 | { | |
244 | fu_uefi_get_esp_path_for_os (const gchar *base) | |
245 | { | |
246 | #ifndef EFI_OS_DIR | |
246 | 247 | const gchar *os_release_id = NULL; |
247 | #ifndef EFI_OS_DIR | |
248 | const gchar *id_like_id; | |
249 | g_autofree gchar *esp_path = NULL; | |
248 | 250 | g_autoptr(GError) error_local = NULL; |
249 | 251 | g_autoptr(GHashTable) os_release = fwupd_get_os_release (&error_local); |
252 | /* try to lookup /etc/os-release ID key */ | |
250 | 253 | if (os_release != NULL) { |
251 | 254 | os_release_id = g_hash_table_lookup (os_release, "ID"); |
252 | 255 | } else { |
254 | 257 | } |
255 | 258 | if (os_release_id == NULL) |
256 | 259 | os_release_id = "unknown"; |
260 | /* if ID key points at something existing return it */ | |
261 | esp_path = g_build_filename (base, "EFI", os_release_id, NULL); | |
262 | if (g_file_test (esp_path, G_FILE_TEST_IS_DIR) || os_release == NULL) | |
263 | return g_steal_pointer (&esp_path); | |
264 | /* if ID key doesn't exist, try ID_LIKE */ | |
265 | id_like_id = g_hash_table_lookup (os_release, "ID_LIKE"); | |
266 | if (id_like_id != NULL) { | |
267 | g_autofree gchar* id_like_path = g_build_filename (base, "EFI", id_like_id, NULL); | |
268 | if (g_file_test (id_like_path, G_FILE_TEST_IS_DIR)) { | |
269 | g_debug ("Using ID_LIKE key from os-release"); | |
270 | return g_steal_pointer (&id_like_path); | |
271 | } | |
272 | } | |
273 | return g_steal_pointer (&esp_path); | |
257 | 274 | #else |
258 | os_release_id = EFI_OS_DIR; | |
275 | return g_build_filename (base, "EFI", EFI_OS_DIR, NULL); | |
259 | 276 | #endif |
260 | return g_build_filename (esp_path, "EFI", os_release_id, NULL); | |
261 | 277 | } |
262 | 278 | |
263 | 279 | guint64 |
0 | Unifying Support | |
1 | ================ | |
2 | ||
3 | Introduction | |
4 | ------------ | |
5 | ||
6 | This plugin can flash the firmware on Logitech Unifying dongles, both the | |
7 | Nordic (U0007) device and the Texas Instruments (U0008) version. | |
8 | ||
9 | This plugin will not work with the different "Nano" dongle (U0010) as it does | |
10 | not use the Unifying protocol. | |
11 | ||
12 | Some bootloader protocol information was taken from the Mousejack[1] project, | |
13 | specifically logitech-usb-restore.py and unifying.py. Other documentation was | |
14 | supplied by Logitech. | |
15 | ||
16 | Additional constants were taken from the Solaar[2] project. | |
17 | ||
18 | Firmware Format | |
19 | --------------- | |
20 | ||
21 | The daemon will decompress the cabinet archive and extract a firmware blob in | |
22 | a vendor-specific format that appears to be a subset of the Intel HEX format. | |
23 | ||
24 | This plugin supports the following protocol IDs: | |
25 | ||
26 | * com.logitech.unifying | |
27 | * com.logitech.unifyingsigned | |
28 | ||
29 | GUID Generation | |
30 | --------------- | |
31 | ||
32 | These devices use the standard USB DeviceInstanceId values when in DFU mode: | |
33 | ||
34 | * `USB\VID_046D&PID_AAAA&REV_0001` | |
35 | * `USB\VID_046D&PID_AAAA` | |
36 | * `USB\VID_046D` | |
37 | ||
38 | When in runtime mode, the HID raw DeviceInstanceId values are used: | |
39 | ||
40 | * `HIDRAW\VEN_046D&DEV_C52B` | |
41 | * `HIDRAW\VEN_046D` | |
42 | ||
43 | Design Notes | |
44 | ------------ | |
45 | ||
46 | When a dongle is detected in bootloader mode we detach the hidraw driver from | |
47 | the kernel and use raw control transfers. This ensures that we don't accidentally | |
48 | corrupt the uploading firmware. For application firmware we use hidraw which | |
49 | means the hardware keeps working while probing, and also allows us to detect | |
50 | paired devices. | |
51 | ||
52 | [1] https://www.mousejack.com/ | |
53 | [2] https://pwr-Solaar.github.io/Solaar/ |
0 | Bus 001 Device 036: ID 046d:aaaa Logitech, Inc. | |
1 | Device Descriptor: | |
2 | bLength 18 | |
3 | bDescriptorType 1 | |
4 | bcdUSB 2.00 | |
5 | bDeviceClass 0 | |
6 | bDeviceSubClass 0 | |
7 | bDeviceProtocol 0 | |
8 | bMaxPacketSize0 32 | |
9 | idVendor 0x046d Logitech, Inc. | |
10 | idProduct 0xaaaa | |
11 | bcdDevice 1.02 | |
12 | iManufacturer 1 | |
13 | iProduct 2 | |
14 | iSerial 0 | |
15 | bNumConfigurations 1 | |
16 | Configuration Descriptor: | |
17 | bLength 9 | |
18 | bDescriptorType 2 | |
19 | wTotalLength 34 | |
20 | bNumInterfaces 1 | |
21 | bConfigurationValue 1 | |
22 | iConfiguration 4 | |
23 | bmAttributes 0x80 | |
24 | (Bus Powered) | |
25 | MaxPower 98mA | |
26 | Interface Descriptor: | |
27 | bLength 9 | |
28 | bDescriptorType 4 | |
29 | bInterfaceNumber 0 | |
30 | bAlternateSetting 0 | |
31 | bNumEndpoints 1 | |
32 | bInterfaceClass 3 Human Interface Device | |
33 | bInterfaceSubClass 0 | |
34 | bInterfaceProtocol 0 | |
35 | iInterface 0 | |
36 | HID Device Descriptor: | |
37 | bLength 9 | |
38 | bDescriptorType 33 | |
39 | bcdHID 1.11 | |
40 | bCountryCode 0 Not supported | |
41 | bNumDescriptors 1 | |
42 | bDescriptorType 34 Report | |
43 | wDescriptorLength 25 | |
44 | Report Descriptors: | |
45 | ** UNAVAILABLE ** | |
46 | Endpoint Descriptor: | |
47 | bLength 7 | |
48 | bDescriptorType 5 | |
49 | bEndpointAddress 0x81 EP 1 IN | |
50 | bmAttributes 3 | |
51 | Transfer Type Interrupt | |
52 | Synch Type None | |
53 | Usage Type Data | |
54 | wMaxPacketSize 0x0020 1x 32 bytes | |
55 | bInterval 1 |
0 | Bus 001 Device 049: ID 046d:c52b Logitech, Inc. Unifying Receiver | |
1 | Device Descriptor: | |
2 | bLength 18 | |
3 | bDescriptorType 1 | |
4 | bcdUSB 2.00 | |
5 | bDeviceClass 0 | |
6 | bDeviceSubClass 0 | |
7 | bDeviceProtocol 0 | |
8 | bMaxPacketSize0 8 | |
9 | idVendor 0x046d Logitech, Inc. | |
10 | idProduct 0xc52b Unifying Receiver | |
11 | bcdDevice 12.07 | |
12 | iManufacturer 1 Logitech | |
13 | iProduct 2 USB Receiver | |
14 | iSerial 0 | |
15 | bNumConfigurations 1 | |
16 | Configuration Descriptor: | |
17 | bLength 9 | |
18 | bDescriptorType 2 | |
19 | wTotalLength 84 | |
20 | bNumInterfaces 3 | |
21 | bConfigurationValue 1 | |
22 | iConfiguration 4 RQR12.07_B0029 | |
23 | bmAttributes 0xa0 | |
24 | (Bus Powered) | |
25 | Remote Wakeup | |
26 | MaxPower 98mA | |
27 | Interface Descriptor: | |
28 | bLength 9 | |
29 | bDescriptorType 4 | |
30 | bInterfaceNumber 0 | |
31 | bAlternateSetting 0 | |
32 | bNumEndpoints 1 | |
33 | bInterfaceClass 3 Human Interface Device | |
34 | bInterfaceSubClass 1 Boot Interface Subclass | |
35 | bInterfaceProtocol 1 Keyboard | |
36 | iInterface 0 | |
37 | HID Device Descriptor: | |
38 | bLength 9 | |
39 | bDescriptorType 33 | |
40 | bcdHID 1.11 | |
41 | bCountryCode 0 Not supported | |
42 | bNumDescriptors 1 | |
43 | bDescriptorType 34 Report | |
44 | wDescriptorLength 59 | |
45 | Report Descriptor: (length is 59) | |
46 | Endpoint Descriptor: | |
47 | bLength 7 | |
48 | bDescriptorType 5 | |
49 | bEndpointAddress 0x81 EP 1 IN | |
50 | bmAttributes 3 | |
51 | Transfer Type Interrupt | |
52 | Synch Type None | |
53 | Usage Type Data | |
54 | wMaxPacketSize 0x0008 1x 8 bytes | |
55 | bInterval 8 | |
56 | Interface Descriptor: | |
57 | bLength 9 | |
58 | bDescriptorType 4 | |
59 | bInterfaceNumber 1 | |
60 | bAlternateSetting 0 | |
61 | bNumEndpoints 1 | |
62 | bInterfaceClass 3 Human Interface Device | |
63 | bInterfaceSubClass 1 Boot Interface Subclass | |
64 | bInterfaceProtocol 2 Mouse | |
65 | iInterface 0 | |
66 | HID Device Descriptor: | |
67 | bLength 9 | |
68 | bDescriptorType 33 | |
69 | bcdHID 1.11 | |
70 | bCountryCode 0 Not supported | |
71 | bNumDescriptors 1 | |
72 | bDescriptorType 34 Report | |
73 | wDescriptorLength 148 | |
74 | Report Descriptors: | |
75 | ** UNAVAILABLE ** | |
76 | Endpoint Descriptor: | |
77 | bLength 7 | |
78 | bDescriptorType 5 | |
79 | bEndpointAddress 0x82 EP 2 IN | |
80 | bmAttributes 3 | |
81 | Transfer Type Interrupt | |
82 | Synch Type None | |
83 | Usage Type Data | |
84 | wMaxPacketSize 0x0008 1x 8 bytes | |
85 | bInterval 2 | |
86 | Interface Descriptor: | |
87 | bLength 9 | |
88 | bDescriptorType 4 | |
89 | bInterfaceNumber 2 | |
90 | bAlternateSetting 0 | |
91 | bNumEndpoints 1 | |
92 | bInterfaceClass 3 Human Interface Device | |
93 | bInterfaceSubClass 0 | |
94 | bInterfaceProtocol 0 | |
95 | iInterface 0 | |
96 | HID Device Descriptor: | |
97 | bLength 9 | |
98 | bDescriptorType 33 | |
99 | bcdHID 1.11 | |
100 | bCountryCode 0 Not supported | |
101 | bNumDescriptors 1 | |
102 | bDescriptorType 34 Report | |
103 | wDescriptorLength 93 | |
104 | Report Descriptors: | |
105 | ** UNAVAILABLE ** | |
106 | Endpoint Descriptor: | |
107 | bLength 7 | |
108 | bDescriptorType 5 | |
109 | bEndpointAddress 0x83 EP 3 IN | |
110 | bmAttributes 3 | |
111 | Transfer Type Interrupt | |
112 | Synch Type None | |
113 | Usage Type Data | |
114 | wMaxPacketSize 0x0020 1x 32 bytes | |
115 | bInterval 2 | |
116 | Device Status: 0x0000 | |
117 | (Bus Powered) |
0 | ||
1 | Bus 003 Device 036: ID 046d:aaac Logitech, Inc. | |
2 | Device Descriptor: | |
3 | bLength 18 | |
4 | bDescriptorType 1 | |
5 | bcdUSB 2.00 | |
6 | bDeviceClass 0 | |
7 | bDeviceSubClass 0 | |
8 | bDeviceProtocol 0 | |
9 | bMaxPacketSize0 32 | |
10 | idVendor 0x046d Logitech, Inc. | |
11 | idProduct 0xaaac | |
12 | bcdDevice 3.01 | |
13 | iManufacturer 1 Logitech | |
14 | iProduct 2 USB BootLoader | |
15 | iSerial 0 | |
16 | bNumConfigurations 1 | |
17 | Configuration Descriptor: | |
18 | bLength 9 | |
19 | bDescriptorType 2 | |
20 | wTotalLength 34 | |
21 | bNumInterfaces 1 | |
22 | bConfigurationValue 1 | |
23 | iConfiguration 4 BOT03.01_B0008 | |
24 | bmAttributes 0x80 | |
25 | (Bus Powered) | |
26 | MaxPower 98mA | |
27 | Interface Descriptor: | |
28 | bLength 9 | |
29 | bDescriptorType 4 | |
30 | bInterfaceNumber 0 | |
31 | bAlternateSetting 0 | |
32 | bNumEndpoints 1 | |
33 | bInterfaceClass 3 Human Interface Device | |
34 | bInterfaceSubClass 0 | |
35 | bInterfaceProtocol 0 | |
36 | iInterface 0 | |
37 | HID Device Descriptor: | |
38 | bLength 9 | |
39 | bDescriptorType 33 | |
40 | bcdHID 1.11 | |
41 | bCountryCode 0 Not supported | |
42 | bNumDescriptors 1 | |
43 | bDescriptorType 34 Report | |
44 | wDescriptorLength 25 | |
45 | Report Descriptor: (length is 25) | |
46 | Item(Global): Usage Page, data= [ 0xb0 0xff ] 65456 | |
47 | (null) | |
48 | Item(Local ): Usage, data= [ 0x01 ] 1 | |
49 | (null) | |
50 | Item(Main ): Collection, data= [ 0x01 ] 1 | |
51 | Application | |
52 | Item(Global): Report Size, data= [ 0x08 ] 8 | |
53 | Item(Global): Report Count, data= [ 0x20 ] 32 | |
54 | Item(Global): Logical Minimum, data= [ 0x00 ] 0 | |
55 | Item(Global): Logical Maximum, data= [ 0xff 0x00 ] 255 | |
56 | Item(Local ): Usage, data= [ 0x01 ] 1 | |
57 | (null) | |
58 | Item(Main ): Input, data= [ 0x00 ] 0 | |
59 | Data Array Absolute No_Wrap Linear | |
60 | Preferred_State No_Null_Position Non_Volatile Bitfield | |
61 | Item(Local ): Usage, data= [ 0x01 ] 1 | |
62 | (null) | |
63 | Item(Main ): Output, data= [ 0x00 ] 0 | |
64 | Data Array Absolute No_Wrap Linear | |
65 | Preferred_State No_Null_Position Non_Volatile Bitfield | |
66 | Item(Main ): End Collection, data=none | |
67 | Endpoint Descriptor: | |
68 | bLength 7 | |
69 | bDescriptorType 5 | |
70 | bEndpointAddress 0x81 EP 1 IN | |
71 | bmAttributes 3 | |
72 | Transfer Type Interrupt | |
73 | Synch Type None | |
74 | Usage Type Data | |
75 | wMaxPacketSize 0x0020 1x 32 bytes | |
76 | bInterval 1 | |
77 | Device Status: 0x0000 | |
78 | (Bus Powered) |
0 | ||
1 | Bus 003 Device 039: ID 046d:aaac Logitech, Inc. | |
2 | Device Descriptor: | |
3 | bLength 18 | |
4 | bDescriptorType 1 | |
5 | bcdUSB 2.00 | |
6 | bDeviceClass 0 | |
7 | bDeviceSubClass 0 | |
8 | bDeviceProtocol 0 | |
9 | bMaxPacketSize0 32 | |
10 | idVendor 0x046d Logitech, Inc. | |
11 | idProduct 0xaaac | |
12 | bcdDevice 3.00 | |
13 | iManufacturer 1 Logitech | |
14 | iProduct 2 USB BootLoader | |
15 | iSerial 0 | |
16 | bNumConfigurations 1 | |
17 | Configuration Descriptor: | |
18 | bLength 9 | |
19 | bDescriptorType 2 | |
20 | wTotalLength 34 | |
21 | bNumInterfaces 1 | |
22 | bConfigurationValue 1 | |
23 | iConfiguration 4 BOT03.00_B0006 | |
24 | bmAttributes 0x80 | |
25 | (Bus Powered) | |
26 | MaxPower 98mA | |
27 | Interface Descriptor: | |
28 | bLength 9 | |
29 | bDescriptorType 4 | |
30 | bInterfaceNumber 0 | |
31 | bAlternateSetting 0 | |
32 | bNumEndpoints 1 | |
33 | bInterfaceClass 3 Human Interface Device | |
34 | bInterfaceSubClass 0 | |
35 | bInterfaceProtocol 0 | |
36 | iInterface 0 | |
37 | HID Device Descriptor: | |
38 | bLength 9 | |
39 | bDescriptorType 33 | |
40 | bcdHID 1.11 | |
41 | bCountryCode 0 Not supported | |
42 | bNumDescriptors 1 | |
43 | bDescriptorType 34 Report | |
44 | wDescriptorLength 25 | |
45 | Report Descriptor: (length is 25) | |
46 | Item(Global): Usage Page, data= [ 0xb0 0xff ] 65456 | |
47 | (null) | |
48 | Item(Local ): Usage, data= [ 0x01 ] 1 | |
49 | (null) | |
50 | Item(Main ): Collection, data= [ 0x01 ] 1 | |
51 | Application | |
52 | Item(Global): Report Size, data= [ 0x08 ] 8 | |
53 | Item(Global): Report Count, data= [ 0x20 ] 32 | |
54 | Item(Global): Logical Minimum, data= [ 0x00 ] 0 | |
55 | Item(Global): Logical Maximum, data= [ 0xff 0x00 ] 255 | |
56 | Item(Local ): Usage, data= [ 0x01 ] 1 | |
57 | (null) | |
58 | Item(Main ): Input, data= [ 0x00 ] 0 | |
59 | Data Array Absolute No_Wrap Linear | |
60 | Preferred_State No_Null_Position Non_Volatile Bitfield | |
61 | Item(Local ): Usage, data= [ 0x01 ] 1 | |
62 | (null) | |
63 | Item(Main ): Output, data= [ 0x00 ] 0 | |
64 | Data Array Absolute No_Wrap Linear | |
65 | Preferred_State No_Null_Position Non_Volatile Bitfield | |
66 | Item(Main ): End Collection, data=none | |
67 | Endpoint Descriptor: | |
68 | bLength 7 | |
69 | bDescriptorType 5 | |
70 | bEndpointAddress 0x81 EP 1 IN | |
71 | bmAttributes 3 | |
72 | Transfer Type Interrupt | |
73 | Synch Type None | |
74 | Usage Type Data | |
75 | wMaxPacketSize 0x0020 1x 32 bytes | |
76 | bInterval 1 | |
77 | Device Status: 0x0000 | |
78 | (Bus Powered) |
0 | ||
1 | Bus 003 Device 033: ID 046d:c52b Logitech, Inc. Unifying Receiver | |
2 | Device Descriptor: | |
3 | bLength 18 | |
4 | bDescriptorType 1 | |
5 | bcdUSB 2.00 | |
6 | bDeviceClass 0 | |
7 | bDeviceSubClass 0 | |
8 | bDeviceProtocol 0 | |
9 | bMaxPacketSize0 32 | |
10 | idVendor 0x046d Logitech, Inc. | |
11 | idProduct 0xc52b Unifying Receiver | |
12 | bcdDevice 24.01 | |
13 | iManufacturer 1 Logitech | |
14 | iProduct 2 USB Receiver | |
15 | iSerial 0 | |
16 | bNumConfigurations 1 | |
17 | Configuration Descriptor: | |
18 | bLength 9 | |
19 | bDescriptorType 2 | |
20 | wTotalLength 84 | |
21 | bNumInterfaces 3 | |
22 | bConfigurationValue 1 | |
23 | iConfiguration 4 RQR24.01_B0023 | |
24 | bmAttributes 0xa0 | |
25 | (Bus Powered) | |
26 | Remote Wakeup | |
27 | MaxPower 98mA | |
28 | Interface Descriptor: | |
29 | bLength 9 | |
30 | bDescriptorType 4 | |
31 | bInterfaceNumber 0 | |
32 | bAlternateSetting 0 | |
33 | bNumEndpoints 1 | |
34 | bInterfaceClass 3 Human Interface Device | |
35 | bInterfaceSubClass 1 Boot Interface Subclass | |
36 | bInterfaceProtocol 1 Keyboard | |
37 | iInterface 0 | |
38 | HID Device Descriptor: | |
39 | bLength 9 | |
40 | bDescriptorType 33 | |
41 | bcdHID 1.11 | |
42 | bCountryCode 0 Not supported | |
43 | bNumDescriptors 1 | |
44 | bDescriptorType 34 Report | |
45 | wDescriptorLength 59 | |
46 | Report Descriptor: (length is 59) | |
47 | Item(Global): Usage Page, data= [ 0x01 ] 1 | |
48 | Generic Desktop Controls | |
49 | Item(Local ): Usage, data= [ 0x06 ] 6 | |
50 | Keyboard | |
51 | Item(Main ): Collection, data= [ 0x01 ] 1 | |
52 | Application | |
53 | Item(Global): Usage Page, data= [ 0x07 ] 7 | |
54 | Keyboard | |
55 | Item(Local ): Usage Minimum, data= [ 0xe0 ] 224 | |
56 | Control Left | |
57 | Item(Local ): Usage Maximum, data= [ 0xe7 ] 231 | |
58 | GUI Right | |
59 | Item(Global): Logical Minimum, data= [ 0x00 ] 0 | |
60 | Item(Global): Logical Maximum, data= [ 0x01 ] 1 | |
61 | Item(Global): Report Size, data= [ 0x01 ] 1 | |
62 | Item(Global): Report Count, data= [ 0x08 ] 8 | |
63 | Item(Main ): Input, data= [ 0x02 ] 2 | |
64 | Data Variable Absolute No_Wrap Linear | |
65 | Preferred_State No_Null_Position Non_Volatile Bitfield | |
66 | Item(Main ): Input, data= [ 0x03 ] 3 | |
67 | Constant Variable Absolute No_Wrap Linear | |
68 | Preferred_State No_Null_Position Non_Volatile Bitfield | |
69 | Item(Global): Report Count, data= [ 0x05 ] 5 | |
70 | Item(Global): Usage Page, data= [ 0x08 ] 8 | |
71 | LEDs | |
72 | Item(Local ): Usage Minimum, data= [ 0x01 ] 1 | |
73 | NumLock | |
74 | Item(Local ): Usage Maximum, data= [ 0x05 ] 5 | |
75 | Kana | |
76 | Item(Main ): Output, data= [ 0x02 ] 2 | |
77 | Data Variable Absolute No_Wrap Linear | |
78 | Preferred_State No_Null_Position Non_Volatile Bitfield | |
79 | Item(Global): Report Count, data= [ 0x01 ] 1 | |
80 | Item(Global): Report Size, data= [ 0x03 ] 3 | |
81 | Item(Main ): Output, data= [ 0x01 ] 1 | |
82 | Constant Array Absolute No_Wrap Linear | |
83 | Preferred_State No_Null_Position Non_Volatile Bitfield | |
84 | Item(Global): Report Count, data= [ 0x06 ] 6 | |
85 | Item(Global): Report Size, data= [ 0x08 ] 8 | |
86 | Item(Global): Logical Minimum, data= [ 0x00 ] 0 | |
87 | Item(Global): Logical Maximum, data= [ 0xa4 0x00 ] 164 | |
88 | Item(Global): Usage Page, data= [ 0x07 ] 7 | |
89 | Keyboard | |
90 | Item(Local ): Usage Minimum, data= [ 0x00 ] 0 | |
91 | No Event | |
92 | Item(Local ): Usage Maximum, data= [ 0xa4 0x00 ] 164 | |
93 | ExSel | |
94 | Item(Main ): Input, data= [ 0x00 ] 0 | |
95 | Data Array Absolute No_Wrap Linear | |
96 | Preferred_State No_Null_Position Non_Volatile Bitfield | |
97 | Item(Main ): End Collection, data=none | |
98 | Endpoint Descriptor: | |
99 | bLength 7 | |
100 | bDescriptorType 5 | |
101 | bEndpointAddress 0x81 EP 1 IN | |
102 | bmAttributes 3 | |
103 | Transfer Type Interrupt | |
104 | Synch Type None | |
105 | Usage Type Data | |
106 | wMaxPacketSize 0x0008 1x 8 bytes | |
107 | bInterval 8 | |
108 | Interface Descriptor: | |
109 | bLength 9 | |
110 | bDescriptorType 4 | |
111 | bInterfaceNumber 1 | |
112 | bAlternateSetting 0 | |
113 | bNumEndpoints 1 | |
114 | bInterfaceClass 3 Human Interface Device | |
115 | bInterfaceSubClass 1 Boot Interface Subclass | |
116 | bInterfaceProtocol 2 Mouse | |
117 | iInterface 0 | |
118 | HID Device Descriptor: | |
119 | bLength 9 | |
120 | bDescriptorType 33 | |
121 | bcdHID 1.11 | |
122 | bCountryCode 0 Not supported | |
123 | bNumDescriptors 1 | |
124 | bDescriptorType 34 Report | |
125 | wDescriptorLength 148 | |
126 | Report Descriptor: (length is 148) | |
127 | Item(Global): Usage Page, data= [ 0x01 ] 1 | |
128 | Generic Desktop Controls | |
129 | Item(Local ): Usage, data= [ 0x02 ] 2 | |
130 | Mouse | |
131 | Item(Main ): Collection, data= [ 0x01 ] 1 | |
132 | Application | |
133 | Item(Global): Report ID, data= [ 0x02 ] 2 | |
134 | Item(Local ): Usage, data= [ 0x01 ] 1 | |
135 | Pointer | |
136 | Item(Main ): Collection, data= [ 0x00 ] 0 | |
137 | Physical | |
138 | Item(Global): Usage Page, data= [ 0x09 ] 9 | |
139 | Buttons | |
140 | Item(Local ): Usage Minimum, data= [ 0x01 ] 1 | |
141 | Button 1 (Primary) | |
142 | Item(Local ): Usage Maximum, data= [ 0x10 ] 16 | |
143 | (null) | |
144 | Item(Global): Logical Minimum, data= [ 0x00 ] 0 | |
145 | Item(Global): Logical Maximum, data= [ 0x01 ] 1 | |
146 | Item(Global): Report Count, data= [ 0x10 ] 16 | |
147 | Item(Global): Report Size, data= [ 0x01 ] 1 | |
148 | Item(Main ): Input, data= [ 0x02 ] 2 | |
149 | Data Variable Absolute No_Wrap Linear | |
150 | Preferred_State No_Null_Position Non_Volatile Bitfield | |
151 | Item(Global): Usage Page, data= [ 0x01 ] 1 | |
152 | Generic Desktop Controls | |
153 | Item(Global): Logical Minimum, data= [ 0x01 0xf8 ] 63489 | |
154 | Item(Global): Logical Maximum, data= [ 0xff 0x07 ] 2047 | |
155 | Item(Global): Report Size, data= [ 0x0c ] 12 | |
156 | Item(Global): Report Count, data= [ 0x02 ] 2 | |
157 | Item(Local ): Usage, data= [ 0x30 ] 48 | |
158 | Direction-X | |
159 | Item(Local ): Usage, data= [ 0x31 ] 49 | |
160 | Direction-Y | |
161 | Item(Main ): Input, data= [ 0x06 ] 6 | |
162 | Data Variable Relative No_Wrap Linear | |
163 | Preferred_State No_Null_Position Non_Volatile Bitfield | |
164 | Item(Global): Logical Minimum, data= [ 0x81 ] 129 | |
165 | Item(Global): Logical Maximum, data= [ 0x7f ] 127 | |
166 | Item(Global): Report Size, data= [ 0x08 ] 8 | |
167 | Item(Global): Report Count, data= [ 0x01 ] 1 | |
168 | Item(Local ): Usage, data= [ 0x38 ] 56 | |
169 | Wheel | |
170 | Item(Main ): Input, data= [ 0x06 ] 6 | |
171 | Data Variable Relative No_Wrap Linear | |
172 | Preferred_State No_Null_Position Non_Volatile Bitfield | |
173 | Item(Global): Usage Page, data= [ 0x0c ] 12 | |
174 | Consumer | |
175 | Item(Local ): Usage, data= [ 0x38 0x02 ] 568 | |
176 | AC Pan | |
177 | Item(Global): Report Count, data= [ 0x01 ] 1 | |
178 | Item(Main ): Input, data= [ 0x06 ] 6 | |
179 | Data Variable Relative No_Wrap Linear | |
180 | Preferred_State No_Null_Position Non_Volatile Bitfield | |
181 | Item(Main ): End Collection, data=none | |
182 | Item(Main ): End Collection, data=none | |
183 | Item(Global): Usage Page, data= [ 0x0c ] 12 | |
184 | Consumer | |
185 | Item(Local ): Usage, data= [ 0x01 ] 1 | |
186 | Consumer Control | |
187 | Item(Main ): Collection, data= [ 0x01 ] 1 | |
188 | Application | |
189 | Item(Global): Report ID, data= [ 0x03 ] 3 | |
190 | Item(Global): Report Size, data= [ 0x10 ] 16 | |
191 | Item(Global): Report Count, data= [ 0x02 ] 2 | |
192 | Item(Global): Logical Minimum, data= [ 0x01 ] 1 | |
193 | Item(Global): Logical Maximum, data= [ 0x8c 0x02 ] 652 | |
194 | Item(Local ): Usage Minimum, data= [ 0x01 ] 1 | |
195 | Consumer Control | |
196 | Item(Local ): Usage Maximum, data= [ 0x8c 0x02 ] 652 | |
197 | (null) | |
198 | Item(Main ): Input, data= [ 0x00 ] 0 | |
199 | Data Array Absolute No_Wrap Linear | |
200 | Preferred_State No_Null_Position Non_Volatile Bitfield | |
201 | Item(Main ): End Collection, data=none | |
202 | Item(Global): Usage Page, data= [ 0x01 ] 1 | |
203 | Generic Desktop Controls | |
204 | Item(Local ): Usage, data= [ 0x80 ] 128 | |
205 | System Control | |
206 | Item(Main ): Collection, data= [ 0x01 ] 1 | |
207 | Application | |
208 | Item(Global): Report ID, data= [ 0x04 ] 4 | |
209 | Item(Global): Report Size, data= [ 0x02 ] 2 | |
210 | Item(Global): Report Count, data= [ 0x01 ] 1 | |
211 | Item(Global): Logical Minimum, data= [ 0x01 ] 1 | |
212 | Item(Global): Logical Maximum, data= [ 0x03 ] 3 | |
213 | Item(Local ): Usage, data= [ 0x82 ] 130 | |
214 | System Sleep | |
215 | Item(Local ): Usage, data= [ 0x81 ] 129 | |
216 | System Power Down | |
217 | Item(Local ): Usage, data= [ 0x83 ] 131 | |
218 | System Wake Up | |
219 | Item(Main ): Input, data= [ 0x60 ] 96 | |
220 | Data Array Absolute No_Wrap Linear | |
221 | No_Preferred_State Null_State Non_Volatile Bitfield | |
222 | Item(Global): Report Size, data= [ 0x06 ] 6 | |
223 | Item(Main ): Input, data= [ 0x03 ] 3 | |
224 | Constant Variable Absolute No_Wrap Linear | |
225 | Preferred_State No_Null_Position Non_Volatile Bitfield | |
226 | Item(Main ): End Collection, data=none | |
227 | Item(Global): Usage Page, data= [ 0xbc 0xff ] 65468 | |
228 | (null) | |
229 | Item(Local ): Usage, data= [ 0x88 ] 136 | |
230 | (null) | |
231 | Item(Main ): Collection, data= [ 0x01 ] 1 | |
232 | Application | |
233 | Item(Global): Report ID, data= [ 0x08 ] 8 | |
234 | Item(Local ): Usage Minimum, data= [ 0x01 ] 1 | |
235 | (null) | |
236 | Item(Local ): Usage Maximum, data= [ 0xff ] 255 | |
237 | (null) | |
238 | Item(Global): Logical Minimum, data= [ 0x01 ] 1 | |
239 | Item(Global): Logical Maximum, data= [ 0xff 0x00 ] 255 | |
240 | Item(Global): Report Size, data= [ 0x08 ] 8 | |
241 | Item(Global): Report Count, data= [ 0x01 ] 1 | |
242 | Item(Main ): Input, data= [ 0x00 ] 0 | |
243 | Data Array Absolute No_Wrap Linear | |
244 | Preferred_State No_Null_Position Non_Volatile Bitfield | |
245 | Item(Main ): End Collection, data=none | |
246 | Endpoint Descriptor: | |
247 | bLength 7 | |
248 | bDescriptorType 5 | |
249 | bEndpointAddress 0x82 EP 2 IN | |
250 | bmAttributes 3 | |
251 | Transfer Type Interrupt | |
252 | Synch Type None | |
253 | Usage Type Data | |
254 | wMaxPacketSize 0x0008 1x 8 bytes | |
255 | bInterval 2 | |
256 | Interface Descriptor: | |
257 | bLength 9 | |
258 | bDescriptorType 4 | |
259 | bInterfaceNumber 2 | |
260 | bAlternateSetting 0 | |
261 | bNumEndpoints 1 | |
262 | bInterfaceClass 3 Human Interface Device | |
263 | bInterfaceSubClass 0 | |
264 | bInterfaceProtocol 0 | |
265 | iInterface 0 | |
266 | HID Device Descriptor: | |
267 | bLength 9 | |
268 | bDescriptorType 33 | |
269 | bcdHID 1.11 | |
270 | bCountryCode 0 Not supported | |
271 | bNumDescriptors 1 | |
272 | bDescriptorType 34 Report | |
273 | wDescriptorLength 98 | |
274 | Report Descriptor: (length is 98) | |
275 | Item(Global): Usage Page, data= [ 0x00 0xff ] 65280 | |
276 | (null) | |
277 | Item(Local ): Usage, data= [ 0x01 ] 1 | |
278 | (null) | |
279 | Item(Main ): Collection, data= [ 0x01 ] 1 | |
280 | Application | |
281 | Item(Global): Report ID, data= [ 0x10 ] 16 | |
282 | Item(Global): Report Size, data= [ 0x08 ] 8 | |
283 | Item(Global): Report Count, data= [ 0x06 ] 6 | |
284 | Item(Global): Logical Minimum, data= [ 0x00 ] 0 | |
285 | Item(Global): Logical Maximum, data= [ 0xff 0x00 ] 255 | |
286 | Item(Local ): Usage, data= [ 0x01 ] 1 | |
287 | (null) | |
288 | Item(Main ): Input, data= [ 0x00 ] 0 | |
289 | Data Array Absolute No_Wrap Linear | |
290 | Preferred_State No_Null_Position Non_Volatile Bitfield | |
291 | Item(Local ): Usage, data= [ 0x01 ] 1 | |
292 | (null) | |
293 | Item(Main ): Output, data= [ 0x00 ] 0 | |
294 | Data Array Absolute No_Wrap Linear | |
295 | Preferred_State No_Null_Position Non_Volatile Bitfield | |
296 | Item(Main ): End Collection, data=none | |
297 | Item(Global): Usage Page, data= [ 0x00 0xff ] 65280 | |
298 | (null) | |
299 | Item(Local ): Usage, data= [ 0x02 ] 2 | |
300 | (null) | |
301 | Item(Main ): Collection, data= [ 0x01 ] 1 | |
302 | Application | |
303 | Item(Global): Report ID, data= [ 0x11 ] 17 | |
304 | Item(Global): Report Size, data= [ 0x08 ] 8 | |
305 | Item(Global): Report Count, data= [ 0x13 ] 19 | |
306 | Item(Global): Logical Minimum, data= [ 0x00 ] 0 | |
307 | Item(Global): Logical Maximum, data= [ 0xff 0x00 ] 255 | |
308 | Item(Local ): Usage, data= [ 0x02 ] 2 | |
309 | (null) | |
310 | Item(Main ): Input, data= [ 0x00 ] 0 | |
311 | Data Array Absolute No_Wrap Linear | |
312 | Preferred_State No_Null_Position Non_Volatile Bitfield | |
313 | Item(Local ): Usage, data= [ 0x02 ] 2 | |
314 | (null) | |
315 | Item(Main ): Output, data= [ 0x00 ] 0 | |
316 | Data Array Absolute No_Wrap Linear | |
317 | Preferred_State No_Null_Position Non_Volatile Bitfield | |
318 | Item(Main ): End Collection, data=none | |
319 | Item(Global): Usage Page, data= [ 0x00 0xff ] 65280 | |
320 | (null) | |
321 | Item(Local ): Usage, data= [ 0x04 ] 4 | |
322 | (null) | |
323 | Item(Main ): Collection, data= [ 0x01 ] 1 | |
324 | Application | |
325 | Item(Global): Report ID, data= [ 0x20 ] 32 | |
326 | Item(Global): Report Size, data= [ 0x08 ] 8 | |
327 | Item(Global): Report Count, data= [ 0x0e ] 14 | |
328 | Item(Global): Logical Minimum, data= [ 0x00 ] 0 | |
329 | Item(Global): Logical Maximum, data= [ 0xff 0x00 ] 255 | |
330 | Item(Local ): Usage, data= [ 0x41 ] 65 | |
331 | (null) | |
332 | Item(Main ): Input, data= [ 0x00 ] 0 | |
333 | Data Array Absolute No_Wrap Linear | |
334 | Preferred_State No_Null_Position Non_Volatile Bitfield | |
335 | Item(Local ): Usage, data= [ 0x41 ] 65 | |
336 | (null) | |
337 | Item(Main ): Output, data= [ 0x00 ] 0 | |
338 | Data Array Absolute No_Wrap Linear | |
339 | Preferred_State No_Null_Position Non_Volatile Bitfield | |
340 | Item(Global): Report ID, data= [ 0x21 ] 33 | |
341 | Item(Global): Report Count, data= [ 0x1f ] 31 | |
342 | Item(Global): Logical Minimum, data= [ 0x00 ] 0 | |
343 | Item(Global): Logical Maximum, data= [ 0xff 0x00 ] 255 | |
344 | Item(Local ): Usage, data= [ 0x42 ] 66 | |
345 | (null) | |
346 | Item(Main ): Input, data= [ 0x00 ] 0 | |
347 | Data Array Absolute No_Wrap Linear | |
348 | Preferred_State No_Null_Position Non_Volatile Bitfield | |
349 | Item(Local ): Usage, data= [ 0x42 ] 66 | |
350 | (null) | |
351 | Item(Main ): Output, data= [ 0x00 ] 0 | |
352 | Data Array Absolute No_Wrap Linear | |
353 | Preferred_State No_Null_Position Non_Volatile Bitfield | |
354 | Item(Main ): End Collection, data=none | |
355 | Endpoint Descriptor: | |
356 | bLength 7 | |
357 | bDescriptorType 5 | |
358 | bEndpointAddress 0x83 EP 3 IN | |
359 | bmAttributes 3 | |
360 | Transfer Type Interrupt | |
361 | Synch Type None | |
362 | Usage Type Data | |
363 | wMaxPacketSize 0x0020 1x 32 bytes | |
364 | bInterval 2 | |
365 | Device Status: 0x0000 | |
366 | (Bus Powered) |
0 | Bus 003 Device 032: ID 046d:c52b Logitech, Inc. Unifying Receiver | |
1 | Device Descriptor: | |
2 | bLength 18 | |
3 | bDescriptorType 1 | |
4 | bcdUSB 2.00 | |
5 | bDeviceClass 0 | |
6 | bDeviceSubClass 0 | |
7 | bDeviceProtocol 0 | |
8 | bMaxPacketSize0 32 | |
9 | idVendor 0x046d Logitech, Inc. | |
10 | idProduct 0xc52b Unifying Receiver | |
11 | bcdDevice 24.05 | |
12 | iManufacturer 1 Logitech | |
13 | iProduct 2 USB Receiver | |
14 | iSerial 0 | |
15 | bNumConfigurations 1 | |
16 | Configuration Descriptor: | |
17 | bLength 9 | |
18 | bDescriptorType 2 | |
19 | wTotalLength 84 | |
20 | bNumInterfaces 3 | |
21 | bConfigurationValue 1 | |
22 | iConfiguration 4 RQR24.05_B0029 | |
23 | bmAttributes 0xa0 | |
24 | (Bus Powered) | |
25 | Remote Wakeup | |
26 | MaxPower 98mA | |
27 | Interface Descriptor: | |
28 | bLength 9 | |
29 | bDescriptorType 4 | |
30 | bInterfaceNumber 0 | |
31 | bAlternateSetting 0 | |
32 | bNumEndpoints 1 | |
33 | bInterfaceClass 3 Human Interface Device | |
34 | bInterfaceSubClass 1 Boot Interface Subclass | |
35 | bInterfaceProtocol 1 Keyboard | |
36 | iInterface 0 | |
37 | HID Device Descriptor: | |
38 | bLength 9 | |
39 | bDescriptorType 33 | |
40 | bcdHID 1.11 | |
41 | bCountryCode 0 Not supported | |
42 | bNumDescriptors 1 | |
43 | bDescriptorType 34 Report | |
44 | wDescriptorLength 59 | |
45 | Report Descriptor: (length is 59) | |
46 | Item(Global): Usage Page, data= [ 0x01 ] 1 | |
47 | Generic Desktop Controls | |
48 | Item(Local ): Usage, data= [ 0x06 ] 6 | |
49 | Keyboard | |
50 | Item(Main ): Collection, data= [ 0x01 ] 1 | |
51 | Application | |
52 | Item(Global): Usage Page, data= [ 0x07 ] 7 | |
53 | Keyboard | |
54 | Item(Local ): Usage Minimum, data= [ 0xe0 ] 224 | |
55 | Control Left | |
56 | Item(Local ): Usage Maximum, data= [ 0xe7 ] 231 | |
57 | GUI Right | |
58 | Item(Global): Logical Minimum, data= [ 0x00 ] 0 | |
59 | Item(Global): Logical Maximum, data= [ 0x01 ] 1 | |
60 | Item(Global): Report Size, data= [ 0x01 ] 1 | |
61 | Item(Global): Report Count, data= [ 0x08 ] 8 | |
62 | Item(Main ): Input, data= [ 0x02 ] 2 | |
63 | Data Variable Absolute No_Wrap Linear | |
64 | Preferred_State No_Null_Position Non_Volatile Bitfield | |
65 | Item(Main ): Input, data= [ 0x03 ] 3 | |
66 | Constant Variable Absolute No_Wrap Linear | |
67 | Preferred_State No_Null_Position Non_Volatile Bitfield | |
68 | Item(Global): Report Count, data= [ 0x05 ] 5 | |
69 | Item(Global): Usage Page, data= [ 0x08 ] 8 | |
70 | LEDs | |
71 | Item(Local ): Usage Minimum, data= [ 0x01 ] 1 | |
72 | NumLock | |
73 | Item(Local ): Usage Maximum, data= [ 0x05 ] 5 | |
74 | Kana | |
75 | Item(Main ): Output, data= [ 0x02 ] 2 | |
76 | Data Variable Absolute No_Wrap Linear | |
77 | Preferred_State No_Null_Position Non_Volatile Bitfield | |
78 | Item(Global): Report Count, data= [ 0x01 ] 1 | |
79 | Item(Global): Report Size, data= [ 0x03 ] 3 | |
80 | Item(Main ): Output, data= [ 0x01 ] 1 | |
81 | Constant Array Absolute No_Wrap Linear | |
82 | Preferred_State No_Null_Position Non_Volatile Bitfield | |
83 | Item(Global): Report Count, data= [ 0x06 ] 6 | |
84 | Item(Global): Report Size, data= [ 0x08 ] 8 | |
85 | Item(Global): Logical Minimum, data= [ 0x00 ] 0 | |
86 | Item(Global): Logical Maximum, data= [ 0xa4 0x00 ] 164 | |
87 | Item(Global): Usage Page, data= [ 0x07 ] 7 | |
88 | Keyboard | |
89 | Item(Local ): Usage Minimum, data= [ 0x00 ] 0 | |
90 | No Event | |
91 | Item(Local ): Usage Maximum, data= [ 0xa4 0x00 ] 164 | |
92 | ExSel | |
93 | Item(Main ): Input, data= [ 0x00 ] 0 | |
94 | Data Array Absolute No_Wrap Linear | |
95 | Preferred_State No_Null_Position Non_Volatile Bitfield | |
96 | Item(Main ): End Collection, data=none | |
97 | Endpoint Descriptor: | |
98 | bLength 7 | |
99 | bDescriptorType 5 | |
100 | bEndpointAddress 0x81 EP 1 IN | |
101 | bmAttributes 3 | |
102 | Transfer Type Interrupt | |
103 | Synch Type None | |
104 | Usage Type Data | |
105 | wMaxPacketSize 0x0008 1x 8 bytes | |
106 | bInterval 8 | |
107 | Interface Descriptor: | |
108 | bLength 9 | |
109 | bDescriptorType 4 | |
110 | bInterfaceNumber 1 | |
111 | bAlternateSetting 0 | |
112 | bNumEndpoints 1 | |
113 | bInterfaceClass 3 Human Interface Device | |
114 | bInterfaceSubClass 1 Boot Interface Subclass | |
115 | bInterfaceProtocol 2 Mouse | |
116 | iInterface 0 | |
117 | HID Device Descriptor: | |
118 | bLength 9 | |
119 | bDescriptorType 33 | |
120 | bcdHID 1.11 | |
121 | bCountryCode 0 Not supported | |
122 | bNumDescriptors 1 | |
123 | bDescriptorType 34 Report | |
124 | wDescriptorLength 148 | |
125 | Report Descriptor: (length is 148) | |
126 | Item(Global): Usage Page, data= [ 0x01 ] 1 | |
127 | Generic Desktop Controls | |
128 | Item(Local ): Usage, data= [ 0x02 ] 2 | |
129 | Mouse | |
130 | Item(Main ): Collection, data= [ 0x01 ] 1 | |
131 | Application | |
132 | Item(Global): Report ID, data= [ 0x02 ] 2 | |
133 | Item(Local ): Usage, data= [ 0x01 ] 1 | |
134 | Pointer | |
135 | Item(Main ): Collection, data= [ 0x00 ] 0 | |
136 | Physical | |
137 | Item(Global): Usage Page, data= [ 0x09 ] 9 | |
138 | Buttons | |
139 | Item(Local ): Usage Minimum, data= [ 0x01 ] 1 | |
140 | Button 1 (Primary) | |
141 | Item(Local ): Usage Maximum, data= [ 0x10 ] 16 | |
142 | (null) | |
143 | Item(Global): Logical Minimum, data= [ 0x00 ] 0 | |
144 | Item(Global): Logical Maximum, data= [ 0x01 ] 1 | |
145 | Item(Global): Report Count, data= [ 0x10 ] 16 | |
146 | Item(Global): Report Size, data= [ 0x01 ] 1 | |
147 | Item(Main ): Input, data= [ 0x02 ] 2 | |
148 | Data Variable Absolute No_Wrap Linear | |
149 | Preferred_State No_Null_Position Non_Volatile Bitfield | |
150 | Item(Global): Usage Page, data= [ 0x01 ] 1 | |
151 | Generic Desktop Controls | |
152 | Item(Global): Logical Minimum, data= [ 0x01 0xf8 ] 63489 | |
153 | Item(Global): Logical Maximum, data= [ 0xff 0x07 ] 2047 | |
154 | Item(Global): Report Size, data= [ 0x0c ] 12 | |
155 | Item(Global): Report Count, data= [ 0x02 ] 2 | |
156 | Item(Local ): Usage, data= [ 0x30 ] 48 | |
157 | Direction-X | |
158 | Item(Local ): Usage, data= [ 0x31 ] 49 | |
159 | Direction-Y | |
160 | Item(Main ): Input, data= [ 0x06 ] 6 | |
161 | Data Variable Relative No_Wrap Linear | |
162 | Preferred_State No_Null_Position Non_Volatile Bitfield | |
163 | Item(Global): Logical Minimum, data= [ 0x81 ] 129 | |
164 | Item(Global): Logical Maximum, data= [ 0x7f ] 127 | |
165 | Item(Global): Report Size, data= [ 0x08 ] 8 | |
166 | Item(Global): Report Count, data= [ 0x01 ] 1 | |
167 | Item(Local ): Usage, data= [ 0x38 ] 56 | |
168 | Wheel | |
169 | Item(Main ): Input, data= [ 0x06 ] 6 | |
170 | Data Variable Relative No_Wrap Linear | |
171 | Preferred_State No_Null_Position Non_Volatile Bitfield | |
172 | Item(Global): Usage Page, data= [ 0x0c ] 12 | |
173 | Consumer | |
174 | Item(Local ): Usage, data= [ 0x38 0x02 ] 568 | |
175 | AC Pan | |
176 | Item(Global): Report Count, data= [ 0x01 ] 1 | |
177 | Item(Main ): Input, data= [ 0x06 ] 6 | |
178 | Data Variable Relative No_Wrap Linear | |
179 | Preferred_State No_Null_Position Non_Volatile Bitfield | |
180 | Item(Main ): End Collection, data=none | |
181 | Item(Main ): End Collection, data=none | |
182 | Item(Global): Usage Page, data= [ 0x0c ] 12 | |
183 | Consumer | |
184 | Item(Local ): Usage, data= [ 0x01 ] 1 | |
185 | Consumer Control | |
186 | Item(Main ): Collection, data= [ 0x01 ] 1 | |
187 | Application | |
188 | Item(Global): Report ID, data= [ 0x03 ] 3 | |
189 | Item(Global): Report Size, data= [ 0x10 ] 16 | |
190 | Item(Global): Report Count, data= [ 0x02 ] 2 | |
191 | Item(Global): Logical Minimum, data= [ 0x01 ] 1 | |
192 | Item(Global): Logical Maximum, data= [ 0x8c 0x02 ] 652 | |
193 | Item(Local ): Usage Minimum, data= [ 0x01 ] 1 | |
194 | Consumer Control | |
195 | Item(Local ): Usage Maximum, data= [ 0x8c 0x02 ] 652 | |
196 | (null) | |
197 | Item(Main ): Input, data= [ 0x00 ] 0 | |
198 | Data Array Absolute No_Wrap Linear | |
199 | Preferred_State No_Null_Position Non_Volatile Bitfield | |
200 | Item(Main ): End Collection, data=none | |
201 | Item(Global): Usage Page, data= [ 0x01 ] 1 | |
202 | Generic Desktop Controls | |
203 | Item(Local ): Usage, data= [ 0x80 ] 128 | |
204 | System Control | |
205 | Item(Main ): Collection, data= [ 0x01 ] 1 | |
206 | Application | |
207 | Item(Global): Report ID, data= [ 0x04 ] 4 | |
208 | Item(Global): Report Size, data= [ 0x02 ] 2 | |
209 | Item(Global): Report Count, data= [ 0x01 ] 1 | |
210 | Item(Global): Logical Minimum, data= [ 0x01 ] 1 | |
211 | Item(Global): Logical Maximum, data= [ 0x03 ] 3 | |
212 | Item(Local ): Usage, data= [ 0x82 ] 130 | |
213 | System Sleep | |
214 | Item(Local ): Usage, data= [ 0x81 ] 129 | |
215 | System Power Down | |
216 | Item(Local ): Usage, data= [ 0x83 ] 131 | |
217 | System Wake Up | |
218 | Item(Main ): Input, data= [ 0x60 ] 96 | |
219 | Data Array Absolute No_Wrap Linear | |
220 | No_Preferred_State Null_State Non_Volatile Bitfield | |
221 | Item(Global): Report Size, data= [ 0x06 ] 6 | |
222 | Item(Main ): Input, data= [ 0x03 ] 3 | |
223 | Constant Variable Absolute No_Wrap Linear | |
224 | Preferred_State No_Null_Position Non_Volatile Bitfield | |
225 | Item(Main ): End Collection, data=none | |
226 | Item(Global): Usage Page, data= [ 0xbc 0xff ] 65468 | |
227 | (null) | |
228 | Item(Local ): Usage, data= [ 0x88 ] 136 | |
229 | (null) | |
230 | Item(Main ): Collection, data= [ 0x01 ] 1 | |
231 | Application | |
232 | Item(Global): Report ID, data= [ 0x08 ] 8 | |
233 | Item(Local ): Usage Minimum, data= [ 0x01 ] 1 | |
234 | (null) | |
235 | Item(Local ): Usage Maximum, data= [ 0xff ] 255 | |
236 | (null) | |
237 | Item(Global): Logical Minimum, data= [ 0x01 ] 1 | |
238 | Item(Global): Logical Maximum, data= [ 0xff 0x00 ] 255 | |
239 | Item(Global): Report Size, data= [ 0x08 ] 8 | |
240 | Item(Global): Report Count, data= [ 0x01 ] 1 | |
241 | Item(Main ): Input, data= [ 0x00 ] 0 | |
242 | Data Array Absolute No_Wrap Linear | |
243 | Preferred_State No_Null_Position Non_Volatile Bitfield | |
244 | Item(Main ): End Collection, data=none | |
245 | Endpoint Descriptor: | |
246 | bLength 7 | |
247 | bDescriptorType 5 | |
248 | bEndpointAddress 0x82 EP 2 IN | |
249 | bmAttributes 3 | |
250 | Transfer Type Interrupt | |
251 | Synch Type None | |
252 | Usage Type Data | |
253 | wMaxPacketSize 0x0008 1x 8 bytes | |
254 | bInterval 2 | |
255 | Interface Descriptor: | |
256 | bLength 9 | |
257 | bDescriptorType 4 | |
258 | bInterfaceNumber 2 | |
259 | bAlternateSetting 0 | |
260 | bNumEndpoints 1 | |
261 | bInterfaceClass 3 Human Interface Device | |
262 | bInterfaceSubClass 0 | |
263 | bInterfaceProtocol 0 | |
264 | iInterface 0 | |
265 | HID Device Descriptor: | |
266 | bLength 9 | |
267 | bDescriptorType 33 | |
268 | bcdHID 1.11 | |
269 | bCountryCode 0 Not supported | |
270 | bNumDescriptors 1 | |
271 | bDescriptorType 34 Report | |
272 | wDescriptorLength 98 | |
273 | Report Descriptor: (length is 98) | |
274 | Item(Global): Usage Page, data= [ 0x00 0xff ] 65280 | |
275 | (null) | |
276 | Item(Local ): Usage, data= [ 0x01 ] 1 | |
277 | (null) | |
278 | Item(Main ): Collection, data= [ 0x01 ] 1 | |
279 | Application | |
280 | Item(Global): Report ID, data= [ 0x10 ] 16 | |
281 | Item(Global): Report Size, data= [ 0x08 ] 8 | |
282 | Item(Global): Report Count, data= [ 0x06 ] 6 | |
283 | Item(Global): Logical Minimum, data= [ 0x00 ] 0 | |
284 | Item(Global): Logical Maximum, data= [ 0xff 0x00 ] 255 | |
285 | Item(Local ): Usage, data= [ 0x01 ] 1 | |
286 | (null) | |
287 | Item(Main ): Input, data= [ 0x00 ] 0 | |
288 | Data Array Absolute No_Wrap Linear | |
289 | Preferred_State No_Null_Position Non_Volatile Bitfield | |
290 | Item(Local ): Usage, data= [ 0x01 ] 1 | |
291 | (null) | |
292 | Item(Main ): Output, data= [ 0x00 ] 0 | |
293 | Data Array Absolute No_Wrap Linear | |
294 | Preferred_State No_Null_Position Non_Volatile Bitfield | |
295 | Item(Main ): End Collection, data=none | |
296 | Item(Global): Usage Page, data= [ 0x00 0xff ] 65280 | |
297 | (null) | |
298 | Item(Local ): Usage, data= [ 0x02 ] 2 | |
299 | (null) | |
300 | Item(Main ): Collection, data= [ 0x01 ] 1 | |
301 | Application | |
302 | Item(Global): Report ID, data= [ 0x11 ] 17 | |
303 | Item(Global): Report Size, data= [ 0x08 ] 8 | |
304 | Item(Global): Report Count, data= [ 0x13 ] 19 | |
305 | Item(Global): Logical Minimum, data= [ 0x00 ] 0 | |
306 | Item(Global): Logical Maximum, data= [ 0xff 0x00 ] 255 | |
307 | Item(Local ): Usage, data= [ 0x02 ] 2 | |
308 | (null) | |
309 | Item(Main ): Input, data= [ 0x00 ] 0 | |
310 | Data Array Absolute No_Wrap Linear | |
311 | Preferred_State No_Null_Position Non_Volatile Bitfield | |
312 | Item(Local ): Usage, data= [ 0x02 ] 2 | |
313 | (null) | |
314 | Item(Main ): Output, data= [ 0x00 ] 0 | |
315 | Data Array Absolute No_Wrap Linear | |
316 | Preferred_State No_Null_Position Non_Volatile Bitfield | |
317 | Item(Main ): End Collection, data=none | |
318 | Item(Global): Usage Page, data= [ 0x00 0xff ] 65280 | |
319 | (null) | |
320 | Item(Local ): Usage, data= [ 0x04 ] 4 | |
321 | (null) | |
322 | Item(Main ): Collection, data= [ 0x01 ] 1 | |
323 | Application | |
324 | Item(Global): Report ID, data= [ 0x20 ] 32 | |
325 | Item(Global): Report Size, data= [ 0x08 ] 8 | |
326 | Item(Global): Report Count, data= [ 0x0e ] 14 | |
327 | Item(Global): Logical Minimum, data= [ 0x00 ] 0 | |
328 | Item(Global): Logical Maximum, data= [ 0xff 0x00 ] 255 | |
329 | Item(Local ): Usage, data= [ 0x41 ] 65 | |
330 | (null) | |
331 | Item(Main ): Input, data= [ 0x00 ] 0 | |
332 | Data Array Absolute No_Wrap Linear | |
333 | Preferred_State No_Null_Position Non_Volatile Bitfield | |
334 | Item(Local ): Usage, data= [ 0x41 ] 65 | |
335 | (null) | |
336 | Item(Main ): Output, data= [ 0x00 ] 0 | |
337 | Data Array Absolute No_Wrap Linear | |
338 | Preferred_State No_Null_Position Non_Volatile Bitfield | |
339 | Item(Global): Report ID, data= [ 0x21 ] 33 | |
340 | Item(Global): Report Count, data= [ 0x1f ] 31 | |
341 | Item(Global): Logical Minimum, data= [ 0x00 ] 0 | |
342 | Item(Global): Logical Maximum, data= [ 0xff 0x00 ] 255 | |
343 | Item(Local ): Usage, data= [ 0x42 ] 66 | |
344 | (null) | |
345 | Item(Main ): Input, data= [ 0x00 ] 0 | |
346 | Data Array Absolute No_Wrap Linear | |
347 | Preferred_State No_Null_Position Non_Volatile Bitfield | |
348 | Item(Local ): Usage, data= [ 0x42 ] 66 | |
349 | (null) | |
350 | Item(Main ): Output, data= [ 0x00 ] 0 | |
351 | Data Array Absolute No_Wrap Linear | |
352 | Preferred_State No_Null_Position Non_Volatile Bitfield | |
353 | Item(Main ): End Collection, data=none | |
354 | Endpoint Descriptor: | |
355 | bLength 7 | |
356 | bDescriptorType 5 | |
357 | bEndpointAddress 0x83 EP 3 IN | |
358 | bmAttributes 3 | |
359 | Transfer Type Interrupt | |
360 | Synch Type None | |
361 | Usage Type Data | |
362 | wMaxPacketSize 0x0020 1x 32 bytes | |
363 | bInterval 2 | |
364 | Device Status: 0x0000 | |
365 | (Bus Powered) |
0 | /* | |
1 | * Copyright (C) 2016-2018 Richard Hughes <richard@hughsie.com> | |
2 | * | |
3 | * SPDX-License-Identifier: LGPL-2.1+ | |
4 | */ | |
5 | ||
6 | #include "config.h" | |
7 | ||
8 | #include <fwupd.h> | |
9 | ||
10 | #include "fu-plugin-vfuncs.h" | |
11 | ||
12 | #include "fu-unifying-bootloader-nordic.h" | |
13 | #include "fu-unifying-bootloader-texas.h" | |
14 | #include "fu-unifying-common.h" | |
15 | #include "fu-unifying-peripheral.h" | |
16 | #include "fu-unifying-runtime.h" | |
17 | ||
18 | gboolean | |
19 | fu_plugin_startup (FuPlugin *plugin, GError **error) | |
20 | { | |
21 | /* check the kernel has CONFIG_HIDRAW */ | |
22 | if (!g_file_test ("/sys/class/hidraw", G_FILE_TEST_IS_DIR)) { | |
23 | g_set_error_literal (error, | |
24 | FWUPD_ERROR, | |
25 | FWUPD_ERROR_NOT_SUPPORTED, | |
26 | "no kernel support for CONFIG_HIDRAW"); | |
27 | return FALSE; | |
28 | } | |
29 | return TRUE; | |
30 | } | |
31 | ||
32 | void | |
33 | fu_plugin_init (FuPlugin *plugin) | |
34 | { | |
35 | fu_plugin_set_build_hash (plugin, FU_BUILD_HASH); | |
36 | fu_plugin_add_rule (plugin, FU_PLUGIN_RULE_SUPPORTS_PROTOCOL, "com.logitech.unifying"); | |
37 | fu_plugin_add_rule (plugin, FU_PLUGIN_RULE_SUPPORTS_PROTOCOL, "com.logitech.unifyingsigned"); | |
38 | fu_plugin_add_udev_subsystem (plugin, "hidraw"); | |
39 | ||
40 | /* register the custom types */ | |
41 | g_type_ensure (FU_TYPE_UNIFYING_BOOTLOADER_NORDIC); | |
42 | g_type_ensure (FU_TYPE_UNIFYING_BOOTLOADER_TEXAS); | |
43 | g_type_ensure (FU_TYPE_UNIFYING_PERIPHERAL); | |
44 | g_type_ensure (FU_TYPE_UNIFYING_RUNTIME); | |
45 | } |
0 | /* | |
1 | * Copyright (C) 2016-2018 Richard Hughes <richard@hughsie.com> | |
2 | * | |
3 | * SPDX-License-Identifier: LGPL-2.1+ | |
4 | */ | |
5 | ||
6 | #include "config.h" | |
7 | ||
8 | #include <string.h> | |
9 | ||
10 | #include "fu-unifying-common.h" | |
11 | #include "fu-unifying-bootloader-nordic.h" | |
12 | ||
13 | struct _FuUnifyingBootloaderNordic | |
14 | { | |
15 | FuUnifyingBootloader parent_instance; | |
16 | }; | |
17 | ||
18 | G_DEFINE_TYPE (FuUnifyingBootloaderNordic, fu_unifying_bootloader_nordic, FU_TYPE_UNIFYING_BOOTLOADER) | |
19 | ||
20 | static gchar * | |
21 | fu_unifying_bootloader_nordic_get_hw_platform_id (FuUnifyingBootloader *self, GError **error) | |
22 | { | |
23 | g_autoptr(FuUnifyingBootloaderRequest) req = fu_unifying_bootloader_request_new (); | |
24 | req->cmd = FU_UNIFYING_BOOTLOADER_CMD_GET_HW_PLATFORM_ID; | |
25 | if (!fu_unifying_bootloader_request (self, req, error)) { | |
26 | g_prefix_error (error, "failed to get HW ID: "); | |
27 | return NULL; | |
28 | } | |
29 | return g_strndup ((const gchar *) req->data, req->len); | |
30 | } | |
31 | ||
32 | static gchar * | |
33 | fu_unifying_bootloader_nordic_get_fw_version (FuUnifyingBootloader *self, GError **error) | |
34 | { | |
35 | guint16 micro; | |
36 | ||
37 | g_autoptr(FuUnifyingBootloaderRequest) req = fu_unifying_bootloader_request_new (); | |
38 | req->cmd = FU_UNIFYING_BOOTLOADER_CMD_GET_FW_VERSION; | |
39 | if (!fu_unifying_bootloader_request (self, req, error)) { | |
40 | g_prefix_error (error, "failed to get firmware version: "); | |
41 | return NULL; | |
42 | } | |
43 | ||
44 | /* RRRxx.yy_Bzzzz | |
45 | * 012345678901234*/ | |
46 | micro = (guint16) fu_unifying_buffer_read_uint8 ((const gchar *) req->data + 10) << 8; | |
47 | micro += fu_unifying_buffer_read_uint8 ((const gchar *) req->data + 12); | |
48 | return fu_unifying_format_version ("RQR", | |
49 | fu_unifying_buffer_read_uint8 ((const gchar *) req->data + 3), | |
50 | fu_unifying_buffer_read_uint8 ((const gchar *) req->data + 6), | |
51 | micro); | |
52 | } | |
53 | ||
54 | static gboolean | |
55 | fu_unifying_bootloader_nordic_setup (FuUnifyingBootloader *self, GError **error) | |
56 | { | |
57 | g_autofree gchar *hw_platform_id = NULL; | |
58 | g_autofree gchar *version_fw = NULL; | |
59 | g_autoptr(GError) error_local = NULL; | |
60 | ||
61 | /* get MCU */ | |
62 | hw_platform_id = fu_unifying_bootloader_nordic_get_hw_platform_id (self, error); | |
63 | if (hw_platform_id == NULL) | |
64 | return FALSE; | |
65 | g_debug ("hw-platform-id=%s", hw_platform_id); | |
66 | ||
67 | /* get firmware version, which is not fatal */ | |
68 | version_fw = fu_unifying_bootloader_nordic_get_fw_version (self, &error_local); | |
69 | if (version_fw == NULL) { | |
70 | g_warning ("failed to get firmware version: %s", | |
71 | error_local->message); | |
72 | fu_device_set_version (FU_DEVICE (self), "RQR12.00_B0000", | |
73 | FWUPD_VERSION_FORMAT_PLAIN); | |
74 | } else { | |
75 | fu_device_set_version (FU_DEVICE (self), version_fw, | |
76 | FWUPD_VERSION_FORMAT_PLAIN); | |
77 | } | |
78 | ||
79 | return TRUE; | |
80 | } | |
81 | ||
82 | static gboolean | |
83 | fu_unifying_bootloader_nordic_write_signature (FuUnifyingBootloader *self, | |
84 | guint16 addr, guint8 len, const guint8 *data, | |
85 | GError **error) | |
86 | { | |
87 | g_autoptr(FuUnifyingBootloaderRequest) req = fu_unifying_bootloader_request_new(); | |
88 | req->cmd = 0xC0; | |
89 | req->addr = addr; | |
90 | req->len = len; | |
91 | memcpy (req->data, data, req->len); | |
92 | if (!fu_unifying_bootloader_request (self, req, error)) { | |
93 | g_prefix_error (error, "failed to write sig @0x%02x: ", addr); | |
94 | return FALSE; | |
95 | } | |
96 | if (req->cmd == FU_UNIFYING_BOOTLOADER_CMD_WRITE_RAM_BUFFER_INVALID_ADDR) { | |
97 | g_set_error (error, | |
98 | G_IO_ERROR, | |
99 | G_IO_ERROR_FAILED, | |
100 | "failed to write @%04x: signature is too big", | |
101 | addr); | |
102 | return FALSE; | |
103 | } | |
104 | return TRUE; | |
105 | } | |
106 | ||
107 | static gboolean | |
108 | fu_unifying_bootloader_nordic_write (FuUnifyingBootloader *self, | |
109 | guint16 addr, guint8 len, const guint8 *data, | |
110 | GError **error) | |
111 | { | |
112 | g_autoptr(FuUnifyingBootloaderRequest) req = fu_unifying_bootloader_request_new (); | |
113 | req->cmd = FU_UNIFYING_BOOTLOADER_CMD_WRITE; | |
114 | req->addr = addr; | |
115 | req->len = len; | |
116 | if (req->len > 28) { | |
117 | g_set_error (error, | |
118 | G_IO_ERROR, | |
119 | G_IO_ERROR_FAILED, | |
120 | "failed to write @%04x: data length too large %02x", | |
121 | addr, req->len); | |
122 | return FALSE; | |
123 | } | |
124 | memcpy (req->data, data, req->len); | |
125 | if (!fu_unifying_bootloader_request (self, req, error)) { | |
126 | g_prefix_error (error, "failed to transfer fw @0x%02x: ", addr); | |
127 | return FALSE; | |
128 | } | |
129 | if (req->cmd == FU_UNIFYING_BOOTLOADER_CMD_WRITE_INVALID_ADDR) { | |
130 | g_set_error (error, | |
131 | G_IO_ERROR, | |
132 | G_IO_ERROR_FAILED, | |
133 | "failed to write @%04x: invalid address", | |
134 | addr); | |
135 | return FALSE; | |
136 | } | |
137 | if (req->cmd == FU_UNIFYING_BOOTLOADER_CMD_WRITE_VERIFY_FAIL) { | |
138 | g_set_error (error, | |
139 | G_IO_ERROR, | |
140 | G_IO_ERROR_FAILED, | |
141 | "failed to write @%04x: failed to verify flash content", | |
142 | addr); | |
143 | return FALSE; | |
144 | } | |
145 | if (req->cmd == FU_UNIFYING_BOOTLOADER_CMD_WRITE_NONZERO_START) { | |
146 | g_debug ("wrote %d bytes at address %04x, value %02x", req->len, | |
147 | req->addr, req->data[0]); | |
148 | g_set_error (error, | |
149 | G_IO_ERROR, | |
150 | G_IO_ERROR_FAILED, | |
151 | "failed to write @%04x: only 1 byte write of 0xff supported", | |
152 | addr); | |
153 | return FALSE; | |
154 | } | |
155 | if (req->cmd == FU_UNIFYING_BOOTLOADER_CMD_WRITE_INVALID_CRC) { | |
156 | g_set_error (error, | |
157 | G_IO_ERROR, | |
158 | G_IO_ERROR_FAILED, | |
159 | "failed to write @%04x: invalid CRC", | |
160 | addr); | |
161 | return FALSE; | |
162 | } | |
163 | return TRUE; | |
164 | } | |
165 | ||
166 | static gboolean | |
167 | fu_unifying_bootloader_nordic_erase (FuUnifyingBootloader *self, guint16 addr, GError **error) | |
168 | { | |
169 | g_autoptr(FuUnifyingBootloaderRequest) req = fu_unifying_bootloader_request_new (); | |
170 | req->cmd = FU_UNIFYING_BOOTLOADER_CMD_ERASE_PAGE; | |
171 | req->addr = addr; | |
172 | req->len = 0x01; | |
173 | if (!fu_unifying_bootloader_request (self, req, error)) { | |
174 | g_prefix_error (error, "failed to erase fw @0x%02x: ", addr); | |
175 | return FALSE; | |
176 | } | |
177 | if (req->cmd == FU_UNIFYING_BOOTLOADER_CMD_ERASE_PAGE_INVALID_ADDR) { | |
178 | g_set_error (error, | |
179 | G_IO_ERROR, | |
180 | G_IO_ERROR_FAILED, | |
181 | "failed to erase @%04x: invalid page", | |
182 | addr); | |
183 | return FALSE; | |
184 | } | |
185 | if (req->cmd == FU_UNIFYING_BOOTLOADER_CMD_ERASE_PAGE_NONZERO_START) { | |
186 | g_set_error (error, | |
187 | G_IO_ERROR, | |
188 | G_IO_ERROR_FAILED, | |
189 | "failed to erase @%04x: byte 0x00 is not 0xff", | |
190 | addr); | |
191 | return FALSE; | |
192 | } | |
193 | return TRUE; | |
194 | } | |
195 | ||
196 | static gboolean | |
197 | fu_unifying_bootloader_nordic_write_firmware (FuDevice *device, | |
198 | FuFirmware *firmware, | |
199 | FwupdInstallFlags flags, | |
200 | GError **error) | |
201 | { | |
202 | FuUnifyingBootloader *self = FU_UNIFYING_BOOTLOADER (device); | |
203 | const FuUnifyingBootloaderRequest *payload; | |
204 | guint16 addr; | |
205 | g_autoptr(GBytes) fw = NULL; | |
206 | g_autoptr(GPtrArray) reqs = NULL; | |
207 | ||
208 | /* get default image */ | |
209 | fw = fu_firmware_get_image_default_bytes (firmware, error); | |
210 | if (fw == NULL) | |
211 | return FALSE; | |
212 | ||
213 | /* erase firmware pages up to the bootloader */ | |
214 | fu_device_set_status (device, FWUPD_STATUS_DEVICE_ERASE); | |
215 | for (addr = fu_unifying_bootloader_get_addr_lo (self); | |
216 | addr < fu_unifying_bootloader_get_addr_hi (self); | |
217 | addr += fu_unifying_bootloader_get_blocksize (self)) { | |
218 | if (!fu_unifying_bootloader_nordic_erase (self, addr, error)) | |
219 | return FALSE; | |
220 | } | |
221 | ||
222 | /* transfer payload */ | |
223 | reqs = fu_unifying_bootloader_parse_requests (self, fw, error); | |
224 | if (reqs == NULL) | |
225 | return FALSE; | |
226 | fu_device_set_status (device, FWUPD_STATUS_DEVICE_WRITE); | |
227 | for (guint i = 1; i < reqs->len; i++) { | |
228 | gboolean res; | |
229 | payload = g_ptr_array_index (reqs, i); | |
230 | ||
231 | if (payload->cmd == FU_UNIFYING_BOOTLOADER_CMD_WRITE_SIGNATURE) { | |
232 | res = fu_unifying_bootloader_nordic_write_signature (self, | |
233 | payload->addr, | |
234 | payload->len, | |
235 | payload->data, | |
236 | error); | |
237 | } else { | |
238 | res = fu_unifying_bootloader_nordic_write (self, | |
239 | payload->addr, | |
240 | payload->len, | |
241 | payload->data, | |
242 | error); | |
243 | } | |
244 | ||
245 | if (!res) | |
246 | return FALSE; | |
247 | fu_device_set_progress_full (device, i * 32, reqs->len * 32); | |
248 | } | |
249 | ||
250 | /* send the first managed packet last, excluding the reset vector */ | |
251 | payload = g_ptr_array_index (reqs, 0); | |
252 | if (!fu_unifying_bootloader_nordic_write (self, | |
253 | payload->addr + 1, | |
254 | payload->len - 1, | |
255 | payload->data + 1, | |
256 | error)) | |
257 | return FALSE; | |
258 | ||
259 | if (!fu_unifying_bootloader_nordic_write (self, | |
260 | 0x0000, | |
261 | 0x01, | |
262 | payload->data, | |
263 | error)) | |
264 | return FALSE; | |
265 | ||
266 | /* mark as complete */ | |
267 | fu_device_set_progress_full (device, reqs->len * 32, reqs->len * 32); | |
268 | ||
269 | /* success! */ | |
270 | return TRUE; | |
271 | } | |
272 | ||
273 | static void | |
274 | fu_unifying_bootloader_nordic_class_init (FuUnifyingBootloaderNordicClass *klass) | |
275 | { | |
276 | FuDeviceClass *klass_device = FU_DEVICE_CLASS (klass); | |
277 | FuUnifyingBootloaderClass *klass_device_bootloader = FU_UNIFYING_BOOTLOADER_CLASS (klass); | |
278 | klass_device->write_firmware = fu_unifying_bootloader_nordic_write_firmware; | |
279 | klass_device_bootloader->setup = fu_unifying_bootloader_nordic_setup; | |
280 | } | |
281 | ||
282 | static void | |
283 | fu_unifying_bootloader_nordic_init (FuUnifyingBootloaderNordic *self) | |
284 | { | |
285 | } |
0 | /* | |
1 | * Copyright (C) 2016-2018 Richard Hughes <richard@hughsie.com> | |
2 | * | |
3 | * SPDX-License-Identifier: LGPL-2.1+ | |
4 | */ | |
5 | ||
6 | #pragma once | |
7 | ||
8 | #include "fu-unifying-bootloader.h" | |
9 | ||
10 | #define FU_TYPE_UNIFYING_BOOTLOADER_NORDIC (fu_unifying_bootloader_nordic_get_type ()) | |
11 | G_DECLARE_FINAL_TYPE (FuUnifyingBootloaderNordic, fu_unifying_bootloader_nordic, FU, UNIFYING_BOOTLOADER_NORDIC, FuUnifyingBootloader) |
0 | /* | |
1 | * Copyright (C) 2016-2018 Richard Hughes <richard@hughsie.com> | |
2 | * | |
3 | * SPDX-License-Identifier: LGPL-2.1+ | |
4 | */ | |
5 | ||
6 | #include "config.h" | |
7 | ||
8 | #include <string.h> | |
9 | ||
10 | #include "fu-unifying-common.h" | |
11 | #include "fu-unifying-bootloader-texas.h" | |
12 | ||
13 | struct _FuUnifyingBootloaderTexas | |
14 | { | |
15 | FuUnifyingBootloader parent_instance; | |
16 | }; | |
17 | ||
18 | G_DEFINE_TYPE (FuUnifyingBootloaderTexas, fu_unifying_bootloader_texas, FU_TYPE_UNIFYING_BOOTLOADER) | |
19 | ||
20 | static gboolean | |
21 | fu_unifying_bootloader_texas_erase_all (FuUnifyingBootloader *self, GError **error) | |
22 | { | |
23 | g_autoptr(FuUnifyingBootloaderRequest) req = fu_unifying_bootloader_request_new (); | |
24 | req->cmd = FU_UNIFYING_BOOTLOADER_CMD_FLASH_RAM; | |
25 | req->len = 0x01; /* magic number */ | |
26 | req->data[0] = 0x00; /* magic number */ | |
27 | if (!fu_unifying_bootloader_request (self, req, error)) { | |
28 | g_prefix_error (error, "failed to erase all pages: "); | |
29 | return FALSE; | |
30 | } | |
31 | return TRUE; | |
32 | } | |
33 | ||
34 | static gboolean | |
35 | fu_unifying_bootloader_texas_compute_and_test_crc (FuUnifyingBootloader *self, GError **error) | |
36 | { | |
37 | g_autoptr(FuUnifyingBootloaderRequest) req = fu_unifying_bootloader_request_new (); | |
38 | req->cmd = FU_UNIFYING_BOOTLOADER_CMD_FLASH_RAM; | |
39 | req->len = 0x01; /* magic number */ | |
40 | req->data[0] = 0x03; /* magic number */ | |
41 | if (!fu_unifying_bootloader_request (self, req, error)) { | |
42 | g_prefix_error (error, "failed to compute and test CRC: "); | |
43 | return FALSE; | |
44 | } | |
45 | if (req->cmd == FU_UNIFYING_BOOTLOADER_CMD_FLASH_RAM_WRONG_CRC) { | |
46 | g_set_error_literal (error, | |
47 | G_IO_ERROR, | |
48 | G_IO_ERROR_FAILED, | |
49 | "CRC is incorrect"); | |
50 | return FALSE; | |
51 | } | |
52 | return TRUE; | |
53 | } | |
54 | ||
55 | static gboolean | |
56 | fu_unifying_bootloader_texas_flash_ram_buffer (FuUnifyingBootloader *self, guint16 addr, GError **error) | |
57 | { | |
58 | g_autoptr(FuUnifyingBootloaderRequest) req = fu_unifying_bootloader_request_new (); | |
59 | req->cmd = FU_UNIFYING_BOOTLOADER_CMD_FLASH_RAM; | |
60 | req->addr = addr; | |
61 | req->len = 0x01; /* magic number */ | |
62 | req->data[0] = 0x01; /* magic number */ | |
63 | if (!fu_unifying_bootloader_request (self, req, error)) { | |
64 | g_prefix_error (error, "failed to flash ram buffer @%04x: ", addr); | |
65 | return FALSE; | |
66 | } | |
67 | if (req->cmd == FU_UNIFYING_BOOTLOADER_CMD_FLASH_RAM_INVALID_ADDR) { | |
68 | g_set_error (error, | |
69 | G_IO_ERROR, | |
70 | G_IO_ERROR_FAILED, | |
71 | "failed to flash ram buffer @%04x: invalid flash page", | |
72 | addr); | |
73 | return FALSE; | |
74 | } | |
75 | if (req->cmd == FU_UNIFYING_BOOTLOADER_CMD_FLASH_RAM_PAGE0_INVALID) { | |
76 | g_set_error (error, | |
77 | G_IO_ERROR, | |
78 | G_IO_ERROR_FAILED, | |
79 | "failed to flash ram buffer @%04x: invalid App JMP vector", | |
80 | addr); | |
81 | return FALSE; | |
82 | } | |
83 | if (req->cmd == FU_UNIFYING_BOOTLOADER_CMD_FLASH_RAM_INVALID_ORDER) { | |
84 | g_set_error (error, | |
85 | G_IO_ERROR, | |
86 | G_IO_ERROR_FAILED, | |
87 | "failed to flash ram buffer @%04x: page flashed before page 0", | |
88 | addr); | |
89 | return FALSE; | |
90 | } | |
91 | return TRUE; | |
92 | } | |
93 | ||
94 | static gboolean | |
95 | fu_unifying_bootloader_texas_clear_ram_buffer (FuUnifyingBootloader *self, GError **error) | |
96 | { | |
97 | g_autoptr(FuUnifyingBootloaderRequest) req = fu_unifying_bootloader_request_new (); | |
98 | req->cmd = FU_UNIFYING_BOOTLOADER_CMD_FLASH_RAM; | |
99 | req->addr = 0x0000; | |
100 | req->len = 0x01; /* magic number */ | |
101 | req->data[0] = 0x02; /* magic number */ | |
102 | if (!fu_unifying_bootloader_request (self, req, error)) { | |
103 | g_prefix_error (error, "failed to clear ram buffer @%04x: ", req->addr); | |
104 | return FALSE; | |
105 | } | |
106 | return TRUE; | |
107 | } | |
108 | ||
109 | static gboolean | |
110 | fu_unifying_bootloader_texas_write_firmware (FuDevice *device, | |
111 | FuFirmware *firmware, | |
112 | FwupdInstallFlags flags, | |
113 | GError **error) | |
114 | { | |
115 | FuUnifyingBootloader *self = FU_UNIFYING_BOOTLOADER (device); | |
116 | const FuUnifyingBootloaderRequest *payload; | |
117 | g_autoptr(GBytes) fw = NULL; | |
118 | g_autoptr(GPtrArray) reqs = NULL; | |
119 | g_autoptr(FuUnifyingBootloaderRequest) req = fu_unifying_bootloader_request_new (); | |
120 | ||
121 | /* get default image */ | |
122 | fw = fu_firmware_get_image_default_bytes (firmware, error); | |
123 | if (fw == NULL) | |
124 | return FALSE; | |
125 | ||
126 | /* transfer payload */ | |
127 | reqs = fu_unifying_bootloader_parse_requests (self, fw, error); | |
128 | if (reqs == NULL) | |
129 | return FALSE; | |
130 | ||
131 | /* erase all flash pages */ | |
132 | fu_device_set_status (device, FWUPD_STATUS_DEVICE_ERASE); | |
133 | if (!fu_unifying_bootloader_texas_erase_all (self, error)) | |
134 | return FALSE; | |
135 | ||
136 | /* set existing RAM buffer to 0xff's */ | |
137 | if (!fu_unifying_bootloader_texas_clear_ram_buffer (self, error)) | |
138 | return FALSE; | |
139 | ||
140 | /* write to RAM buffer */ | |
141 | fu_device_set_status (device, FWUPD_STATUS_DEVICE_WRITE); | |
142 | for (guint i = 0; i < reqs->len; i++) { | |
143 | payload = g_ptr_array_index (reqs, i); | |
144 | ||
145 | /* check size */ | |
146 | if (payload->len != 16) { | |
147 | g_set_error (error, | |
148 | G_IO_ERROR, | |
149 | G_IO_ERROR_FAILED, | |
150 | "payload size invalid @%04x: got 0x%02x", | |
151 | payload->addr, payload->len); | |
152 | return FALSE; | |
153 | } | |
154 | ||
155 | /* build packet */ | |
156 | req->cmd = payload->cmd; | |
157 | ||
158 | /* signature addresses do not need to fit inside 128 bytes */ | |
159 | if (req->cmd == FU_UNIFYING_BOOTLOADER_CMD_WRITE_SIGNATURE) | |
160 | req->addr = payload->addr; | |
161 | else | |
162 | req->addr = payload->addr % 0x80; | |
163 | ||
164 | req->len = payload->len; | |
165 | memcpy (req->data, payload->data, payload->len); | |
166 | if (!fu_unifying_bootloader_request (self, req, error)) { | |
167 | g_prefix_error (error, | |
168 | "failed to write ram buffer @0x%02x: ", | |
169 | req->addr); | |
170 | return FALSE; | |
171 | } | |
172 | if (req->cmd == FU_UNIFYING_BOOTLOADER_CMD_WRITE_RAM_BUFFER_INVALID_ADDR) { | |
173 | g_set_error (error, | |
174 | G_IO_ERROR, | |
175 | G_IO_ERROR_FAILED, | |
176 | "failed to write ram buffer @%04x: invalid location", | |
177 | req->addr); | |
178 | return FALSE; | |
179 | } | |
180 | if (req->cmd == FU_UNIFYING_BOOTLOADER_CMD_WRITE_RAM_BUFFER_OVERFLOW) { | |
181 | g_set_error (error, | |
182 | G_IO_ERROR, | |
183 | G_IO_ERROR_FAILED, | |
184 | "failed to write ram buffer @%04x: invalid size 0x%02x", | |
185 | req->addr, req->len); | |
186 | return FALSE; | |
187 | } | |
188 | ||
189 | /* flush RAM buffer to EEPROM */ | |
190 | if ((payload->addr + 0x10) % 0x80 == 0 && | |
191 | req->cmd != FU_UNIFYING_BOOTLOADER_CMD_WRITE_SIGNATURE) { | |
192 | guint16 addr_start = payload->addr - (7 * 0x10); | |
193 | g_debug ("addr flush @ 0x%04x for 0x%04x", | |
194 | payload->addr, addr_start); | |
195 | if (!fu_unifying_bootloader_texas_flash_ram_buffer (self, | |
196 | addr_start, | |
197 | error)) { | |
198 | g_prefix_error (error, | |
199 | "failed to flash ram buffer @0x%04x: ", | |
200 | addr_start); | |
201 | return FALSE; | |
202 | } | |
203 | } | |
204 | ||
205 | /* update progress */ | |
206 | fu_device_set_progress_full (device, i * 32, reqs->len * 32); | |
207 | } | |
208 | ||
209 | /* check CRC */ | |
210 | if (!fu_unifying_bootloader_texas_compute_and_test_crc (self, error)) | |
211 | return FALSE; | |
212 | ||
213 | /* mark as complete */ | |
214 | fu_device_set_progress_full (device, reqs->len * 32, reqs->len * 32); | |
215 | ||
216 | /* success! */ | |
217 | return TRUE; | |
218 | } | |
219 | ||
220 | static gboolean | |
221 | fu_unifying_bootloader_texas_setup (FuUnifyingBootloader *self, GError **error) | |
222 | { | |
223 | fu_device_set_version (FU_DEVICE (self), "RQR24.00_B0000", | |
224 | FWUPD_VERSION_FORMAT_PLAIN); | |
225 | return TRUE; | |
226 | } | |
227 | ||
228 | static void | |
229 | fu_unifying_bootloader_texas_class_init (FuUnifyingBootloaderTexasClass *klass) | |
230 | { | |
231 | FuDeviceClass *klass_device = FU_DEVICE_CLASS (klass); | |
232 | FuUnifyingBootloaderClass *klass_device_bootloader = FU_UNIFYING_BOOTLOADER_CLASS (klass); | |
233 | klass_device->write_firmware = fu_unifying_bootloader_texas_write_firmware; | |
234 | klass_device_bootloader->setup = fu_unifying_bootloader_texas_setup; | |
235 | } | |
236 | ||
237 | static void | |
238 | fu_unifying_bootloader_texas_init (FuUnifyingBootloaderTexas *self) | |
239 | { | |
240 | } |
0 | /* | |
1 | * Copyright (C) 2016-2018 Richard Hughes <richard@hughsie.com> | |
2 | * | |
3 | * SPDX-License-Identifier: LGPL-2.1+ | |
4 | */ | |
5 | ||
6 | #pragma once | |
7 | ||
8 | #include "fu-unifying-bootloader.h" | |
9 | ||
10 | #define FU_TYPE_UNIFYING_BOOTLOADER_TEXAS (fu_unifying_bootloader_texas_get_type ()) | |
11 | G_DECLARE_FINAL_TYPE (FuUnifyingBootloaderTexas, fu_unifying_bootloader_texas, FU, UNIFYING_BOOTLOADER_TEXAS, FuUnifyingBootloader) |
0 | /* | |
1 | * Copyright (C) 2016-2018 Richard Hughes <richard@hughsie.com> | |
2 | * | |
3 | * SPDX-License-Identifier: LGPL-2.1+ | |
4 | */ | |
5 | ||
6 | #include "config.h" | |
7 | ||
8 | #include <string.h> | |
9 | ||
10 | #include "fu-firmware-common.h" | |
11 | #include "fu-unifying-common.h" | |
12 | #include "fu-unifying-bootloader.h" | |
13 | #include "fu-unifying-hidpp.h" | |
14 | ||
15 | typedef struct | |
16 | { | |
17 | guint16 flash_addr_lo; | |
18 | guint16 flash_addr_hi; | |
19 | guint16 flash_blocksize; | |
20 | } FuUnifyingBootloaderPrivate; | |
21 | ||
22 | #define FU_UNIFYING_DEVICE_EP1 0x81 | |
23 | #define FU_UNIFYING_DEVICE_EP3 0x83 | |
24 | ||
25 | G_DEFINE_TYPE_WITH_PRIVATE (FuUnifyingBootloader, fu_unifying_bootloader, FU_TYPE_USB_DEVICE) | |
26 | ||
27 | #define GET_PRIVATE(o) (fu_unifying_bootloader_get_instance_private (o)) | |
28 | ||
29 | static void | |
30 | fu_unifying_bootloader_to_string (FuDevice *device, guint idt, GString *str) | |
31 | { | |
32 | FuUnifyingBootloader *self = FU_UNIFYING_BOOTLOADER (device); | |
33 | FuUnifyingBootloaderPrivate *priv = GET_PRIVATE (self); | |
34 | fu_common_string_append_kx (str, idt, "FlashAddrHigh", priv->flash_addr_hi); | |
35 | fu_common_string_append_kx (str, idt, "FlashAddrLow", priv->flash_addr_lo); | |
36 | fu_common_string_append_kx (str, idt, "FlashBlockSize", priv->flash_blocksize); | |
37 | } | |
38 | ||
39 | FuUnifyingBootloaderRequest * | |
40 | fu_unifying_bootloader_request_new (void) | |
41 | { | |
42 | FuUnifyingBootloaderRequest *req = g_new0 (FuUnifyingBootloaderRequest, 1); | |
43 | return req; | |
44 | } | |
45 | ||
46 | GPtrArray * | |
47 | fu_unifying_bootloader_parse_requests (FuUnifyingBootloader *self, GBytes *fw, GError **error) | |
48 | { | |
49 | const gchar *tmp; | |
50 | g_auto(GStrv) lines = NULL; | |
51 | g_autoptr(GPtrArray) reqs = NULL; | |
52 | guint32 last_addr = 0; | |
53 | ||
54 | reqs = g_ptr_array_new_with_free_func (g_free); | |
55 | tmp = g_bytes_get_data (fw, NULL); | |
56 | lines = g_strsplit_set (tmp, "\n\r", -1); | |
57 | for (guint i = 0; lines[i] != NULL; i++) { | |
58 | g_autoptr(FuUnifyingBootloaderRequest) payload = NULL; | |
59 | guint8 rec_type = 0x00; | |
60 | guint16 offset = 0x0000; | |
61 | gboolean exit = FALSE; | |
62 | ||
63 | /* skip empty lines */ | |
64 | tmp = lines[i]; | |
65 | if (strlen (tmp) < 5) | |
66 | continue; | |
67 | ||
68 | payload = fu_unifying_bootloader_request_new (); | |
69 | payload->len = fu_unifying_buffer_read_uint8 (tmp + 0x01); | |
70 | if (payload->len > 28) { | |
71 | g_set_error (error, | |
72 | G_IO_ERROR, | |
73 | G_IO_ERROR_INVALID_DATA, | |
74 | "firmware data invalid: too large %u bytes", | |
75 | payload->len); | |
76 | return NULL; | |
77 | } | |
78 | payload->addr = fu_firmware_strparse_uint16 (tmp + 0x03); | |
79 | payload->cmd = FU_UNIFYING_BOOTLOADER_CMD_WRITE_RAM_BUFFER; | |
80 | ||
81 | rec_type = fu_unifying_buffer_read_uint8 (tmp + 0x07); | |
82 | ||
83 | switch (rec_type) { | |
84 | case 0x00: /* data */ | |
85 | break; | |
86 | case 0x01: /* EOF */ | |
87 | exit = TRUE; | |
88 | break; | |
89 | case 0x03: /* start segment address */ | |
90 | /* this is used to specify the start address, | |
91 | it is doesn't mater in this context so we can | |
92 | safely ignore it */ | |
93 | continue; | |
94 | case 0x04: /* extended linear address */ | |
95 | offset = fu_firmware_strparse_uint16 (tmp + 0x09); | |
96 | if (offset != 0x0000) { | |
97 | g_set_error (error, | |
98 | G_IO_ERROR, | |
99 | G_IO_ERROR_INVALID_DATA, | |
100 | "extended linear addresses with offset different from 0 are not supported"); | |
101 | return NULL; | |
102 | } | |
103 | continue; | |
104 | case 0x05: /* start linear address */ | |
105 | /* this is used to specify the start address, | |
106 | it is doesn't mater in this context so we can | |
107 | safely ignore it */ | |
108 | continue; | |
109 | case 0xFD: /* custom - vendor */ | |
110 | /* record type of 0xFD indicates signature data */ | |
111 | payload->cmd = FU_UNIFYING_BOOTLOADER_CMD_WRITE_SIGNATURE; | |
112 | break; | |
113 | default: | |
114 | g_set_error (error, | |
115 | G_IO_ERROR, | |
116 | G_IO_ERROR_INVALID_DATA, | |
117 | "intel hex file record type %02x not supported", | |
118 | rec_type); | |
119 | return NULL; | |
120 | } | |
121 | ||
122 | if (exit) | |
123 | break; | |
124 | ||
125 | /* read the data, but skip the checksum byte */ | |
126 | for (guint j = 0; j < payload->len; j++) { | |
127 | const gchar *ptr = tmp + 0x09 + (j * 2); | |
128 | if (ptr[0] == '\0') { | |
129 | g_set_error (error, | |
130 | G_IO_ERROR, | |
131 | G_IO_ERROR_INVALID_DATA, | |
132 | "firmware data invalid: expected %u bytes", | |
133 | payload->len); | |
134 | return NULL; | |
135 | } | |
136 | payload->data[j] = fu_unifying_buffer_read_uint8 (ptr); | |
137 | } | |
138 | ||
139 | /* no need to bound check signature addresses */ | |
140 | if (payload->cmd == FU_UNIFYING_BOOTLOADER_CMD_WRITE_SIGNATURE) { | |
141 | g_ptr_array_add (reqs, g_steal_pointer (&payload)); | |
142 | continue; | |
143 | } | |
144 | ||
145 | /* skip the bootloader */ | |
146 | if (payload->addr > fu_unifying_bootloader_get_addr_hi (self)) { | |
147 | g_debug ("skipping write @ %04x", payload->addr); | |
148 | continue; | |
149 | } | |
150 | ||
151 | /* skip the header */ | |
152 | if (payload->addr < fu_unifying_bootloader_get_addr_lo (self)) { | |
153 | g_debug ("skipping write @ %04x", payload->addr); | |
154 | continue; | |
155 | } | |
156 | ||
157 | /* make sure firmware addresses only go up */ | |
158 | if (payload->addr < last_addr) { | |
159 | g_debug ("skipping write @ %04x", payload->addr); | |
160 | continue; | |
161 | } | |
162 | last_addr = payload->addr; | |
163 | ||
164 | /* pending */ | |
165 | g_ptr_array_add (reqs, g_steal_pointer (&payload)); | |
166 | } | |
167 | if (reqs->len == 0) { | |
168 | g_set_error_literal (error, | |
169 | G_IO_ERROR, | |
170 | G_IO_ERROR_INVALID_DATA, | |
171 | "firmware data invalid: no payloads found"); | |
172 | return NULL; | |
173 | } | |
174 | return g_steal_pointer (&reqs); | |
175 | } | |
176 | ||
177 | guint16 | |
178 | fu_unifying_bootloader_get_addr_lo (FuUnifyingBootloader *self) | |
179 | { | |
180 | FuUnifyingBootloaderPrivate *priv = GET_PRIVATE (self); | |
181 | g_return_val_if_fail (FU_IS_UNIFYING_BOOTLOADER (self), 0x0000); | |
182 | return priv->flash_addr_lo; | |
183 | } | |
184 | ||
185 | guint16 | |
186 | fu_unifying_bootloader_get_addr_hi (FuUnifyingBootloader *self) | |
187 | { | |
188 | FuUnifyingBootloaderPrivate *priv = GET_PRIVATE (self); | |
189 | g_return_val_if_fail (FU_IS_UNIFYING_BOOTLOADER (self), 0x0000); | |
190 | return priv->flash_addr_hi; | |
191 | } | |
192 | ||
193 | guint16 | |
194 | fu_unifying_bootloader_get_blocksize (FuUnifyingBootloader *self) | |
195 | { | |
196 | FuUnifyingBootloaderPrivate *priv = GET_PRIVATE (self); | |
197 | g_return_val_if_fail (FU_IS_UNIFYING_BOOTLOADER (self), 0x0000); | |
198 | return priv->flash_blocksize; | |
199 | } | |
200 | ||
201 | static gboolean | |
202 | fu_unifying_bootloader_attach (FuDevice *device, GError **error) | |
203 | { | |
204 | FuUnifyingBootloader *self = FU_UNIFYING_BOOTLOADER (device); | |
205 | g_autoptr(FuUnifyingBootloaderRequest) req = fu_unifying_bootloader_request_new (); | |
206 | req->cmd = FU_UNIFYING_BOOTLOADER_CMD_REBOOT; | |
207 | if (!fu_unifying_bootloader_request (self, req, error)) { | |
208 | g_prefix_error (error, "failed to attach back to runtime: "); | |
209 | return FALSE; | |
210 | } | |
211 | fu_device_add_flag (device, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG); | |
212 | return TRUE; | |
213 | } | |
214 | ||
215 | static gboolean | |
216 | fu_unifying_bootloader_set_bl_version (FuUnifyingBootloader *self, GError **error) | |
217 | { | |
218 | guint16 build; | |
219 | g_autofree gchar *version = NULL; | |
220 | g_autoptr(FuUnifyingBootloaderRequest) req = fu_unifying_bootloader_request_new (); | |
221 | ||
222 | /* call into hardware */ | |
223 | req->cmd = FU_UNIFYING_BOOTLOADER_CMD_GET_BL_VERSION; | |
224 | if (!fu_unifying_bootloader_request (self, req, error)) { | |
225 | g_prefix_error (error, "failed to get firmware version: "); | |
226 | return FALSE; | |
227 | } | |
228 | ||
229 | /* BOTxx.yy_Bzzzz | |
230 | * 012345678901234 */ | |
231 | build = (guint16) fu_unifying_buffer_read_uint8 ((const gchar *) req->data + 10) << 8; | |
232 | build += fu_unifying_buffer_read_uint8 ((const gchar *) req->data + 12); | |
233 | version = fu_unifying_format_version ("BOT", | |
234 | fu_unifying_buffer_read_uint8 ((const gchar *) req->data + 3), | |
235 | fu_unifying_buffer_read_uint8 ((const gchar *) req->data + 6), | |
236 | build); | |
237 | if (version == NULL) { | |
238 | g_prefix_error (error, "failed to format firmware version: "); | |
239 | return FALSE; | |
240 | } | |
241 | fu_device_set_version_bootloader (FU_DEVICE (self), version); | |
242 | return TRUE; | |
243 | } | |
244 | ||
245 | static gboolean | |
246 | fu_unifying_bootloader_open (FuUsbDevice *device, GError **error) | |
247 | { | |
248 | GUsbDevice *usb_device = fu_usb_device_get_dev (device); | |
249 | const guint idx = 0x00; | |
250 | ||
251 | /* claim the only interface */ | |
252 | if (!g_usb_device_claim_interface (usb_device, idx, | |
253 | G_USB_DEVICE_CLAIM_INTERFACE_BIND_KERNEL_DRIVER, | |
254 | error)) { | |
255 | g_prefix_error (error, "Failed to claim 0x%02x: ", idx); | |
256 | return FALSE; | |
257 | } | |
258 | ||
259 | /* success */ | |
260 | return TRUE; | |
261 | } | |
262 | ||
263 | static gboolean | |
264 | fu_unifying_bootloader_setup (FuDevice *device, GError **error) | |
265 | { | |
266 | FuUnifyingBootloaderClass *klass = FU_UNIFYING_BOOTLOADER_GET_CLASS (device); | |
267 | FuUnifyingBootloader *self = FU_UNIFYING_BOOTLOADER (device); | |
268 | FuUnifyingBootloaderPrivate *priv = GET_PRIVATE (self); | |
269 | g_autoptr(FuUnifyingBootloaderRequest) req = fu_unifying_bootloader_request_new (); | |
270 | ||
271 | /* get memory map */ | |
272 | req->cmd = FU_UNIFYING_BOOTLOADER_CMD_GET_MEMINFO; | |
273 | if (!fu_unifying_bootloader_request (self, req, error)) { | |
274 | g_prefix_error (error, "failed to get meminfo: "); | |
275 | return FALSE; | |
276 | } | |
277 | if (req->len != 0x06) { | |
278 | g_set_error (error, | |
279 | G_IO_ERROR, | |
280 | G_IO_ERROR_FAILED, | |
281 | "failed to get meminfo: invalid size %02x", | |
282 | req->len); | |
283 | return FALSE; | |
284 | } | |
285 | ||
286 | /* parse values */ | |
287 | priv->flash_addr_lo = fu_common_read_uint16 (req->data + 0, G_BIG_ENDIAN); | |
288 | priv->flash_addr_hi = fu_common_read_uint16 (req->data + 2, G_BIG_ENDIAN); | |
289 | priv->flash_blocksize = fu_common_read_uint16 (req->data + 4, G_BIG_ENDIAN); | |
290 | ||
291 | /* get bootloader version */ | |
292 | if (!fu_unifying_bootloader_set_bl_version (self, error)) | |
293 | return FALSE; | |
294 | ||
295 | /* subclassed further */ | |
296 | if (klass->setup != NULL) | |
297 | return klass->setup (self, error); | |
298 | ||
299 | /* success */ | |
300 | return TRUE; | |
301 | } | |
302 | ||
303 | static gboolean | |
304 | fu_unifying_bootloader_close (FuUsbDevice *device, GError **error) | |
305 | { | |
306 | GUsbDevice *usb_device = fu_usb_device_get_dev (device); | |
307 | if (usb_device != NULL) { | |
308 | if (!g_usb_device_release_interface (usb_device, 0x00, | |
309 | G_USB_DEVICE_CLAIM_INTERFACE_BIND_KERNEL_DRIVER, | |
310 | error)) { | |
311 | return FALSE; | |
312 | } | |
313 | } | |
314 | return TRUE; | |
315 | } | |
316 | ||
317 | gboolean | |
318 | fu_unifying_bootloader_request (FuUnifyingBootloader *self, | |
319 | FuUnifyingBootloaderRequest *req, | |
320 | GError **error) | |
321 | { | |
322 | GUsbDevice *usb_device = fu_usb_device_get_dev (FU_USB_DEVICE (self)); | |
323 | gsize actual_length = 0; | |
324 | guint8 buf_request[32]; | |
325 | guint8 buf_response[32]; | |
326 | ||
327 | /* build packet */ | |
328 | memset (buf_request, 0x00, sizeof (buf_request)); | |
329 | buf_request[0x00] = req->cmd; | |
330 | buf_request[0x01] = req->addr >> 8; | |
331 | buf_request[0x02] = req->addr & 0xff; | |
332 | buf_request[0x03] = req->len; | |
333 | if (!fu_memcpy_safe (buf_request, sizeof(buf_request), 0x04, /* dst */ | |
334 | req->data, sizeof(req->data), 0x0, /* src */ | |
335 | sizeof(req->data), error)) | |
336 | return FALSE; | |
337 | ||
338 | /* send request */ | |
339 | if (g_getenv ("FWUPD_UNIFYING_VERBOSE") != NULL) { | |
340 | fu_common_dump_raw (G_LOG_DOMAIN, "host->device", | |
341 | buf_request, sizeof (buf_request)); | |
342 | } | |
343 | if (usb_device != NULL) { | |
344 | if (!g_usb_device_control_transfer (usb_device, | |
345 | G_USB_DEVICE_DIRECTION_HOST_TO_DEVICE, | |
346 | G_USB_DEVICE_REQUEST_TYPE_CLASS, | |
347 | G_USB_DEVICE_RECIPIENT_INTERFACE, | |
348 | HID_REPORT_SET, | |
349 | 0x0200, 0x0000, | |
350 | buf_request, | |
351 | sizeof (buf_request), | |
352 | &actual_length, | |
353 | FU_UNIFYING_DEVICE_TIMEOUT_MS, | |
354 | NULL, | |
355 | error)) { | |
356 | g_prefix_error (error, "failed to send data: "); | |
357 | return FALSE; | |
358 | } | |
359 | } | |
360 | ||
361 | /* no response required when rebooting */ | |
362 | if (usb_device != NULL && | |
363 | req->cmd == FU_UNIFYING_BOOTLOADER_CMD_REBOOT) { | |
364 | g_autoptr(GError) error_ignore = NULL; | |
365 | if (!g_usb_device_interrupt_transfer (usb_device, | |
366 | FU_UNIFYING_DEVICE_EP1, | |
367 | buf_response, | |
368 | sizeof (buf_response), | |
369 | &actual_length, | |
370 | FU_UNIFYING_DEVICE_TIMEOUT_MS, | |
371 | NULL, | |
372 | &error_ignore)) { | |
373 | g_debug ("ignoring: %s", error_ignore->message); | |
374 | } else { | |
375 | if (g_getenv ("FWUPD_UNIFYING_VERBOSE") != NULL) { | |
376 | fu_common_dump_raw (G_LOG_DOMAIN, "device->host", | |
377 | buf_response, actual_length); | |
378 | } | |
379 | } | |
380 | return TRUE; | |
381 | } | |
382 | ||
383 | /* get response */ | |
384 | memset (buf_response, 0x00, sizeof (buf_response)); | |
385 | if (usb_device != NULL) { | |
386 | if (!g_usb_device_interrupt_transfer (usb_device, | |
387 | FU_UNIFYING_DEVICE_EP1, | |
388 | buf_response, | |
389 | sizeof (buf_response), | |
390 | &actual_length, | |
391 | FU_UNIFYING_DEVICE_TIMEOUT_MS, | |
392 | NULL, | |
393 | error)) { | |
394 | g_prefix_error (error, "failed to get data: "); | |
395 | return FALSE; | |
396 | } | |
397 | } else { | |
398 | /* emulated */ | |
399 | buf_response[0] = buf_request[0]; | |
400 | if (buf_response[0] == FU_UNIFYING_BOOTLOADER_CMD_GET_MEMINFO) { | |
401 | buf_response[3] = 0x06; /* len */ | |
402 | buf_response[4] = 0x40; /* lo MSB */ | |
403 | buf_response[5] = 0x00; /* lo LSB */ | |
404 | buf_response[6] = 0x6b; /* hi MSB */ | |
405 | buf_response[7] = 0xff; /* hi LSB */ | |
406 | buf_response[8] = 0x00; /* bs MSB */ | |
407 | buf_response[9] = 0x80; /* bs LSB */ | |
408 | } | |
409 | actual_length = sizeof (buf_response); | |
410 | } | |
411 | if (g_getenv ("FWUPD_UNIFYING_VERBOSE") != NULL) { | |
412 | fu_common_dump_raw (G_LOG_DOMAIN, "device->host", | |
413 | buf_response, actual_length); | |
414 | } | |
415 | ||
416 | /* parse response */ | |
417 | if ((buf_response[0x00] & 0xf0) != req->cmd) { | |
418 | g_set_error (error, | |
419 | G_IO_ERROR, | |
420 | G_IO_ERROR_FAILED, | |
421 | "invalid command response of %02x, expected %02x", | |
422 | buf_response[0x00], req->cmd); | |
423 | return FALSE; | |
424 | } | |
425 | req->cmd = buf_response[0x00]; | |
426 | req->addr = ((guint16) buf_response[0x01] << 8) + buf_response[0x02]; | |
427 | req->len = buf_response[0x03]; | |
428 | if (req->len > 28) { | |
429 | g_set_error (error, | |
430 | G_IO_ERROR, | |
431 | G_IO_ERROR_FAILED, | |
432 | "invalid data size of %02x", req->len); | |
433 | return FALSE; | |
434 | } | |
435 | memset (req->data, 0x00, 28); | |
436 | if (req->len > 0) | |
437 | memcpy (req->data, buf_response + 0x04, req->len); | |
438 | return TRUE; | |
439 | } | |
440 | ||
441 | static void | |
442 | fu_unifying_bootloader_init (FuUnifyingBootloader *self) | |
443 | { | |
444 | fu_device_add_flag (FU_DEVICE (self), FWUPD_DEVICE_FLAG_UPDATABLE); | |
445 | fu_device_add_flag (FU_DEVICE (self), FWUPD_DEVICE_FLAG_IS_BOOTLOADER); | |
446 | fu_device_add_icon (FU_DEVICE (self), "preferences-desktop-keyboard"); | |
447 | fu_device_set_name (FU_DEVICE (self), "Unifying Receiver"); | |
448 | fu_device_set_summary (FU_DEVICE (self), "A miniaturised USB wireless receiver (bootloader)"); | |
449 | fu_device_set_version_format (FU_DEVICE (self), FWUPD_VERSION_FORMAT_PLAIN); | |
450 | fu_device_set_remove_delay (FU_DEVICE (self), FU_UNIFYING_DEVICE_TIMEOUT_MS); | |
451 | } | |
452 | ||
453 | static void | |
454 | fu_unifying_bootloader_class_init (FuUnifyingBootloaderClass *klass) | |
455 | { | |
456 | FuDeviceClass *klass_device = FU_DEVICE_CLASS (klass); | |
457 | FuUsbDeviceClass *klass_usb_device = FU_USB_DEVICE_CLASS (klass); | |
458 | klass_device->to_string = fu_unifying_bootloader_to_string; | |
459 | klass_device->attach = fu_unifying_bootloader_attach; | |
460 | klass_device->setup = fu_unifying_bootloader_setup; | |
461 | klass_usb_device->open = fu_unifying_bootloader_open; | |
462 | klass_usb_device->close = fu_unifying_bootloader_close; | |
463 | } |
0 | /* | |
1 | * Copyright (C) 2016-2018 Richard Hughes <richard@hughsie.com> | |
2 | * | |
3 | * SPDX-License-Identifier: LGPL-2.1+ | |
4 | */ | |
5 | ||
6 | #pragma once | |
7 | ||
8 | #include "fu-usb-device.h" | |
9 | ||
10 | #define FU_TYPE_UNIFYING_BOOTLOADER (fu_unifying_bootloader_get_type ()) | |
11 | G_DECLARE_DERIVABLE_TYPE (FuUnifyingBootloader, fu_unifying_bootloader, FU, UNIFYING_BOOTLOADER, FuUsbDevice) | |
12 | ||
13 | struct _FuUnifyingBootloaderClass | |
14 | { | |
15 | FuUsbDeviceClass parent_class; | |
16 | gboolean (*setup) (FuUnifyingBootloader *self, | |
17 | GError **error); | |
18 | }; | |
19 | ||
20 | typedef enum { | |
21 | FU_UNIFYING_BOOTLOADER_CMD_GENERAL_ERROR = 0x01, | |
22 | FU_UNIFYING_BOOTLOADER_CMD_READ = 0x10, | |
23 | FU_UNIFYING_BOOTLOADER_CMD_WRITE = 0x20, | |
24 | FU_UNIFYING_BOOTLOADER_CMD_WRITE_INVALID_ADDR = 0x21, | |
25 | FU_UNIFYING_BOOTLOADER_CMD_WRITE_VERIFY_FAIL = 0x22, | |
26 | FU_UNIFYING_BOOTLOADER_CMD_WRITE_NONZERO_START = 0x23, | |
27 | FU_UNIFYING_BOOTLOADER_CMD_WRITE_INVALID_CRC = 0x24, | |
28 | FU_UNIFYING_BOOTLOADER_CMD_ERASE_PAGE = 0x30, | |
29 | FU_UNIFYING_BOOTLOADER_CMD_ERASE_PAGE_INVALID_ADDR = 0x31, | |
30 | FU_UNIFYING_BOOTLOADER_CMD_ERASE_PAGE_NONZERO_START = 0x33, | |
31 | FU_UNIFYING_BOOTLOADER_CMD_GET_HW_PLATFORM_ID = 0x40, | |
32 | FU_UNIFYING_BOOTLOADER_CMD_GET_FW_VERSION = 0x50, | |
33 | FU_UNIFYING_BOOTLOADER_CMD_GET_CHECKSUM = 0x60, | |
34 | FU_UNIFYING_BOOTLOADER_CMD_REBOOT = 0x70, | |
35 | FU_UNIFYING_BOOTLOADER_CMD_GET_MEMINFO = 0x80, | |
36 | FU_UNIFYING_BOOTLOADER_CMD_GET_BL_VERSION = 0x90, | |
37 | FU_UNIFYING_BOOTLOADER_CMD_GET_INIT_FW_VERSION = 0xa0, | |
38 | FU_UNIFYING_BOOTLOADER_CMD_READ_SIGNATURE = 0xb0, | |
39 | FU_UNIFYING_BOOTLOADER_CMD_WRITE_RAM_BUFFER = 0xc0, | |
40 | FU_UNIFYING_BOOTLOADER_CMD_WRITE_RAM_BUFFER_INVALID_ADDR= 0xc1, | |
41 | FU_UNIFYING_BOOTLOADER_CMD_WRITE_RAM_BUFFER_OVERFLOW = 0xc2, | |
42 | FU_UNIFYING_BOOTLOADER_CMD_FLASH_RAM = 0xd0, | |
43 | FU_UNIFYING_BOOTLOADER_CMD_FLASH_RAM_INVALID_ADDR = 0xd1, | |
44 | FU_UNIFYING_BOOTLOADER_CMD_FLASH_RAM_WRONG_CRC = 0xd2, | |
45 | FU_UNIFYING_BOOTLOADER_CMD_FLASH_RAM_PAGE0_INVALID = 0xd3, | |
46 | FU_UNIFYING_BOOTLOADER_CMD_FLASH_RAM_INVALID_ORDER = 0xd4, | |
47 | FU_UNIFYING_BOOTLOADER_CMD_WRITE_SIGNATURE = 0xe0, | |
48 | FU_UNIFYING_BOOTLOADER_CMD_LAST | |
49 | } FuUnifyingBootloaderCmd; | |
50 | ||
51 | /* packet to and from device */ | |
52 | typedef struct __attribute__((packed)) { | |
53 | guint8 cmd; | |
54 | guint16 addr; | |
55 | guint8 len; | |
56 | guint8 data[28]; | |
57 | } FuUnifyingBootloaderRequest; | |
58 | ||
59 | FuUnifyingBootloaderRequest *fu_unifying_bootloader_request_new (void); | |
60 | ||
61 | #pragma clang diagnostic push | |
62 | #pragma clang diagnostic ignored "-Wunused-function" | |
63 | G_DEFINE_AUTOPTR_CLEANUP_FUNC(FuUnifyingBootloaderRequest, g_free); | |
64 | #pragma clang diagnostic pop | |
65 | ||
66 | GPtrArray *fu_unifying_bootloader_parse_requests (FuUnifyingBootloader *self, | |
67 | GBytes *fw, | |
68 | GError **error); | |
69 | gboolean fu_unifying_bootloader_request (FuUnifyingBootloader *self, | |
70 | FuUnifyingBootloaderRequest *req, | |
71 | GError **error); | |
72 | ||
73 | guint16 fu_unifying_bootloader_get_addr_lo (FuUnifyingBootloader *self); | |
74 | guint16 fu_unifying_bootloader_get_addr_hi (FuUnifyingBootloader *self); | |
75 | guint16 fu_unifying_bootloader_get_blocksize (FuUnifyingBootloader *self); |
0 | /* | |
1 | * Copyright (C) 2016-2018 Richard Hughes <richard@hughsie.com> | |
2 | * | |
3 | * SPDX-License-Identifier: LGPL-2.1+ | |
4 | */ | |
5 | ||
6 | #include "config.h" | |
7 | ||
8 | #include <string.h> | |
9 | #include <fcntl.h> | |
10 | #include <errno.h> | |
11 | #include <gio/gio.h> | |
12 | ||
13 | #include "fu-unifying-common.h" | |
14 | ||
15 | guint8 | |
16 | fu_unifying_buffer_read_uint8 (const gchar *str) | |
17 | { | |
18 | guint64 tmp; | |
19 | gchar buf[3] = { 0x0, 0x0, 0x0 }; | |
20 | memcpy (buf, str, 2); | |
21 | tmp = g_ascii_strtoull (buf, NULL, 16); | |
22 | return tmp; | |
23 | } | |
24 | ||
25 | guint16 | |
26 | fu_unifying_buffer_read_uint16 (const gchar *str) | |
27 | { | |
28 | guint64 tmp; | |
29 | gchar buf[5] = { 0x0, 0x0, 0x0, 0x0, 0x0 }; | |
30 | memcpy (buf, str, 4); | |
31 | tmp = g_ascii_strtoull (buf, NULL, 16); | |
32 | return tmp; | |
33 | } | |
34 | ||
35 | gchar * | |
36 | fu_unifying_format_version (const gchar *name, guint8 major, guint8 minor, guint16 build) | |
37 | { | |
38 | GString *str = g_string_new (NULL); | |
39 | for (guint i = 0; i < 3; i++) { | |
40 | if (g_ascii_isspace (name[i])) | |
41 | continue; | |
42 | g_string_append_c (str, name[i]); | |
43 | } | |
44 | g_string_append_printf (str, "%02x.%02x_B%04x", major, minor, build); | |
45 | return g_string_free (str, FALSE); | |
46 | } |
0 | /* | |
1 | * Copyright (C) 2016-2018 Richard Hughes <richard@hughsie.com> | |
2 | * | |
3 | * SPDX-License-Identifier: LGPL-2.1+ | |
4 | */ | |
5 | ||
6 | #pragma once | |
7 | ||
8 | #include <glib.h> | |
9 | ||
10 | #define FU_UNIFYING_DEVICE_VID 0x046d | |
11 | ||
12 | #define FU_UNIFYING_DEVICE_PID_RUNTIME 0xc52b | |
13 | #define FU_UNIFYING_DEVICE_PID_BOOTLOADER_NORDIC 0xaaaa | |
14 | #define FU_UNIFYING_DEVICE_PID_BOOTLOADER_NORDIC_PICO 0xaaae | |
15 | #define FU_UNIFYING_DEVICE_PID_BOOTLOADER_TEXAS 0xaaac | |
16 | #define FU_UNIFYING_DEVICE_PID_BOOTLOADER_TEXAS_PICO 0xaaad | |
17 | ||
18 | /* Signed firmware are very long to verify on the device */ | |
19 | #define FU_UNIFYING_DEVICE_TIMEOUT_MS 30000 | |
20 | ||
21 | guint8 fu_unifying_buffer_read_uint8 (const gchar *str); | |
22 | guint16 fu_unifying_buffer_read_uint16 (const gchar *str); | |
23 | ||
24 | gchar *fu_unifying_format_version (const gchar *name, | |
25 | guint8 major, | |
26 | guint8 minor, | |
27 | guint16 build); |
0 | /* | |
1 | * Copyright (C) 2017-2018 Richard Hughes <richard@hughsie.com> | |
2 | * | |
3 | * SPDX-License-Identifier: LGPL-2.1+ | |
4 | */ | |
5 | ||
6 | #include "config.h" | |
7 | ||
8 | #include <string.h> | |
9 | ||
10 | #include "fu-unifying-hidpp.h" | |
11 | #include "fu-unifying-hidpp-msg.h" | |
12 | ||
13 | FuUnifyingHidppMsg * | |
14 | fu_unifying_hidpp_msg_new (void) | |
15 | { | |
16 | return g_new0 (FuUnifyingHidppMsg, 1); | |
17 | } | |
18 | ||
19 | const gchar * | |
20 | fu_unifying_hidpp_msg_dev_id_to_string (FuUnifyingHidppMsg *msg) | |
21 | { | |
22 | g_return_val_if_fail (msg != NULL, NULL); | |
23 | if (msg->device_id == HIDPP_DEVICE_ID_WIRED) | |
24 | return "wired"; | |
25 | if (msg->device_id == HIDPP_DEVICE_ID_RECEIVER) | |
26 | return "receiver"; | |
27 | if (msg->device_id == HIDPP_DEVICE_ID_UNSET) | |
28 | return "unset"; | |
29 | return NULL; | |
30 | } | |
31 | ||
32 | const gchar * | |
33 | fu_unifying_hidpp_msg_rpt_id_to_string (FuUnifyingHidppMsg *msg) | |
34 | { | |
35 | g_return_val_if_fail (msg != NULL, NULL); | |
36 | if (msg->report_id == HIDPP_REPORT_ID_SHORT) | |
37 | return "short"; | |
38 | if (msg->report_id == HIDPP_REPORT_ID_LONG) | |
39 | return "long"; | |
40 | if (msg->report_id == HIDPP_REPORT_ID_VERY_LONG) | |
41 | return "very-long"; | |
42 | return NULL; | |
43 | } | |
44 | ||
45 | gsize | |
46 | fu_unifying_hidpp_msg_get_payload_length (FuUnifyingHidppMsg *msg) | |
47 | { | |
48 | if (msg->report_id == HIDPP_REPORT_ID_SHORT) | |
49 | return 0x07; | |
50 | if (msg->report_id == HIDPP_REPORT_ID_LONG) | |
51 | return 0x14; | |
52 | if (msg->report_id == HIDPP_REPORT_ID_VERY_LONG) | |
53 | return 0x2f; | |
54 | if (msg->report_id == HIDPP_REPORT_NOTIFICATION) | |
55 | return 0x08; | |
56 | return 0x0; | |
57 | } | |
58 | ||
59 | const gchar * | |
60 | fu_unifying_hidpp_msg_fcn_id_to_string (FuUnifyingHidppMsg *msg) | |
61 | { | |
62 | g_return_val_if_fail (msg != NULL, NULL); | |
63 | switch (msg->sub_id) { | |
64 | case HIDPP_SUBID_SET_REGISTER: | |
65 | case HIDPP_SUBID_GET_REGISTER: | |
66 | case HIDPP_SUBID_SET_LONG_REGISTER: | |
67 | case HIDPP_SUBID_GET_LONG_REGISTER: | |
68 | case HIDPP_SUBID_SET_VERY_LONG_REGISTER: | |
69 | case HIDPP_SUBID_GET_VERY_LONG_REGISTER: | |
70 | if (msg->function_id == HIDPP_REGISTER_HIDPP_NOTIFICATIONS) | |
71 | return "hidpp-notifications"; | |
72 | if (msg->function_id == HIDPP_REGISTER_ENABLE_INDIVIDUAL_FEATURES) | |
73 | return "individual-features"; | |
74 | if (msg->function_id == HIDPP_REGISTER_BATTERY_STATUS) | |
75 | return "battery-status"; | |
76 | if (msg->function_id == HIDPP_REGISTER_BATTERY_MILEAGE) | |
77 | return "battery-mileage"; | |
78 | if (msg->function_id == HIDPP_REGISTER_PROFILE) | |
79 | return "profile"; | |
80 | if (msg->function_id == HIDPP_REGISTER_LED_STATUS) | |
81 | return "led-status"; | |
82 | if (msg->function_id == HIDPP_REGISTER_LED_INTENSITY) | |
83 | return "led-intensity"; | |
84 | if (msg->function_id == HIDPP_REGISTER_LED_COLOR) | |
85 | return "led-color"; | |
86 | if (msg->function_id == HIDPP_REGISTER_OPTICAL_SENSOR_SETTINGS) | |
87 | return "optical-sensor-settings"; | |
88 | if (msg->function_id == HIDPP_REGISTER_CURRENT_RESOLUTION) | |
89 | return "current-resolution"; | |
90 | if (msg->function_id == HIDPP_REGISTER_USB_REFRESH_RATE) | |
91 | return "usb-refresh-rate"; | |
92 | if (msg->function_id == HIDPP_REGISTER_GENERIC_MEMORY_MANAGEMENT) | |
93 | return "generic-memory-management"; | |
94 | if (msg->function_id == HIDPP_REGISTER_HOT_CONTROL) | |
95 | return "hot-control"; | |
96 | if (msg->function_id == HIDPP_REGISTER_READ_MEMORY) | |
97 | return "read-memory"; | |
98 | if (msg->function_id == HIDPP_REGISTER_DEVICE_CONNECTION_DISCONNECTION) | |
99 | return "device-connection-disconnection"; | |
100 | if (msg->function_id == HIDPP_REGISTER_PAIRING_INFORMATION) | |
101 | return "pairing-information"; | |
102 | if (msg->function_id == HIDPP_REGISTER_DEVICE_FIRMWARE_UPDATE_MODE) | |
103 | return "device-firmware-update-mode"; | |
104 | if (msg->function_id == HIDPP_REGISTER_DEVICE_FIRMWARE_INFORMATION) | |
105 | return "device-firmware-information"; | |
106 | break; | |
107 | default: | |
108 | break; | |
109 | } | |
110 | return NULL; | |
111 | ||
112 | } | |
113 | ||
114 | const gchar * | |
115 | fu_unifying_hidpp_msg_sub_id_to_string (FuUnifyingHidppMsg *msg) | |
116 | { | |
117 | g_return_val_if_fail (msg != NULL, NULL); | |
118 | if (msg->sub_id == HIDPP_SUBID_VENDOR_SPECIFIC_KEYS) | |
119 | return "vendor-specific-keys"; | |
120 | if (msg->sub_id == HIDPP_SUBID_POWER_KEYS) | |
121 | return "power-keys"; | |
122 | if (msg->sub_id == HIDPP_SUBID_ROLLER) | |
123 | return "roller"; | |
124 | if (msg->sub_id == HIDPP_SUBID_MOUSE_EXTRA_BUTTONS) | |
125 | return "mouse-extra-buttons"; | |
126 | if (msg->sub_id == HIDPP_SUBID_BATTERY_CHARGING_LEVEL) | |
127 | return "battery-charging-level"; | |
128 | if (msg->sub_id == HIDPP_SUBID_USER_INTERFACE_EVENT) | |
129 | return "user-interface-event"; | |
130 | if (msg->sub_id == HIDPP_SUBID_F_LOCK_STATUS) | |
131 | return "f-lock-status"; | |
132 | if (msg->sub_id == HIDPP_SUBID_CALCULATOR_RESULT) | |
133 | return "calculator-result"; | |
134 | if (msg->sub_id == HIDPP_SUBID_MENU_NAVIGATE) | |
135 | return "menu-navigate"; | |
136 | if (msg->sub_id == HIDPP_SUBID_FN_KEY) | |
137 | return "fn-key"; | |
138 | if (msg->sub_id == HIDPP_SUBID_BATTERY_MILEAGE) | |
139 | return "battery-mileage"; | |
140 | if (msg->sub_id == HIDPP_SUBID_UART_RX) | |
141 | return "uart-rx"; | |
142 | if (msg->sub_id == HIDPP_SUBID_BACKLIGHT_DURATION_UPDATE) | |
143 | return "backlight-duration-update"; | |
144 | if (msg->sub_id == HIDPP_SUBID_DEVICE_DISCONNECTION) | |
145 | return "device-disconnection"; | |
146 | if (msg->sub_id == HIDPP_SUBID_DEVICE_CONNECTION) | |
147 | return "device-connection"; | |
148 | if (msg->sub_id == HIDPP_SUBID_DEVICE_DISCOVERY) | |
149 | return "device-discovery"; | |
150 | if (msg->sub_id == HIDPP_SUBID_PIN_CODE_REQUEST) | |
151 | return "pin-code-request"; | |
152 | if (msg->sub_id == HIDPP_SUBID_RECEIVER_WORKING_MODE) | |
153 | return "receiver-working-mode"; | |
154 | if (msg->sub_id == HIDPP_SUBID_ERROR_MESSAGE) | |
155 | return "error-message"; | |
156 | if (msg->sub_id == HIDPP_SUBID_RF_LINK_CHANGE) | |
157 | return "rf-link-change"; | |
158 | if (msg->sub_id == HIDPP_SUBID_HCI) | |
159 | return "hci"; | |
160 | if (msg->sub_id == HIDPP_SUBID_LINK_QUALITY) | |
161 | return "link-quality"; | |
162 | if (msg->sub_id == HIDPP_SUBID_DEVICE_LOCKING_CHANGED) | |
163 | return "device-locking-changed"; | |
164 | if (msg->sub_id == HIDPP_SUBID_WIRELESS_DEVICE_CHANGE) | |
165 | return "wireless-device-change"; | |
166 | if (msg->sub_id == HIDPP_SUBID_ACL) | |
167 | return "acl"; | |
168 | if (msg->sub_id == HIDPP_SUBID_VOIP_TELEPHONY_EVENT) | |
169 | return "voip-telephony-event"; | |
170 | if (msg->sub_id == HIDPP_SUBID_LED) | |
171 | return "led"; | |
172 | if (msg->sub_id == HIDPP_SUBID_GESTURE_AND_AIR) | |
173 | return "gesture-and-air"; | |
174 | if (msg->sub_id == HIDPP_SUBID_TOUCHPAD_MULTI_TOUCH) | |
175 | return "touchpad-multi-touch"; | |
176 | if (msg->sub_id == HIDPP_SUBID_TRACEABILITY) | |
177 | return "traceability"; | |
178 | if (msg->sub_id == HIDPP_SUBID_SET_REGISTER) | |
179 | return "set-register"; | |
180 | if (msg->sub_id == HIDPP_SUBID_GET_REGISTER) | |
181 | return "get-register"; | |
182 | if (msg->sub_id == HIDPP_SUBID_SET_LONG_REGISTER) | |
183 | return "set-long-register"; | |
184 | if (msg->sub_id == HIDPP_SUBID_GET_LONG_REGISTER) | |
185 | return "get-long-register"; | |
186 | if (msg->sub_id == HIDPP_SUBID_SET_VERY_LONG_REGISTER) | |
187 | return "set-very-long-register"; | |
188 | if (msg->sub_id == HIDPP_SUBID_GET_VERY_LONG_REGISTER) | |
189 | return "get-very-long-register"; | |
190 | if (msg->sub_id == HIDPP_SUBID_ERROR_MSG) | |
191 | return "error-msg"; | |
192 | if (msg->sub_id == HIDPP_SUBID_ERROR_MSG_20) | |
193 | return "error-msg-v2"; | |
194 | return NULL; | |
195 | } | |
196 | ||
197 | gboolean | |
198 | fu_unifying_hidpp_msg_is_reply (FuUnifyingHidppMsg *msg1, FuUnifyingHidppMsg *msg2) | |
199 | { | |
200 | g_return_val_if_fail (msg1 != NULL, FALSE); | |
201 | g_return_val_if_fail (msg2 != NULL, FALSE); | |
202 | if (msg1->device_id != msg2->device_id && | |
203 | msg1->device_id != HIDPP_DEVICE_ID_UNSET && | |
204 | msg2->device_id != HIDPP_DEVICE_ID_UNSET) | |
205 | return FALSE; | |
206 | if (msg1->flags & FU_UNIFYING_HIDPP_MSG_FLAG_IGNORE_SUB_ID || | |
207 | msg2->flags & FU_UNIFYING_HIDPP_MSG_FLAG_IGNORE_SUB_ID) | |
208 | return TRUE; | |
209 | if (msg1->sub_id != msg2->sub_id) | |
210 | return FALSE; | |
211 | if (msg1->flags & FU_UNIFYING_HIDPP_MSG_FLAG_IGNORE_FNCT_ID || | |
212 | msg2->flags & FU_UNIFYING_HIDPP_MSG_FLAG_IGNORE_FNCT_ID) | |
213 | return TRUE; | |
214 | if (msg1->function_id != msg2->function_id) | |
215 | return FALSE; | |
216 | return TRUE; | |
217 | } | |
218 | ||
219 | /* HID++ error */ | |
220 | gboolean | |
221 | fu_unifying_hidpp_msg_is_error (FuUnifyingHidppMsg *msg, GError **error) | |
222 | { | |
223 | g_return_val_if_fail (msg != NULL, FALSE); | |
224 | if (msg->sub_id == HIDPP_SUBID_ERROR_MSG) { | |
225 | switch (msg->data[1]) { | |
226 | case HIDPP_ERR_INVALID_SUBID: | |
227 | g_set_error_literal (error, | |
228 | G_IO_ERROR, | |
229 | G_IO_ERROR_NOT_SUPPORTED, | |
230 | "invalid SubID"); | |
231 | break; | |
232 | case HIDPP_ERR_INVALID_ADDRESS: | |
233 | g_set_error_literal (error, | |
234 | G_IO_ERROR, | |
235 | G_IO_ERROR_INVALID_DATA, | |
236 | "invalid address"); | |
237 | break; | |
238 | case HIDPP_ERR_INVALID_VALUE: | |
239 | g_set_error_literal (error, | |
240 | G_IO_ERROR, | |
241 | G_IO_ERROR_INVALID_DATA, | |
242 | "invalid value"); | |
243 | break; | |
244 | case HIDPP_ERR_CONNECT_FAIL: | |
245 | g_set_error_literal (error, | |
246 | G_IO_ERROR, | |
247 | G_IO_ERROR_FAILED, | |
248 | "connection request failed"); | |
249 | break; | |
250 | case HIDPP_ERR_TOO_MANY_DEVICES: | |
251 | g_set_error_literal (error, | |
252 | G_IO_ERROR, | |
253 | G_IO_ERROR_NO_SPACE, | |
254 | "too many devices connected"); | |
255 | break; | |
256 | case HIDPP_ERR_ALREADY_EXISTS: | |
257 | g_set_error_literal (error, | |
258 | G_IO_ERROR, | |
259 | G_IO_ERROR_EXISTS, | |
260 | "already exists"); | |
261 | break; | |
262 | case HIDPP_ERR_BUSY: | |
263 | g_set_error_literal (error, | |
264 | G_IO_ERROR, | |
265 | G_IO_ERROR_BUSY, | |
266 | "busy"); | |
267 | break; | |
268 | case HIDPP_ERR_UNKNOWN_DEVICE: | |
269 | g_set_error_literal (error, | |
270 | G_IO_ERROR, | |
271 | G_IO_ERROR_NOT_FOUND, | |
272 | "unknown device"); | |
273 | break; | |
274 | case HIDPP_ERR_RESOURCE_ERROR: | |
275 | g_set_error_literal (error, | |
276 | G_IO_ERROR, | |
277 | G_IO_ERROR_HOST_UNREACHABLE, | |
278 | "resource error"); | |
279 | break; | |
280 | case HIDPP_ERR_REQUEST_UNAVAILABLE: | |
281 | g_set_error_literal (error, | |
282 | G_IO_ERROR, | |
283 | G_IO_ERROR_EXISTS, | |
284 | "request not valid in current context"); | |
285 | break; | |
286 | case HIDPP_ERR_INVALID_PARAM_VALUE: | |
287 | g_set_error_literal (error, | |
288 | G_IO_ERROR, | |
289 | G_IO_ERROR_INVALID_DATA, | |
290 | "request parameter has unsupported value"); | |
291 | break; | |
292 | case HIDPP_ERR_WRONG_PIN_CODE: | |
293 | g_set_error_literal (error, | |
294 | G_IO_ERROR, | |
295 | G_IO_ERROR_CONNECTION_REFUSED, | |
296 | "the pin code was wrong"); | |
297 | break; | |
298 | default: | |
299 | g_set_error_literal (error, | |
300 | G_IO_ERROR, | |
301 | G_IO_ERROR_FAILED, | |
302 | "generic failure"); | |
303 | } | |
304 | return FALSE; | |
305 | } | |
306 | if (msg->sub_id == HIDPP_SUBID_ERROR_MSG_20) { | |
307 | switch (msg->data[1]) { | |
308 | case HIDPP_ERROR_CODE_INVALID_ARGUMENT: | |
309 | g_set_error (error, | |
310 | G_IO_ERROR, | |
311 | G_IO_ERROR_INVALID_ARGUMENT, | |
312 | "Invalid argument 0x%02x", | |
313 | msg->data[2]); | |
314 | break; | |
315 | case HIDPP_ERROR_CODE_OUT_OF_RANGE: | |
316 | g_set_error_literal (error, | |
317 | G_IO_ERROR, | |
318 | G_IO_ERROR_INVALID_DATA, | |
319 | "out of range"); | |
320 | break; | |
321 | case HIDPP_ERROR_CODE_HW_ERROR: | |
322 | g_set_error_literal (error, | |
323 | G_IO_ERROR, | |
324 | G_IO_ERROR_BROKEN_PIPE, | |
325 | "hardware error"); | |
326 | break; | |
327 | case HIDPP_ERROR_CODE_INVALID_FEATURE_INDEX: | |
328 | g_set_error_literal (error, | |
329 | G_IO_ERROR, | |
330 | G_IO_ERROR_INVALID_ARGUMENT, | |
331 | "invalid feature index"); | |
332 | break; | |
333 | case HIDPP_ERROR_CODE_INVALID_FUNCTION_ID: | |
334 | g_set_error_literal (error, | |
335 | G_IO_ERROR, | |
336 | G_IO_ERROR_INVALID_ARGUMENT, | |
337 | "invalid function ID"); | |
338 | break; | |
339 | case HIDPP_ERROR_CODE_BUSY: | |
340 | g_set_error_literal (error, | |
341 | G_IO_ERROR, | |
342 | G_IO_ERROR_BUSY, | |
343 | "busy"); | |
344 | break; | |
345 | case HIDPP_ERROR_CODE_UNSUPPORTED: | |
346 | g_set_error_literal (error, | |
347 | G_IO_ERROR, | |
348 | G_IO_ERROR_NOT_SUPPORTED, | |
349 | "unsupported"); | |
350 | break; | |
351 | default: | |
352 | g_set_error_literal (error, | |
353 | G_IO_ERROR, | |
354 | G_IO_ERROR_FAILED, | |
355 | "generic failure"); | |
356 | break; | |
357 | } | |
358 | return FALSE; | |
359 | } | |
360 | return TRUE; | |
361 | } | |
362 | ||
363 | void | |
364 | fu_unifying_hidpp_msg_copy (FuUnifyingHidppMsg *msg_dst, const FuUnifyingHidppMsg *msg_src) | |
365 | { | |
366 | g_return_if_fail (msg_dst != NULL); | |
367 | g_return_if_fail (msg_src != NULL); | |
368 | memset (msg_dst->data, 0x00, sizeof(msg_dst->data)); | |
369 | msg_dst->device_id = msg_src->device_id; | |
370 | msg_dst->sub_id = msg_src->sub_id; | |
371 | msg_dst->function_id = msg_src->function_id; | |
372 | memcpy (msg_dst->data, msg_src->data, sizeof(msg_dst->data)); | |
373 | } | |
374 | ||
375 | /* filter HID++1.0 messages */ | |
376 | gboolean | |
377 | fu_unifying_hidpp_msg_is_hidpp10_compat (FuUnifyingHidppMsg *msg) | |
378 | { | |
379 | g_return_val_if_fail (msg != NULL, FALSE); | |
380 | if (msg->sub_id == 0x40 || | |
381 | msg->sub_id == 0x41 || | |
382 | msg->sub_id == 0x49 || | |
383 | msg->sub_id == 0x4b || | |
384 | msg->sub_id == 0x8f) { | |
385 | return TRUE; | |
386 | } | |
387 | return FALSE; | |
388 | } | |
389 | ||
390 | gboolean | |
391 | fu_unifying_hidpp_msg_verify_swid (FuUnifyingHidppMsg *msg) | |
392 | { | |
393 | g_return_val_if_fail (msg != NULL, FALSE); | |
394 | if ((msg->function_id & 0x0f) != FU_UNIFYING_HIDPP_MSG_SW_ID) | |
395 | return FALSE; | |
396 | return TRUE; | |
397 | } |
0 | /* | |
1 | * Copyright (C) 2017-2018 Richard Hughes <richard@hughsie.com> | |
2 | * | |
3 | * SPDX-License-Identifier: LGPL-2.1+ | |
4 | */ | |
5 | ||
6 | #pragma once | |
7 | ||
8 | #include <glib.h> | |
9 | ||
10 | typedef enum { | |
11 | FU_UNIFYING_HIDPP_MSG_FLAG_NONE, | |
12 | FU_UNIFYING_HIDPP_MSG_FLAG_LONGER_TIMEOUT = 1 << 0, | |
13 | FU_UNIFYING_HIDPP_MSG_FLAG_IGNORE_SUB_ID = 1 << 1, | |
14 | FU_UNIFYING_HIDPP_MSG_FLAG_IGNORE_FNCT_ID = 1 << 2, | |
15 | FU_UNIFYING_HIDPP_MSG_FLAG_IGNORE_SWID = 1 << 3, | |
16 | /*< private >*/ | |
17 | FU_UNIFYING_HIDPP_MSG_FLAG_LAST | |
18 | } FuUnifyingHidppMsgFlags; | |
19 | ||
20 | typedef struct __attribute__((packed)) { | |
21 | guint8 report_id; | |
22 | guint8 device_id; | |
23 | guint8 sub_id; | |
24 | guint8 function_id; /* funcId:software_id */ | |
25 | guint8 data[47]; /* maximum supported by Windows XP SP2 */ | |
26 | /* not included in the packet sent to the hardware */ | |
27 | guint32 flags; | |
28 | guint8 hidpp_version; | |
29 | } FuUnifyingHidppMsg; | |
30 | ||
31 | /* this is specific to fwupd */ | |
32 | #define FU_UNIFYING_HIDPP_MSG_SW_ID 0x07 | |
33 | ||
34 | #pragma clang diagnostic push | |
35 | #pragma clang diagnostic ignored "-Wunused-function" | |
36 | G_DEFINE_AUTOPTR_CLEANUP_FUNC(FuUnifyingHidppMsg, g_free); | |
37 | #pragma clang diagnostic pop | |
38 | ||
39 | FuUnifyingHidppMsg *fu_unifying_hidpp_msg_new (void); | |
40 | void fu_unifying_hidpp_msg_copy (FuUnifyingHidppMsg *msg_dst, | |
41 | const FuUnifyingHidppMsg *msg_src); | |
42 | gsize fu_unifying_hidpp_msg_get_payload_length (FuUnifyingHidppMsg *msg); | |
43 | gboolean fu_unifying_hidpp_msg_is_reply (FuUnifyingHidppMsg *msg1, | |
44 | FuUnifyingHidppMsg *msg2); | |
45 | gboolean fu_unifying_hidpp_msg_is_hidpp10_compat (FuUnifyingHidppMsg *msg); | |
46 | gboolean fu_unifying_hidpp_msg_is_error (FuUnifyingHidppMsg *msg, | |
47 | GError **error); | |
48 | gboolean fu_unifying_hidpp_msg_verify_swid (FuUnifyingHidppMsg *msg); | |
49 | ||
50 | const gchar *fu_unifying_hidpp_msg_dev_id_to_string (FuUnifyingHidppMsg *msg); | |
51 | const gchar *fu_unifying_hidpp_msg_rpt_id_to_string (FuUnifyingHidppMsg *msg); | |
52 | const gchar *fu_unifying_hidpp_msg_sub_id_to_string (FuUnifyingHidppMsg *msg); | |
53 | const gchar *fu_unifying_hidpp_msg_fcn_id_to_string (FuUnifyingHidppMsg *msg); |
0 | /* | |
1 | * Copyright (C) 2016-2018 Richard Hughes <richard@hughsie.com> | |
2 | * | |
3 | * SPDX-License-Identifier: LGPL-2.1+ | |
4 | */ | |
5 | ||
6 | #include "config.h" | |
7 | ||
8 | #include "fu-common.h" | |
9 | #include "fu-unifying-common.h" | |
10 | #include "fu-unifying-hidpp.h" | |
11 | ||
12 | static gchar * | |
13 | fu_unifying_hidpp_msg_to_string (FuUnifyingHidppMsg *msg) | |
14 | { | |
15 | GString *str = g_string_new (NULL); | |
16 | const gchar *tmp; | |
17 | g_autoptr(GError) error = NULL; | |
18 | g_autoptr(GString) flags_str = g_string_new (NULL); | |
19 | ||
20 | g_return_val_if_fail (msg != NULL, NULL); | |
21 | ||
22 | if (msg->flags == FU_UNIFYING_HIDPP_MSG_FLAG_NONE) { | |
23 | g_string_append (flags_str, "none"); | |
24 | } else { | |
25 | if (msg->flags & FU_UNIFYING_HIDPP_MSG_FLAG_LONGER_TIMEOUT) | |
26 | g_string_append (flags_str, "longer-timeout,"); | |
27 | if (msg->flags & FU_UNIFYING_HIDPP_MSG_FLAG_IGNORE_SUB_ID) | |
28 | g_string_append (flags_str, "ignore-sub-id,"); | |
29 | if (msg->flags & FU_UNIFYING_HIDPP_MSG_FLAG_IGNORE_FNCT_ID) | |
30 | g_string_append (flags_str, "ignore-fnct-id,"); | |
31 | if (msg->flags & FU_UNIFYING_HIDPP_MSG_FLAG_IGNORE_SWID) | |
32 | g_string_append (flags_str, "ignore-swid,"); | |
33 | if (str->len > 0) | |
34 | g_string_truncate (str, str->len - 1); | |
35 | } | |
36 | g_string_append_printf (str, "flags: %02x [%s]\n", | |
37 | msg->flags, | |
38 | flags_str->str); | |
39 | g_string_append_printf (str, "report-id: %02x [%s]\n", | |
40 | msg->report_id, | |
41 | fu_unifying_hidpp_msg_rpt_id_to_string (msg)); | |
42 | tmp = fu_unifying_hidpp_msg_dev_id_to_string (msg); | |
43 | g_string_append_printf (str, "device-id: %02x [%s]\n", | |
44 | msg->device_id, tmp ); | |
45 | g_string_append_printf (str, "sub-id: %02x [%s]\n", | |
46 | msg->sub_id, | |
47 | fu_unifying_hidpp_msg_sub_id_to_string (msg)); | |
48 | g_string_append_printf (str, "function-id: %02x [%s]\n", | |
49 | msg->function_id, | |
50 | fu_unifying_hidpp_msg_fcn_id_to_string (msg)); | |
51 | if (!fu_unifying_hidpp_msg_is_error (msg, &error)) { | |
52 | g_string_append_printf (str, "error: %s\n", | |
53 | error->message); | |
54 | } | |
55 | return g_string_free (str, FALSE); | |
56 | } | |
57 | ||
58 | gboolean | |
59 | fu_unifying_hidpp_send (FuIOChannel *io_channel, | |
60 | FuUnifyingHidppMsg *msg, | |
61 | guint timeout, | |
62 | GError **error) | |
63 | { | |
64 | gsize len = fu_unifying_hidpp_msg_get_payload_length (msg); | |
65 | ||
66 | /* only for HID++2.0 */ | |
67 | if (msg->hidpp_version >= 2.f) | |
68 | msg->function_id |= FU_UNIFYING_HIDPP_MSG_SW_ID; | |
69 | ||
70 | /* detailed debugging */ | |
71 | if (g_getenv ("FWUPD_UNIFYING_VERBOSE") != NULL) { | |
72 | g_autofree gchar *str = fu_unifying_hidpp_msg_to_string (msg); | |
73 | fu_common_dump_raw (G_LOG_DOMAIN, "host->device", (guint8 *) msg, len); | |
74 | g_print ("%s", str); | |
75 | } | |
76 | ||
77 | /* HID */ | |
78 | if (!fu_io_channel_write_raw (io_channel, (guint8 *) msg, len, 1500, | |
79 | FU_IO_CHANNEL_FLAG_FLUSH_INPUT | | |
80 | FU_IO_CHANNEL_FLAG_USE_BLOCKING_IO, error)) { | |
81 | g_prefix_error (error, "failed to send: "); | |
82 | return FALSE; | |
83 | } | |
84 | ||
85 | /* success */ | |
86 | return TRUE; | |
87 | } | |
88 | ||
89 | gboolean | |
90 | fu_unifying_hidpp_receive (FuIOChannel *io_channel, | |
91 | FuUnifyingHidppMsg *msg, | |
92 | guint timeout, | |
93 | GError **error) | |
94 | { | |
95 | gsize read_size = 0; | |
96 | ||
97 | if (!fu_io_channel_read_raw (io_channel, | |
98 | (guint8 *) msg, | |
99 | sizeof(FuUnifyingHidppMsg), | |
100 | &read_size, | |
101 | timeout, | |
102 | FU_IO_CHANNEL_FLAG_SINGLE_SHOT, | |
103 | error)) { | |
104 | g_prefix_error (error, "failed to receive: "); | |
105 | return FALSE; | |
106 | } | |
107 | ||
108 | /* check long enough, but allow returning oversize packets */ | |
109 | if (g_getenv ("FWUPD_UNIFYING_VERBOSE") != NULL) | |
110 | fu_common_dump_raw (G_LOG_DOMAIN, "device->host", (guint8 *) msg, read_size); | |
111 | if (read_size < fu_unifying_hidpp_msg_get_payload_length (msg)) { | |
112 | g_set_error (error, | |
113 | G_IO_ERROR, | |
114 | G_IO_ERROR_FAILED, | |
115 | "message length too small, " | |
116 | "got %" G_GSIZE_FORMAT " expected %" G_GSIZE_FORMAT, | |
117 | read_size, fu_unifying_hidpp_msg_get_payload_length (msg)); | |
118 | return FALSE; | |
119 | } | |
120 | ||
121 | /* detailed debugging */ | |
122 | if (g_getenv ("FWUPD_UNIFYING_VERBOSE") != NULL) { | |
123 | g_autofree gchar *str = fu_unifying_hidpp_msg_to_string (msg); | |
124 | g_print ("%s", str); | |
125 | } | |
126 | ||
127 | /* success */ | |
128 | return TRUE; | |
129 | } | |
130 | ||
131 | gboolean | |
132 | fu_unifying_hidpp_transfer (FuIOChannel *io_channel, FuUnifyingHidppMsg *msg, GError **error) | |
133 | { | |
134 | guint timeout = FU_UNIFYING_DEVICE_TIMEOUT_MS; | |
135 | guint ignore_cnt = 0; | |
136 | g_autoptr(FuUnifyingHidppMsg) msg_tmp = fu_unifying_hidpp_msg_new (); | |
137 | ||
138 | /* increase timeout for some operations */ | |
139 | if (msg->flags & FU_UNIFYING_HIDPP_MSG_FLAG_LONGER_TIMEOUT) | |
140 | timeout *= 10; | |
141 | ||
142 | /* send request */ | |
143 | if (!fu_unifying_hidpp_send (io_channel, msg, timeout, error)) | |
144 | return FALSE; | |
145 | ||
146 | /* keep trying to receive until we get a valid reply */ | |
147 | while (1) { | |
148 | msg_tmp->hidpp_version = msg->hidpp_version; | |
149 | if (!fu_unifying_hidpp_receive (io_channel, msg_tmp, timeout, error)) { | |
150 | g_prefix_error (error, "failed to receive: "); | |
151 | return FALSE; | |
152 | } | |
153 | ||
154 | /* we don't know how to handle this report packet */ | |
155 | if (fu_unifying_hidpp_msg_get_payload_length (msg_tmp) == 0x0) { | |
156 | g_debug ("HID++1.0 report 0x%02x has unknown length, ignoring", | |
157 | msg_tmp->report_id); | |
158 | continue; | |
159 | } | |
160 | ||
161 | /* maybe something is also writing to the device? -- | |
162 | * we can't use the SwID as this is a HID++2.0 feature */ | |
163 | if (!fu_unifying_hidpp_msg_is_error (msg_tmp, error)) | |
164 | return FALSE; | |
165 | ||
166 | /* is valid reply */ | |
167 | if (fu_unifying_hidpp_msg_is_reply (msg, msg_tmp)) | |
168 | break; | |
169 | ||
170 | /* to ensure compatibility when an HID++ 2.0 device is | |
171 | * connected to an HID++ 1.0 receiver, any feature index | |
172 | * corresponding to an HID++ 1.0 sub-identifier which could be | |
173 | * sent by the receiver, must be assigned to a dummy feature */ | |
174 | if (msg->hidpp_version >= 2.f) { | |
175 | if (fu_unifying_hidpp_msg_is_hidpp10_compat (msg_tmp)) { | |
176 | g_debug ("ignoring HID++1.0 reply"); | |
177 | continue; | |
178 | } | |
179 | ||
180 | /* not us */ | |
181 | if ((msg->flags & FU_UNIFYING_HIDPP_MSG_FLAG_IGNORE_SWID) == 0) { | |
182 | if (!fu_unifying_hidpp_msg_verify_swid (msg_tmp)) { | |
183 | g_debug ("ignoring reply with SwId 0x%02i, expected 0x%02i", | |
184 | msg_tmp->function_id & 0x0f, | |
185 | FU_UNIFYING_HIDPP_MSG_SW_ID); | |
186 | continue; | |
187 | } | |
188 | } | |
189 | } | |
190 | ||
191 | /* hardware not responding */ | |
192 | if (ignore_cnt++ > 10) { | |
193 | g_set_error (error, | |
194 | G_IO_ERROR, | |
195 | G_IO_ERROR_FAILED, | |
196 | "too many messages to ignore"); | |
197 | return FALSE; | |
198 | } | |
199 | ||
200 | g_debug ("ignoring message %u", ignore_cnt); | |
201 | }; | |
202 | ||
203 | /* copy over data */ | |
204 | fu_unifying_hidpp_msg_copy (msg, msg_tmp); | |
205 | return TRUE; | |
206 | } |
0 | /* | |
1 | * Copyright (C) 2016-2018 Richard Hughes <richard@hughsie.com> | |
2 | * | |
3 | * SPDX-License-Identifier: LGPL-2.1+ | |
4 | */ | |
5 | ||
6 | #pragma once | |
7 | ||
8 | #include <gio/gio.h> | |
9 | ||
10 | #include "fu-io-channel.h" | |
11 | ||
12 | /* | |
13 | * Based on the HID++ documentation provided by Nestor Lopez Casado at: | |
14 | * https://drive.google.com/folderview?id=0BxbRzx7vEV7eWmgwazJ3NUFfQ28&usp=sharing | |
15 | */ | |
16 | #define HIDPP_DEVICE_ID_WIRED 0x00 | |
17 | #define HIDPP_DEVICE_ID_RECEIVER 0xFF | |
18 | #define HIDPP_DEVICE_ID_UNSET 0xFE | |
19 | ||
20 | #define HIDPP_REPORT_NOTIFICATION 0x01 | |
21 | #define HIDPP_REPORT_ID_SHORT 0x10 | |
22 | #define HIDPP_REPORT_ID_LONG 0x11 | |
23 | #define HIDPP_REPORT_ID_VERY_LONG 0x12 | |
24 | ||
25 | #define HIDPP_SUBID_VENDOR_SPECIFIC_KEYS 0x03 | |
26 | #define HIDPP_SUBID_POWER_KEYS 0x04 | |
27 | #define HIDPP_SUBID_ROLLER 0x05 | |
28 | #define HIDPP_SUBID_MOUSE_EXTRA_BUTTONS 0x06 | |
29 | #define HIDPP_SUBID_BATTERY_CHARGING_LEVEL 0x07 | |
30 | #define HIDPP_SUBID_USER_INTERFACE_EVENT 0x08 | |
31 | #define HIDPP_SUBID_F_LOCK_STATUS 0x09 | |
32 | #define HIDPP_SUBID_CALCULATOR_RESULT 0x0A | |
33 | #define HIDPP_SUBID_MENU_NAVIGATE 0x0B | |
34 | #define HIDPP_SUBID_FN_KEY 0x0C | |
35 | #define HIDPP_SUBID_BATTERY_MILEAGE 0x0D | |
36 | #define HIDPP_SUBID_UART_RX 0x0E | |
37 | #define HIDPP_SUBID_BACKLIGHT_DURATION_UPDATE 0x17 | |
38 | #define HIDPP_SUBID_DEVICE_DISCONNECTION 0x40 | |
39 | #define HIDPP_SUBID_DEVICE_CONNECTION 0x41 | |
40 | #define HIDPP_SUBID_DEVICE_DISCOVERY 0x42 | |
41 | #define HIDPP_SUBID_PIN_CODE_REQUEST 0x43 | |
42 | #define HIDPP_SUBID_RECEIVER_WORKING_MODE 0x44 | |
43 | #define HIDPP_SUBID_ERROR_MESSAGE 0x45 | |
44 | #define HIDPP_SUBID_RF_LINK_CHANGE 0x46 | |
45 | #define HIDPP_SUBID_HCI 0x48 | |
46 | #define HIDPP_SUBID_LINK_QUALITY 0x49 | |
47 | #define HIDPP_SUBID_DEVICE_LOCKING_CHANGED 0x4a | |
48 | #define HIDPP_SUBID_WIRELESS_DEVICE_CHANGE 0x4B | |
49 | #define HIDPP_SUBID_ACL 0x51 | |
50 | #define HIDPP_SUBID_VOIP_TELEPHONY_EVENT 0x5B | |
51 | #define HIDPP_SUBID_LED 0x60 | |
52 | #define HIDPP_SUBID_GESTURE_AND_AIR 0x65 | |
53 | #define HIDPP_SUBID_TOUCHPAD_MULTI_TOUCH 0x66 | |
54 | #define HIDPP_SUBID_TRACEABILITY 0x78 | |
55 | #define HIDPP_SUBID_SET_REGISTER 0x80 | |
56 | #define HIDPP_SUBID_GET_REGISTER 0x81 | |
57 | #define HIDPP_SUBID_SET_LONG_REGISTER 0x82 | |
58 | #define HIDPP_SUBID_GET_LONG_REGISTER 0x83 | |
59 | #define HIDPP_SUBID_SET_VERY_LONG_REGISTER 0x84 | |
60 | #define HIDPP_SUBID_GET_VERY_LONG_REGISTER 0x85 | |
61 | #define HIDPP_SUBID_ERROR_MSG 0x8F | |
62 | #define HIDPP_SUBID_ERROR_MSG_20 0xFF | |
63 | ||
64 | #define HIDPP_ERR_SUCCESS 0x00 | |
65 | #define HIDPP_ERR_INVALID_SUBID 0x01 | |
66 | #define HIDPP_ERR_INVALID_ADDRESS 0x02 | |
67 | #define HIDPP_ERR_INVALID_VALUE 0x03 | |
68 | #define HIDPP_ERR_CONNECT_FAIL 0x04 | |
69 | #define HIDPP_ERR_TOO_MANY_DEVICES 0x05 | |
70 | #define HIDPP_ERR_ALREADY_EXISTS 0x06 | |
71 | #define HIDPP_ERR_BUSY 0x07 | |
72 | #define HIDPP_ERR_UNKNOWN_DEVICE 0x08 | |
73 | #define HIDPP_ERR_RESOURCE_ERROR 0x09 | |
74 | #define HIDPP_ERR_REQUEST_UNAVAILABLE 0x0A | |
75 | #define HIDPP_ERR_INVALID_PARAM_VALUE 0x0B | |
76 | #define HIDPP_ERR_WRONG_PIN_CODE 0x0C | |
77 | ||
78 | /* | |
79 | * HID++1.0 registers | |
80 | */ | |
81 | ||
82 | #define HIDPP_REGISTER_HIDPP_NOTIFICATIONS 0x00 | |
83 | #define HIDPP_REGISTER_ENABLE_INDIVIDUAL_FEATURES 0x01 | |
84 | #define HIDPP_REGISTER_BATTERY_STATUS 0x07 | |
85 | #define HIDPP_REGISTER_BATTERY_MILEAGE 0x0D | |
86 | #define HIDPP_REGISTER_PROFILE 0x0F | |
87 | #define HIDPP_REGISTER_LED_STATUS 0x51 | |
88 | #define HIDPP_REGISTER_LED_INTENSITY 0x54 | |
89 | #define HIDPP_REGISTER_LED_COLOR 0x57 | |
90 | #define HIDPP_REGISTER_OPTICAL_SENSOR_SETTINGS 0x61 | |
91 | #define HIDPP_REGISTER_CURRENT_RESOLUTION 0x63 | |
92 | #define HIDPP_REGISTER_USB_REFRESH_RATE 0x64 | |
93 | #define HIDPP_REGISTER_GENERIC_MEMORY_MANAGEMENT 0xA0 | |
94 | #define HIDPP_REGISTER_HOT_CONTROL 0xA1 | |
95 | #define HIDPP_REGISTER_READ_MEMORY 0xA2 | |
96 | #define HIDPP_REGISTER_DEVICE_CONNECTION_DISCONNECTION 0xB2 | |
97 | #define HIDPP_REGISTER_PAIRING_INFORMATION 0xB5 | |
98 | #define HIDPP_REGISTER_DEVICE_FIRMWARE_UPDATE_MODE 0xF0 | |
99 | #define HIDPP_REGISTER_DEVICE_FIRMWARE_INFORMATION 0xF1 | |
100 | ||
101 | /* | |
102 | * HID++2.0 error codes | |
103 | */ | |
104 | #define HIDPP_ERROR_CODE_NO_ERROR 0x00 | |
105 | #define HIDPP_ERROR_CODE_UNKNOWN 0x01 | |
106 | #define HIDPP_ERROR_CODE_INVALID_ARGUMENT 0x02 | |
107 | #define HIDPP_ERROR_CODE_OUT_OF_RANGE 0x03 | |
108 | #define HIDPP_ERROR_CODE_HW_ERROR 0x04 | |
109 | #define HIDPP_ERROR_CODE_LOGITECH_INTERNAL 0x05 | |
110 | #define HIDPP_ERROR_CODE_INVALID_FEATURE_INDEX 0x06 | |
111 | #define HIDPP_ERROR_CODE_INVALID_FUNCTION_ID 0x07 | |
112 | #define HIDPP_ERROR_CODE_BUSY 0x08 | |
113 | #define HIDPP_ERROR_CODE_UNSUPPORTED 0x09 | |
114 | ||
115 | /* | |
116 | * HID++2.0 features | |
117 | */ | |
118 | #define HIDPP_FEATURE_ROOT 0x0000 | |
119 | #define HIDPP_FEATURE_I_FEATURE_SET 0x0001 | |
120 | #define HIDPP_FEATURE_I_FIRMWARE_INFO 0x0003 | |
121 | #define HIDPP_FEATURE_GET_DEVICE_NAME_TYPE 0x0005 | |
122 | #define HIDPP_FEATURE_DFU_CONTROL 0x00c1 | |
123 | #define HIDPP_FEATURE_DFU_CONTROL_SIGNED 0x00c2 | |
124 | #define HIDPP_FEATURE_DFU 0x00d0 | |
125 | #define HIDPP_FEATURE_BATTERY_LEVEL_STATUS 0x1000 | |
126 | #define HIDPP_FEATURE_KBD_REPROGRAMMABLE_KEYS 0x1b00 | |
127 | #define HIDPP_FEATURE_SPECIAL_KEYS_BUTTONS 0x1b04 | |
128 | #define HIDPP_FEATURE_MOUSE_POINTER_BASIC 0x2200 | |
129 | #define HIDPP_FEATURE_ADJUSTABLE_DPI 0x2201 | |
130 | #define HIDPP_FEATURE_ADJUSTABLE_REPORT_RATE 0x8060 | |
131 | #define HIDPP_FEATURE_COLOR_LED_EFFECTS 0x8070 | |
132 | #define HIDPP_FEATURE_ONBOARD_PROFILES 0x8100 | |
133 | #define HIDPP_FEATURE_MOUSE_BUTTON_SPY 0x8110 | |
134 | ||
135 | #include "fu-unifying-hidpp-msg.h" | |
136 | ||
137 | gboolean fu_unifying_hidpp_send (FuIOChannel *self, | |
138 | FuUnifyingHidppMsg *msg, | |
139 | guint timeout, | |
140 | GError **error); | |
141 | gboolean fu_unifying_hidpp_receive (FuIOChannel *self, | |
142 | FuUnifyingHidppMsg *msg, | |
143 | guint timeout, | |
144 | GError **error); | |
145 | gboolean fu_unifying_hidpp_transfer (FuIOChannel *self, | |
146 | FuUnifyingHidppMsg *msg, | |
147 | GError **error); |
0 | /* | |
1 | * Copyright (C) 2017-2018 Richard Hughes <richard@hughsie.com> | |
2 | * | |
3 | * SPDX-License-Identifier: LGPL-2.1+ | |
4 | */ | |
5 | ||
6 | #include "config.h" | |
7 | ||
8 | #include <string.h> | |
9 | ||
10 | #include "fu-unifying-common.h" | |
11 | #include "fu-unifying-peripheral.h" | |
12 | #include "fu-unifying-hidpp.h" | |
13 | ||
14 | struct _FuUnifyingPeripheral | |
15 | { | |
16 | FuUdevDevice parent_instance; | |
17 | guint8 battery_level; | |
18 | guint8 cached_fw_entity; | |
19 | guint8 hidpp_id; | |
20 | guint8 hidpp_version; | |
21 | gboolean is_updatable; | |
22 | gboolean is_active; | |
23 | FuIOChannel *io_channel; | |
24 | GPtrArray *feature_index; /* of FuUnifyingHidppMap */ | |
25 | }; | |
26 | ||
27 | typedef struct { | |
28 | guint8 idx; | |
29 | guint16 feature; | |
30 | } FuUnifyingHidppMap; | |
31 | ||
32 | G_DEFINE_TYPE (FuUnifyingPeripheral, fu_unifying_peripheral, FU_TYPE_UDEV_DEVICE) | |
33 | ||
34 | typedef enum { | |
35 | FU_UNIFYING_PERIPHERAL_KIND_KEYBOARD, | |
36 | FU_UNIFYING_PERIPHERAL_KIND_REMOTE_CONTROL, | |
37 | FU_UNIFYING_PERIPHERAL_KIND_NUMPAD, | |
38 | FU_UNIFYING_PERIPHERAL_KIND_MOUSE, | |
39 | FU_UNIFYING_PERIPHERAL_KIND_TOUCHPAD, | |
40 | FU_UNIFYING_PERIPHERAL_KIND_TRACKBALL, | |
41 | FU_UNIFYING_PERIPHERAL_KIND_PRESENTER, | |
42 | FU_UNIFYING_PERIPHERAL_KIND_RECEIVER, | |
43 | FU_UNIFYING_PERIPHERAL_KIND_LAST | |
44 | } FuUnifyingPeripheralKind; | |
45 | ||
46 | static const gchar * | |
47 | fu_unifying_peripheral_get_icon (FuUnifyingPeripheralKind kind) | |
48 | { | |
49 | if (kind == FU_UNIFYING_PERIPHERAL_KIND_KEYBOARD) | |
50 | return "input-keyboard"; | |
51 | if (kind == FU_UNIFYING_PERIPHERAL_KIND_REMOTE_CONTROL) | |
52 | return "pda"; // ish | |
53 | if (kind == FU_UNIFYING_PERIPHERAL_KIND_NUMPAD) | |
54 | return "input-dialpad"; | |
55 | if (kind == FU_UNIFYING_PERIPHERAL_KIND_MOUSE) | |
56 | return "input-mouse"; | |
57 | if (kind == FU_UNIFYING_PERIPHERAL_KIND_TOUCHPAD) | |
58 | return "input-touchpad"; | |
59 | if (kind == FU_UNIFYING_PERIPHERAL_KIND_TRACKBALL) | |
60 | return "input-mouse"; // ish | |
61 | if (kind == FU_UNIFYING_PERIPHERAL_KIND_PRESENTER) | |
62 | return "pda"; // ish | |
63 | if (kind == FU_UNIFYING_PERIPHERAL_KIND_RECEIVER) | |
64 | return "preferences-desktop-keyboard"; | |
65 | return NULL; | |
66 | } | |
67 | ||
68 | static const gchar * | |
69 | fu_unifying_peripheral_get_summary (FuUnifyingPeripheralKind kind) | |
70 | { | |
71 | if (kind == FU_UNIFYING_PERIPHERAL_KIND_KEYBOARD) | |
72 | return "Unifying Keyboard"; | |
73 | if (kind == FU_UNIFYING_PERIPHERAL_KIND_REMOTE_CONTROL) | |
74 | return "Unifying Remote Control"; | |
75 | if (kind == FU_UNIFYING_PERIPHERAL_KIND_NUMPAD) | |
76 | return "Unifying Number Pad"; | |
77 | if (kind == FU_UNIFYING_PERIPHERAL_KIND_MOUSE) | |
78 | return "Unifying Mouse"; | |
79 | if (kind == FU_UNIFYING_PERIPHERAL_KIND_TOUCHPAD) | |
80 | return "Unifying Touchpad"; | |
81 | if (kind == FU_UNIFYING_PERIPHERAL_KIND_TRACKBALL) | |
82 | return "Unifying Trackball"; | |
83 | if (kind == FU_UNIFYING_PERIPHERAL_KIND_PRESENTER) | |
84 | return "Unifying Presenter"; | |
85 | if (kind == FU_UNIFYING_PERIPHERAL_KIND_RECEIVER) | |
86 | return "Unifying Receiver"; | |
87 | return NULL; | |
88 | } | |
89 | ||
90 | static const gchar * | |
91 | fu_unifying_hidpp_feature_to_string (guint16 feature) | |
92 | { | |
93 | if (feature == HIDPP_FEATURE_ROOT) | |
94 | return "Root"; | |
95 | if (feature == HIDPP_FEATURE_I_FIRMWARE_INFO) | |
96 | return "IFirmwareInfo"; | |
97 | if (feature == HIDPP_FEATURE_GET_DEVICE_NAME_TYPE) | |
98 | return "GetDevicenameType"; | |
99 | if (feature == HIDPP_FEATURE_BATTERY_LEVEL_STATUS) | |
100 | return "BatteryLevelStatus"; | |
101 | if (feature == HIDPP_FEATURE_DFU_CONTROL) | |
102 | return "DfuControl"; | |
103 | if (feature == HIDPP_FEATURE_DFU_CONTROL_SIGNED) | |
104 | return "DfuControlSigned"; | |
105 | if (feature == HIDPP_FEATURE_DFU) | |
106 | return "Dfu"; | |
107 | return NULL; | |
108 | } | |
109 | ||
110 | static void | |
111 | fu_unifying_peripheral_refresh_updatable (FuUnifyingPeripheral *self) | |
112 | { | |
113 | /* device can only be upgraded if it is capable, and active */ | |
114 | if (self->is_updatable && self->is_active) { | |
115 | fu_device_add_flag (FU_DEVICE (self), FWUPD_DEVICE_FLAG_UPDATABLE); | |
116 | return; | |
117 | } | |
118 | fu_device_remove_flag (FU_DEVICE (self), FWUPD_DEVICE_FLAG_UPDATABLE); | |
119 | } | |
120 | ||
121 | static gboolean | |
122 | fu_unifying_peripheral_ping (FuUnifyingPeripheral *self, GError **error) | |
123 | { | |
124 | gdouble version; | |
125 | g_autoptr(GError) error_local = NULL; | |
126 | g_autoptr(FuUnifyingHidppMsg) msg = fu_unifying_hidpp_msg_new (); | |
127 | ||
128 | /* handle failure */ | |
129 | msg->report_id = HIDPP_REPORT_ID_SHORT; | |
130 | msg->device_id = self->hidpp_id; | |
131 | msg->sub_id = 0x00; /* rootIndex */ | |
132 | msg->function_id = 0x01 << 4; /* ping */ | |
133 | msg->data[0] = 0x00; | |
134 | msg->data[1] = 0x00; | |
135 | msg->data[2] = 0xaa; /* user-selected value */ | |
136 | msg->hidpp_version = self->hidpp_version; | |
137 | if (!fu_unifying_hidpp_transfer (self->io_channel, msg, &error_local)) { | |
138 | if (g_error_matches (error_local, | |
139 | G_IO_ERROR, | |
140 | G_IO_ERROR_NOT_SUPPORTED)) { | |
141 | self->hidpp_version = 1; | |
142 | return TRUE; | |
143 | } | |
144 | if (g_error_matches (error_local, | |
145 | G_IO_ERROR, | |
146 | G_IO_ERROR_HOST_UNREACHABLE)) { | |
147 | self->is_active = FALSE; | |
148 | fu_unifying_peripheral_refresh_updatable (self); | |
149 | return TRUE; | |
150 | } | |
151 | g_set_error (error, | |
152 | G_IO_ERROR, | |
153 | G_IO_ERROR_FAILED, | |
154 | "failed to ping %s: %s", | |
155 | fu_device_get_name (FU_DEVICE (self)), | |
156 | error_local->message); | |
157 | return FALSE; | |
158 | } | |
159 | ||
160 | /* device no longer asleep */ | |
161 | self->is_active = TRUE; | |
162 | fu_unifying_peripheral_refresh_updatable (self); | |
163 | ||
164 | /* if the HID++ ID is unset, grab it from the reply */ | |
165 | if (self->hidpp_id == HIDPP_DEVICE_ID_UNSET) { | |
166 | self->hidpp_id = msg->device_id; | |
167 | g_debug ("HID++ ID is %02x", self->hidpp_id); | |
168 | } | |
169 | ||
170 | /* format version in BCD format */ | |
171 | version = (gdouble) msg->data[0] + ((gdouble) msg->data[1]) / 100.f; | |
172 | self->hidpp_version = (guint) version; | |
173 | ||
174 | /* success */ | |
175 | return TRUE; | |
176 | } | |
177 | ||
178 | static gboolean | |
179 | fu_unifying_peripheral_close (FuDevice *device, GError **error) | |
180 | { | |
181 | FuUnifyingPeripheral *self = FU_UNIFYING_PERIPHERAL (device); | |
182 | if (!fu_io_channel_shutdown (self->io_channel, error)) | |
183 | return FALSE; | |
184 | g_clear_object (&self->io_channel); | |
185 | return TRUE; | |
186 | } | |
187 | ||
188 | static gboolean | |
189 | fu_unifying_peripheral_poll (FuDevice *device, GError **error) | |
190 | { | |
191 | FuUnifyingPeripheral *self = FU_UNIFYING_PERIPHERAL (device); | |
192 | const guint timeout = 1; /* ms */ | |
193 | g_autoptr(GError) error_local = NULL; | |
194 | g_autoptr(FuUnifyingHidppMsg) msg = fu_unifying_hidpp_msg_new (); | |
195 | g_autoptr(FuDeviceLocker) locker = NULL; | |
196 | ||
197 | /* open */ | |
198 | locker = fu_device_locker_new (self, error); | |
199 | if (locker == NULL) | |
200 | return FALSE; | |
201 | ||
202 | /* flush pending data */ | |
203 | msg->device_id = self->hidpp_id; | |
204 | msg->hidpp_version = self->hidpp_version; | |
205 | if (!fu_unifying_hidpp_receive (self->io_channel, msg, timeout, &error_local)) { | |
206 | if (!g_error_matches (error_local, | |
207 | G_IO_ERROR, | |
208 | G_IO_ERROR_TIMED_OUT)) { | |
209 | g_warning ("failed to get pending read: %s", error_local->message); | |
210 | return TRUE; | |
211 | } | |
212 | /* no data to receive */ | |
213 | g_clear_error (&error_local); | |
214 | } | |
215 | ||
216 | /* just ping */ | |
217 | if (!fu_unifying_peripheral_ping (self, &error_local)) { | |
218 | g_warning ("failed to ping device: %s", error_local->message); | |
219 | return TRUE; | |
220 | } | |
221 | ||
222 | /* this is the first time the device has been active */ | |
223 | if (self->feature_index->len == 0) { | |
224 | fu_device_probe_invalidate (FU_DEVICE (self)); | |
225 | if (!fu_device_setup (FU_DEVICE (self), error)) | |
226 | return FALSE; | |
227 | } | |
228 | ||
229 | /* success */ | |
230 | return TRUE; | |
231 | } | |
232 | ||
233 | static gboolean | |
234 | fu_unifying_peripheral_open (FuDevice *device, GError **error) | |
235 | { | |
236 | FuUnifyingPeripheral *self = FU_UNIFYING_PERIPHERAL (device); | |
237 | GUdevDevice *udev_device = fu_udev_device_get_dev (FU_UDEV_DEVICE (device)); | |
238 | const gchar *devpath = g_udev_device_get_device_file (udev_device); | |
239 | ||
240 | /* open */ | |
241 | self->io_channel = fu_io_channel_new_file (devpath, error); | |
242 | if (self->io_channel == NULL) | |
243 | return FALSE; | |
244 | ||
245 | return TRUE; | |
246 | } | |
247 | ||
248 | static void | |
249 | fu_unifying_hidpp_map_to_string (FuUnifyingHidppMap *map, guint idt, GString *str) | |
250 | { | |
251 | g_autofree gchar *title = g_strdup_printf ("Feature%02x", map->idx); | |
252 | g_autofree gchar *tmp = g_strdup_printf ("%s [0x%04x]", | |
253 | fu_unifying_hidpp_feature_to_string (map->feature), | |
254 | map->feature); | |
255 | fu_common_string_append_kv (str, idt, title, tmp); | |
256 | } | |
257 | ||
258 | static void | |
259 | fu_unifying_peripheral_to_string (FuDevice *device, guint idt, GString *str) | |
260 | { | |
261 | FuUnifyingPeripheral *self = FU_UNIFYING_PERIPHERAL (device); | |
262 | fu_common_string_append_ku (str, idt, "HidppVersion", self->hidpp_version); | |
263 | fu_common_string_append_kx (str, idt, "HidppId", self->hidpp_id); | |
264 | fu_common_string_append_ku (str, idt, "BatteryLevel", self->battery_level); | |
265 | fu_common_string_append_kb (str, idt, "IsUpdatable", self->is_updatable); | |
266 | fu_common_string_append_kb (str, idt, "IsActive", self->is_active); | |
267 | for (guint i = 0; i < self->feature_index->len; i++) { | |
268 | FuUnifyingHidppMap *map = g_ptr_array_index (self->feature_index, i); | |
269 | fu_unifying_hidpp_map_to_string (map, idt, str); | |
270 | } | |
271 | } | |
272 | ||
273 | static guint8 | |
274 | fu_unifying_peripheral_feature_get_idx (FuUnifyingPeripheral *self, guint16 feature) | |
275 | { | |
276 | for (guint i = 0; i < self->feature_index->len; i++) { | |
277 | FuUnifyingHidppMap *map = g_ptr_array_index (self->feature_index, i); | |
278 | if (map->feature == feature) | |
279 | return map->idx; | |
280 | } | |
281 | return 0x00; | |
282 | } | |
283 | ||
284 | static gboolean | |
285 | fu_unifying_peripheral_fetch_firmware_info (FuUnifyingPeripheral *self, GError **error) | |
286 | { | |
287 | guint8 idx; | |
288 | guint8 entity_count; | |
289 | g_autoptr(FuUnifyingHidppMsg) msg = fu_unifying_hidpp_msg_new (); | |
290 | ||
291 | /* get the feature index */ | |
292 | idx = fu_unifying_peripheral_feature_get_idx (self, HIDPP_FEATURE_I_FIRMWARE_INFO); | |
293 | if (idx == 0x00) | |
294 | return TRUE; | |
295 | ||
296 | /* get the entity count */ | |
297 | msg->report_id = HIDPP_REPORT_ID_SHORT; | |
298 | msg->device_id = self->hidpp_id; | |
299 | msg->sub_id = idx; | |
300 | msg->function_id = 0x00 << 4; /* getCount */ | |
301 | msg->hidpp_version = self->hidpp_version; | |
302 | if (!fu_unifying_hidpp_transfer (self->io_channel, msg, error)) { | |
303 | g_prefix_error (error, "failed to get firmware count: "); | |
304 | return FALSE; | |
305 | } | |
306 | entity_count = msg->data[0]; | |
307 | g_debug ("firmware entity count is %u", entity_count); | |
308 | ||
309 | /* get firmware, bootloader, hardware versions */ | |
310 | for (guint8 i = 0; i < entity_count; i++) { | |
311 | guint16 build; | |
312 | g_autofree gchar *version = NULL; | |
313 | g_autofree gchar *name = NULL; | |
314 | ||
315 | msg->report_id = HIDPP_REPORT_ID_SHORT; | |
316 | msg->device_id = self->hidpp_id; | |
317 | msg->sub_id = idx; | |
318 | msg->function_id = 0x01 << 4; /* getInfo */ | |
319 | msg->data[0] = i; | |
320 | if (!fu_unifying_hidpp_transfer (self->io_channel, msg, error)) { | |
321 | g_prefix_error (error, "failed to get firmware info: "); | |
322 | return FALSE; | |
323 | } | |
324 | if (msg->data[1] == 0x00 && | |
325 | msg->data[2] == 0x00 && | |
326 | msg->data[3] == 0x00 && | |
327 | msg->data[4] == 0x00 && | |
328 | msg->data[5] == 0x00 && | |
329 | msg->data[6] == 0x00 && | |
330 | msg->data[7] == 0x00) { | |
331 | g_debug ("no version set for entity %u", i); | |
332 | continue; | |
333 | } | |
334 | name = g_strdup_printf ("%c%c%c", | |
335 | msg->data[1], | |
336 | msg->data[2], | |
337 | msg->data[3]); | |
338 | build = ((guint16) msg->data[6]) << 8 | msg->data[7]; | |
339 | version = fu_unifying_format_version (name, | |
340 | msg->data[4], | |
341 | msg->data[5], | |
342 | build); | |
343 | g_debug ("firmware entity 0x%02x version is %s", i, version); | |
344 | if (msg->data[0] == 0) { | |
345 | fu_device_set_version (FU_DEVICE (self), version, | |
346 | FWUPD_VERSION_FORMAT_PLAIN); | |
347 | self->cached_fw_entity = i; | |
348 | } else if (msg->data[0] == 1) { | |
349 | fu_device_set_version_bootloader (FU_DEVICE (self), version); | |
350 | } else if (msg->data[0] == 2) { | |
351 | fu_device_set_metadata (FU_DEVICE (self), "version-hw", version); | |
352 | } | |
353 | } | |
354 | ||
355 | /* not an error, the device just doesn't support this */ | |
356 | return TRUE; | |
357 | } | |
358 | ||
359 | static gboolean | |
360 | fu_unifying_peripheral_fetch_battery_level (FuUnifyingPeripheral *self, GError **error) | |
361 | { | |
362 | /* try using HID++2.0 */ | |
363 | if (self->hidpp_version >= 2.f) { | |
364 | guint8 idx; | |
365 | idx = fu_unifying_peripheral_feature_get_idx (self, HIDPP_FEATURE_BATTERY_LEVEL_STATUS); | |
366 | if (idx != 0x00) { | |
367 | g_autoptr(FuUnifyingHidppMsg) msg = fu_unifying_hidpp_msg_new (); | |
368 | msg->report_id = HIDPP_REPORT_ID_SHORT; | |
369 | msg->device_id = self->hidpp_id; | |
370 | msg->sub_id = idx; | |
371 | msg->function_id = 0x00; /* GetBatteryLevelStatus */ | |
372 | msg->hidpp_version = self->hidpp_version; | |
373 | if (!fu_unifying_hidpp_transfer (self->io_channel, msg, error)) { | |
374 | g_prefix_error (error, "failed to get battery info: "); | |
375 | return FALSE; | |
376 | } | |
377 | if (msg->data[0] != 0x00) | |
378 | self->battery_level = msg->data[0]; | |
379 | return TRUE; | |
380 | } | |
381 | } | |
382 | ||
383 | /* try HID++1.0 battery mileage */ | |
384 | if (self->hidpp_version == 1.f) { | |
385 | g_autoptr(FuUnifyingHidppMsg) msg = fu_unifying_hidpp_msg_new (); | |
386 | msg->report_id = HIDPP_REPORT_ID_SHORT; | |
387 | msg->device_id = self->hidpp_id; | |
388 | msg->sub_id = HIDPP_SUBID_GET_REGISTER; | |
389 | msg->function_id = HIDPP_REGISTER_BATTERY_MILEAGE; | |
390 | msg->hidpp_version = self->hidpp_version; | |
391 | if (fu_unifying_hidpp_transfer (self->io_channel, msg, NULL)) { | |
392 | if (msg->data[0] != 0x00) | |
393 | self->battery_level = msg->data[0]; | |
394 | return TRUE; | |
395 | } | |
396 | ||
397 | /* try HID++1.0 battery status instead */ | |
398 | msg->function_id = HIDPP_REGISTER_BATTERY_STATUS; | |
399 | if (fu_unifying_hidpp_transfer (self->io_channel, msg, NULL)) { | |
400 | switch (msg->data[0]) { | |
401 | case 1: /* 0 - 10 */ | |
402 | self->battery_level = 5; | |
403 | break; | |
404 | case 3: /* 11 - 30 */ | |
405 | self->battery_level = 20; | |
406 | break; | |
407 | case 5: /* 31 - 80 */ | |
408 | self->battery_level = 55; | |
409 | break; | |
410 | case 7: /* 81 - 100 */ | |
411 | self->battery_level = 90; | |
412 | break; | |
413 | default: | |
414 | g_warning ("unknown battery percentage: 0x%02x", | |
415 | msg->data[0]); | |
416 | break; | |
417 | } | |
418 | return TRUE; | |
419 | } | |
420 | } | |
421 | ||
422 | /* not an error, the device just doesn't support any of the methods */ | |
423 | return TRUE; | |
424 | } | |
425 | ||
426 | static gboolean | |
427 | fu_unifying_hidpp_feature_search (FuDevice *device, guint16 feature, GError **error) | |
428 | { | |
429 | FuUnifyingPeripheral *self = FU_UNIFYING_PERIPHERAL (device); | |
430 | FuUnifyingHidppMap *map; | |
431 | g_autoptr(FuUnifyingHidppMsg) msg = fu_unifying_hidpp_msg_new (); | |
432 | ||
433 | /* find the idx for the feature */ | |
434 | msg->report_id = HIDPP_REPORT_ID_SHORT; | |
435 | msg->device_id = self->hidpp_id; | |
436 | msg->sub_id = 0x00; /* rootIndex */ | |
437 | msg->function_id = 0x00 << 4; /* getFeature */ | |
438 | msg->data[0] = feature >> 8; | |
439 | msg->data[1] = feature; | |
440 | msg->data[2] = 0x00; | |
441 | msg->hidpp_version = self->hidpp_version; | |
442 | if (!fu_unifying_hidpp_transfer (self->io_channel, msg, error)) { | |
443 | g_prefix_error (error, | |
444 | "failed to get idx for feature %s [0x%04x]: ", | |
445 | fu_unifying_hidpp_feature_to_string (feature), feature); | |
446 | return FALSE; | |
447 | } | |
448 | ||
449 | /* zero index */ | |
450 | if (msg->data[0] == 0x00) { | |
451 | g_set_error (error, | |
452 | G_IO_ERROR, | |
453 | G_IO_ERROR_NOT_SUPPORTED, | |
454 | "feature %s [0x%04x] not found", | |
455 | fu_unifying_hidpp_feature_to_string (feature), feature); | |
456 | return FALSE; | |
457 | } | |
458 | ||
459 | /* add to map */ | |
460 | map = g_new0 (FuUnifyingHidppMap, 1); | |
461 | map->idx = msg->data[0]; | |
462 | map->feature = feature; | |
463 | g_ptr_array_add (self->feature_index, map); | |
464 | g_debug ("added feature %s [0x%04x] as idx %02x", | |
465 | fu_unifying_hidpp_feature_to_string (feature), feature, map->idx); | |
466 | return TRUE; | |
467 | } | |
468 | ||
469 | static gboolean | |
470 | fu_unifying_peripheral_probe (FuUdevDevice *device, GError **error) | |
471 | { | |
472 | g_autofree gchar *devid = NULL; | |
473 | ||
474 | /* set the physical ID */ | |
475 | if (!fu_udev_device_set_physical_id (device, "hid", error)) | |
476 | return FALSE; | |
477 | ||
478 | /* nearly... */ | |
479 | fu_device_set_vendor_id (FU_DEVICE (device), "USB:0x046D"); | |
480 | ||
481 | /* this is a non-standard extension */ | |
482 | devid = g_strdup_printf ("UFY\\VID_%04X&PID_%04X", | |
483 | fu_udev_device_get_vendor (device), | |
484 | fu_udev_device_get_model (device)); | |
485 | fu_device_add_instance_id (FU_DEVICE (device), devid); | |
486 | return TRUE; | |
487 | } | |
488 | ||
489 | static gboolean | |
490 | fu_unifying_peripheral_setup (FuDevice *device, GError **error) | |
491 | { | |
492 | FuUnifyingPeripheral *self = FU_UNIFYING_PERIPHERAL (device); | |
493 | guint8 idx; | |
494 | const guint16 map_features[] = { | |
495 | HIDPP_FEATURE_GET_DEVICE_NAME_TYPE, | |
496 | HIDPP_FEATURE_I_FIRMWARE_INFO, | |
497 | HIDPP_FEATURE_BATTERY_LEVEL_STATUS, | |
498 | HIDPP_FEATURE_DFU_CONTROL, | |
499 | HIDPP_FEATURE_DFU_CONTROL_SIGNED, | |
500 | HIDPP_FEATURE_DFU, | |
501 | HIDPP_FEATURE_ROOT }; | |
502 | ||
503 | /* ping device to get HID++ version */ | |
504 | if (!fu_unifying_peripheral_ping (self, error)) | |
505 | return FALSE; | |
506 | ||
507 | /* add known root for HID++2.0 */ | |
508 | g_ptr_array_set_size (self->feature_index, 0); | |
509 | if (self->hidpp_version >= 2.f) { | |
510 | FuUnifyingHidppMap *map = g_new0 (FuUnifyingHidppMap, 1); | |
511 | map->idx = 0x00; | |
512 | map->feature = HIDPP_FEATURE_ROOT; | |
513 | g_ptr_array_add (self->feature_index, map); | |
514 | } | |
515 | ||
516 | /* map some *optional* HID++2.0 features we might use */ | |
517 | for (guint i = 0; map_features[i] != HIDPP_FEATURE_ROOT; i++) { | |
518 | g_autoptr(GError) error_local = NULL; | |
519 | if (!fu_unifying_hidpp_feature_search (device, | |
520 | map_features[i], | |
521 | &error_local)) { | |
522 | g_debug ("%s", error_local->message); | |
523 | if (g_error_matches (error_local, | |
524 | G_IO_ERROR, | |
525 | G_IO_ERROR_TIMED_OUT) || | |
526 | g_error_matches (error_local, | |
527 | G_IO_ERROR, | |
528 | G_IO_ERROR_HOST_UNREACHABLE)) { | |
529 | /* timed out, so not trying any more */ | |
530 | break; | |
531 | } | |
532 | } | |
533 | } | |
534 | ||
535 | /* get the firmware information */ | |
536 | if (!fu_unifying_peripheral_fetch_firmware_info (self, error)) | |
537 | return FALSE; | |
538 | ||
539 | /* get the battery level */ | |
540 | if (!fu_unifying_peripheral_fetch_battery_level (self, error)) | |
541 | return FALSE; | |
542 | ||
543 | /* try using HID++2.0 */ | |
544 | idx = fu_unifying_peripheral_feature_get_idx (self, HIDPP_FEATURE_GET_DEVICE_NAME_TYPE); | |
545 | if (idx != 0x00) { | |
546 | const gchar *tmp; | |
547 | g_autoptr(FuUnifyingHidppMsg) msg = fu_unifying_hidpp_msg_new (); | |
548 | msg->report_id = HIDPP_REPORT_ID_SHORT; | |
549 | msg->device_id = self->hidpp_id; | |
550 | msg->sub_id = idx; | |
551 | msg->function_id = 0x02 << 4; /* getDeviceType */ | |
552 | msg->hidpp_version = self->hidpp_version; | |
553 | if (!fu_unifying_hidpp_transfer (self->io_channel, msg, error)) { | |
554 | g_prefix_error (error, "failed to get device type: "); | |
555 | return FALSE; | |
556 | } | |
557 | ||
558 | /* add nice-to-have data */ | |
559 | tmp = fu_unifying_peripheral_get_summary (msg->data[0]); | |
560 | if (tmp != NULL) | |
561 | fu_device_set_summary (FU_DEVICE (device), tmp); | |
562 | tmp = fu_unifying_peripheral_get_icon (msg->data[0]); | |
563 | if (tmp != NULL) | |
564 | fu_device_add_icon (FU_DEVICE (device), tmp); | |
565 | } | |
566 | idx = fu_unifying_peripheral_feature_get_idx (self, HIDPP_FEATURE_DFU_CONTROL); | |
567 | if (idx != 0x00) { | |
568 | self->is_updatable = TRUE; | |
569 | fu_device_remove_flag (FU_DEVICE (device), FWUPD_DEVICE_FLAG_IS_BOOTLOADER); | |
570 | } | |
571 | idx = fu_unifying_peripheral_feature_get_idx (self, HIDPP_FEATURE_DFU_CONTROL_SIGNED); | |
572 | if (idx != 0x00) { | |
573 | /* check the feature is available */ | |
574 | g_autoptr(FuUnifyingHidppMsg) msg = fu_unifying_hidpp_msg_new (); | |
575 | msg->report_id = HIDPP_REPORT_ID_SHORT; | |
576 | msg->device_id = self->hidpp_id; | |
577 | msg->sub_id = idx; | |
578 | msg->function_id = 0x00 << 4; /* getDfuStatus */ | |
579 | msg->hidpp_version = self->hidpp_version; | |
580 | if (!fu_unifying_hidpp_transfer (self->io_channel, msg, error)) { | |
581 | g_prefix_error (error, "failed to get DFU status: "); | |
582 | return FALSE; | |
583 | } | |
584 | if ((msg->data[2] & 0x01) > 0) { | |
585 | g_warning ("DFU mode not available"); | |
586 | } else { | |
587 | self->is_updatable = TRUE; | |
588 | fu_device_remove_flag (FU_DEVICE (device), FWUPD_DEVICE_FLAG_IS_BOOTLOADER); | |
589 | } | |
590 | } | |
591 | idx = fu_unifying_peripheral_feature_get_idx (self, HIDPP_FEATURE_DFU); | |
592 | if (idx != 0x00) { | |
593 | self->is_updatable = TRUE; | |
594 | fu_device_add_flag (FU_DEVICE (device), FWUPD_DEVICE_FLAG_IS_BOOTLOADER); | |
595 | if (fu_device_get_version (device) == NULL) { | |
596 | g_debug ("repairing device in bootloader mode"); | |
597 | fu_device_set_version (FU_DEVICE (device), | |
598 | "MPK00.00_B0000", | |
599 | FWUPD_VERSION_FORMAT_PLAIN); | |
600 | } | |
601 | } | |
602 | ||
603 | /* this device may have changed state */ | |
604 | fu_unifying_peripheral_refresh_updatable (self); | |
605 | ||
606 | /* poll for pings to track active state */ | |
607 | fu_device_set_poll_interval (device, 30000); | |
608 | return TRUE; | |
609 | } | |
610 | ||
611 | static gboolean | |
612 | fu_unifying_peripheral_detach (FuDevice *device, GError **error) | |
613 | { | |
614 | FuUnifyingPeripheral *self = FU_UNIFYING_PERIPHERAL (device); | |
615 | guint8 idx; | |
616 | g_autoptr(FuUnifyingHidppMsg) msg = fu_unifying_hidpp_msg_new (); | |
617 | ||
618 | /* this requires user action */ | |
619 | idx = fu_unifying_peripheral_feature_get_idx (self, HIDPP_FEATURE_DFU_CONTROL); | |
620 | if (idx != 0x00) { | |
621 | msg->report_id = HIDPP_REPORT_ID_LONG; | |
622 | msg->device_id = self->hidpp_id; | |
623 | msg->sub_id = idx; | |
624 | msg->function_id = 0x01 << 4; /* setDfuControl */ | |
625 | msg->data[0] = 0x01; /* enterDfu */ | |
626 | msg->data[1] = 0x00; /* dfuControlParam */ | |
627 | msg->data[2] = 0x00; /* unused */ | |
628 | msg->data[3] = 0x00; /* unused */ | |
629 | msg->data[4] = 'D'; | |
630 | msg->data[5] = 'F'; | |
631 | msg->data[6] = 'U'; | |
632 | msg->hidpp_version = self->hidpp_version; | |
633 | msg->flags = FU_UNIFYING_HIDPP_MSG_FLAG_IGNORE_SUB_ID | | |
634 | FU_UNIFYING_HIDPP_MSG_FLAG_LONGER_TIMEOUT; | |
635 | if (!fu_unifying_hidpp_transfer (self->io_channel, msg, error)) { | |
636 | g_prefix_error (error, "failed to put device into DFU mode: "); | |
637 | return FALSE; | |
638 | } | |
639 | fu_device_add_flag (device, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG); | |
640 | g_set_error (error, | |
641 | FWUPD_ERROR, | |
642 | FWUPD_ERROR_NEEDS_USER_ACTION, | |
643 | "%s needs to be manually restarted to complete the update." | |
644 | "Please unplug and reconnect the device and re-run the update", | |
645 | fu_device_get_name (device)); | |
646 | return FALSE; | |
647 | } | |
648 | ||
649 | /* this can reboot all by itself */ | |
650 | idx = fu_unifying_peripheral_feature_get_idx (self, HIDPP_FEATURE_DFU_CONTROL_SIGNED); | |
651 | if (idx != 0x00) { | |
652 | msg->report_id = HIDPP_REPORT_ID_LONG; | |
653 | msg->device_id = self->hidpp_id; | |
654 | msg->sub_id = idx; | |
655 | msg->function_id = 0x01 << 4; /* setDfuControl */ | |
656 | msg->data[0] = 0x01; /* startDfu */ | |
657 | msg->data[1] = 0x00; /* dfuControlParam */ | |
658 | msg->data[2] = 0x00; /* unused */ | |
659 | msg->data[3] = 0x00; /* unused */ | |
660 | msg->data[4] = 'D'; | |
661 | msg->data[5] = 'F'; | |
662 | msg->data[6] = 'U'; | |
663 | msg->flags = FU_UNIFYING_HIDPP_MSG_FLAG_IGNORE_SUB_ID; | |
664 | if (!fu_unifying_hidpp_transfer (self->io_channel, msg, error)) { | |
665 | g_prefix_error (error, "failed to put device into DFU mode: "); | |
666 | return FALSE; | |
667 | } | |
668 | return fu_unifying_peripheral_setup (FU_DEVICE (self), error); | |
669 | } | |
670 | ||
671 | /* we don't know how */ | |
672 | g_set_error (error, | |
673 | G_IO_ERROR, | |
674 | G_IO_ERROR_FAILED, | |
675 | "no method to detach"); | |
676 | return FALSE; | |
677 | } | |
678 | ||
679 | static gboolean | |
680 | fu_unifying_peripheral_check_status (guint8 status, GError **error) | |
681 | { | |
682 | switch (status & 0x7f) { | |
683 | case 0x00: | |
684 | g_set_error (error, | |
685 | G_IO_ERROR, | |
686 | G_IO_ERROR_FAILED, | |
687 | "invalid status value 0x%02x", | |
688 | status); | |
689 | break; | |
690 | case 0x01: /* packet success */ | |
691 | case 0x02: /* DFU success */ | |
692 | case 0x05: /* DFU success: entity restart required */ | |
693 | case 0x06: /* DFU success: system restart required */ | |
694 | /* success */ | |
695 | return TRUE; | |
696 | break; | |
697 | case 0x03: | |
698 | g_set_error_literal (error, | |
699 | G_IO_ERROR, | |
700 | G_IO_ERROR_PENDING, | |
701 | "wait for event (command in progress)"); | |
702 | break; | |
703 | case 0x04: | |
704 | case 0x10: /* unknown */ | |
705 | g_set_error_literal (error, | |
706 | G_IO_ERROR, | |
707 | G_IO_ERROR_FAILED, | |
708 | "generic error"); | |
709 | break; | |
710 | case 0x11: | |
711 | g_set_error_literal (error, | |
712 | G_IO_ERROR, | |
713 | G_IO_ERROR_FAILED, | |
714 | "bad voltage (power too low?)"); | |
715 | break; | |
716 | case 0x12: | |
717 | case 0x14: /* bad magic string */ | |
718 | case 0x21: /* bad firmware */ | |
719 | g_set_error_literal (error, | |
720 | G_IO_ERROR, | |
721 | G_IO_ERROR_FAILED, | |
722 | "unsupported firmware"); | |
723 | break; | |
724 | case 0x13: | |
725 | g_set_error_literal (error, | |
726 | G_IO_ERROR, | |
727 | G_IO_ERROR_FAILED, | |
728 | "unsupported encryption mode"); | |
729 | break; | |
730 | case 0x15: | |
731 | g_set_error_literal (error, | |
732 | G_IO_ERROR, | |
733 | G_IO_ERROR_FAILED, | |
734 | "erase failure"); | |
735 | break; | |
736 | case 0x16: | |
737 | g_set_error_literal (error, | |
738 | G_IO_ERROR, | |
739 | G_IO_ERROR_FAILED, | |
740 | "DFU not started"); | |
741 | break; | |
742 | case 0x17: | |
743 | g_set_error_literal (error, | |
744 | G_IO_ERROR, | |
745 | G_IO_ERROR_FAILED, | |
746 | "bad sequence number"); | |
747 | break; | |
748 | case 0x18: | |
749 | g_set_error_literal (error, | |
750 | G_IO_ERROR, | |
751 | G_IO_ERROR_FAILED, | |
752 | "unsupported command"); | |
753 | break; | |
754 | case 0x19: | |
755 | g_set_error_literal (error, | |
756 | G_IO_ERROR, | |
757 | G_IO_ERROR_FAILED, | |
758 | "command in progress"); | |
759 | break; | |
760 | case 0x1a: | |
761 | g_set_error_literal (error, | |
762 | G_IO_ERROR, | |
763 | G_IO_ERROR_FAILED, | |
764 | "address out of range"); | |
765 | break; | |
766 | case 0x1b: | |
767 | g_set_error_literal (error, | |
768 | G_IO_ERROR, | |
769 | G_IO_ERROR_FAILED, | |
770 | "unaligned address"); | |
771 | break; | |
772 | case 0x1c: | |
773 | g_set_error_literal (error, | |
774 | G_IO_ERROR, | |
775 | G_IO_ERROR_FAILED, | |
776 | "bad size"); | |
777 | break; | |
778 | case 0x1d: | |
779 | g_set_error_literal (error, | |
780 | G_IO_ERROR, | |
781 | G_IO_ERROR_FAILED, | |
782 | "missing program data"); | |
783 | break; | |
784 | case 0x1e: | |
785 | g_set_error_literal (error, | |
786 | G_IO_ERROR, | |
787 | G_IO_ERROR_FAILED, | |
788 | "missing check data"); | |
789 | break; | |
790 | case 0x1f: | |
791 | g_set_error_literal (error, | |
792 | G_IO_ERROR, | |
793 | G_IO_ERROR_FAILED, | |
794 | "program failed to write"); | |
795 | break; | |
796 | case 0x20: | |
797 | g_set_error_literal (error, | |
798 | G_IO_ERROR, | |
799 | G_IO_ERROR_FAILED, | |
800 | "program failed to verify"); | |
801 | break; | |
802 | case 0x22: | |
803 | g_set_error_literal (error, | |
804 | G_IO_ERROR, | |
805 | G_IO_ERROR_FAILED, | |
806 | "firmware check failure"); | |
807 | break; | |
808 | case 0x23: | |
809 | g_set_error_literal (error, | |
810 | G_IO_ERROR, | |
811 | G_IO_ERROR_FAILED, | |
812 | "blocked command (restart required)"); | |
813 | break; | |
814 | default: | |
815 | g_set_error (error, | |
816 | G_IO_ERROR, | |
817 | G_IO_ERROR_FAILED, | |
818 | "unhandled status value 0x%02x", | |
819 | status); | |
820 | break; | |
821 | } | |
822 | return FALSE; | |
823 | } | |
824 | ||
825 | static gboolean | |
826 | fu_unifying_peripheral_write_firmware_pkt (FuUnifyingPeripheral *self, | |
827 | guint8 idx, | |
828 | guint8 cmd, | |
829 | const guint8 *data, | |
830 | GError **error) | |
831 | { | |
832 | guint32 packet_cnt; | |
833 | g_autoptr(FuUnifyingHidppMsg) msg = fu_unifying_hidpp_msg_new (); | |
834 | g_autoptr(GError) error_local = NULL; | |
835 | ||
836 | /* send firmware data */ | |
837 | msg->report_id = HIDPP_REPORT_ID_LONG; | |
838 | msg->device_id = self->hidpp_id; | |
839 | msg->sub_id = idx; | |
840 | msg->function_id = cmd << 4; /* dfuStart or dfuCmdDataX */ | |
841 | msg->hidpp_version = self->hidpp_version; | |
842 | memcpy (msg->data, data, 16); | |
843 | if (!fu_unifying_hidpp_transfer (self->io_channel, msg, &error_local)) { | |
844 | g_prefix_error (error, "failed to supply program data: "); | |
845 | return FALSE; | |
846 | } | |
847 | ||
848 | /* check error */ | |
849 | packet_cnt = fu_common_read_uint32 (msg->data, G_BIG_ENDIAN); | |
850 | g_debug ("packet_cnt=0x%04x", packet_cnt); | |
851 | if (fu_unifying_peripheral_check_status (msg->data[4], &error_local)) | |
852 | return TRUE; | |
853 | ||
854 | /* fatal error */ | |
855 | if (!g_error_matches (error_local, | |
856 | G_IO_ERROR, | |
857 | G_IO_ERROR_PENDING)) { | |
858 | g_set_error_literal (error, | |
859 | G_IO_ERROR, | |
860 | G_IO_ERROR_FAILED, | |
861 | error_local->message); | |
862 | return FALSE; | |
863 | } | |
864 | ||
865 | /* wait for the HID++ notification */ | |
866 | g_debug ("ignoring: %s", error_local->message); | |
867 | for (guint retry = 0; retry < 10; retry++) { | |
868 | g_autoptr(FuUnifyingHidppMsg) msg2 = fu_unifying_hidpp_msg_new (); | |
869 | msg2->flags = FU_UNIFYING_HIDPP_MSG_FLAG_IGNORE_FNCT_ID; | |
870 | if (!fu_unifying_hidpp_receive (self->io_channel, msg2, 15000, error)) | |
871 | return FALSE; | |
872 | if (fu_unifying_hidpp_msg_is_reply (msg, msg2)) { | |
873 | g_autoptr(GError) error2 = NULL; | |
874 | if (!fu_unifying_peripheral_check_status (msg2->data[4], &error2)) { | |
875 | g_debug ("got %s, waiting a bit longer", error2->message); | |
876 | continue; | |
877 | } | |
878 | return TRUE; | |
879 | } else { | |
880 | g_debug ("got wrong packet, continue to wait..."); | |
881 | } | |
882 | } | |
883 | ||
884 | /* nothing in the queue */ | |
885 | g_set_error_literal (error, | |
886 | G_IO_ERROR, | |
887 | G_IO_ERROR_FAILED, | |
888 | "failed to get event after timeout"); | |
889 | return FALSE; | |
890 | } | |
891 | ||
892 | static gboolean | |
893 | fu_unifying_peripheral_write_firmware (FuDevice *device, | |
894 | FuFirmware *firmware, | |
895 | FwupdInstallFlags flags, | |
896 | GError **error) | |
897 | { | |
898 | FuUnifyingPeripheral *self = FU_UNIFYING_PERIPHERAL (device); | |
899 | gsize sz = 0; | |
900 | const guint8 *data; | |
901 | guint8 cmd = 0x04; | |
902 | guint8 idx; | |
903 | g_autoptr(GBytes) fw = NULL; | |
904 | ||
905 | /* if we're in bootloader mode, we should be able to get this feature */ | |
906 | idx = fu_unifying_peripheral_feature_get_idx (self, HIDPP_FEATURE_DFU); | |
907 | if (idx == 0x00) { | |
908 | g_set_error (error, | |
909 | G_IO_ERROR, | |
910 | G_IO_ERROR_FAILED, | |
911 | "no DFU feature available"); | |
912 | return FALSE; | |
913 | } | |
914 | ||
915 | /* get default image */ | |
916 | fw = fu_firmware_get_image_default_bytes (firmware, error); | |
917 | if (fw == NULL) | |
918 | return FALSE; | |
919 | ||
920 | /* flash hardware */ | |
921 | data = g_bytes_get_data (fw, &sz); | |
922 | fu_device_set_status (device, FWUPD_STATUS_DEVICE_WRITE); | |
923 | for (gsize i = 0; i < sz / 16; i++) { | |
924 | ||
925 | /* send packet and wait for reply */ | |
926 | g_debug ("send data at addr=0x%04x", (guint) i * 16); | |
927 | if (!fu_unifying_peripheral_write_firmware_pkt (self, | |
928 | idx, | |
929 | cmd, | |
930 | data + (i * 16), | |
931 | error)) { | |
932 | g_prefix_error (error, | |
933 | "failed to write @0x%04x: ", | |
934 | (guint) i * 16); | |
935 | return FALSE; | |
936 | } | |
937 | ||
938 | /* use sliding window */ | |
939 | cmd = (cmd + 1) % 4; | |
940 | ||
941 | /* update progress-bar */ | |
942 | fu_device_set_progress_full (device, i * 16, sz); | |
943 | } | |
944 | ||
945 | return TRUE; | |
946 | } | |
947 | ||
948 | static gboolean | |
949 | fu_unifying_peripheral_attach (FuDevice *device, GError **error) | |
950 | { | |
951 | FuUnifyingPeripheral *self = FU_UNIFYING_PERIPHERAL (device); | |
952 | guint8 idx; | |
953 | g_autoptr(FuUnifyingHidppMsg) msg = fu_unifying_hidpp_msg_new (); | |
954 | ||
955 | /* if we're in bootloader mode, we should be able to get this feature */ | |
956 | idx = fu_unifying_peripheral_feature_get_idx (self, HIDPP_FEATURE_DFU); | |
957 | if (idx == 0x00) { | |
958 | g_set_error (error, | |
959 | G_IO_ERROR, | |
960 | G_IO_ERROR_FAILED, | |
961 | "no DFU feature available"); | |
962 | return FALSE; | |
963 | } | |
964 | ||
965 | /* reboot back into firmware mode */ | |
966 | msg->report_id = HIDPP_REPORT_ID_SHORT; | |
967 | msg->device_id = self->hidpp_id; | |
968 | msg->sub_id = idx; | |
969 | msg->function_id = 0x05 << 4; /* restart */ | |
970 | msg->data[0] = self->cached_fw_entity; /* fwEntity */ | |
971 | msg->hidpp_version = self->hidpp_version; | |
972 | msg->flags = FU_UNIFYING_HIDPP_MSG_FLAG_IGNORE_SUB_ID | | |
973 | FU_UNIFYING_HIDPP_MSG_FLAG_IGNORE_SWID | // inferred? | |
974 | FU_UNIFYING_HIDPP_MSG_FLAG_LONGER_TIMEOUT; | |
975 | if (!fu_unifying_hidpp_transfer (self->io_channel, msg, error)) { | |
976 | g_prefix_error (error, "failed to restart device: "); | |
977 | return FALSE; | |
978 | } | |
979 | ||
980 | /* reprobe */ | |
981 | if (!fu_unifying_peripheral_setup (device, error)) | |
982 | return FALSE; | |
983 | ||
984 | /* success */ | |
985 | return TRUE; | |
986 | } | |
987 | ||
988 | static void | |
989 | fu_unifying_peripheral_finalize (GObject *object) | |
990 | { | |
991 | FuUnifyingPeripheral *self = FU_UNIFYING_PERIPHERAL (object); | |
992 | g_ptr_array_unref (self->feature_index); | |
993 | G_OBJECT_CLASS (fu_unifying_peripheral_parent_class)->finalize (object); | |
994 | } | |
995 | ||
996 | static void | |
997 | fu_unifying_peripheral_class_init (FuUnifyingPeripheralClass *klass) | |
998 | { | |
999 | FuDeviceClass *klass_device = FU_DEVICE_CLASS (klass); | |
1000 | FuUdevDeviceClass *klass_device_udev = FU_UDEV_DEVICE_CLASS (klass); | |
1001 | GObjectClass *object_class = G_OBJECT_CLASS (klass); | |
1002 | ||
1003 | object_class->finalize = fu_unifying_peripheral_finalize; | |
1004 | klass_device->setup = fu_unifying_peripheral_setup; | |
1005 | klass_device->open = fu_unifying_peripheral_open; | |
1006 | klass_device->close = fu_unifying_peripheral_close; | |
1007 | klass_device->write_firmware = fu_unifying_peripheral_write_firmware; | |
1008 | klass_device->attach = fu_unifying_peripheral_attach; | |
1009 | klass_device->detach = fu_unifying_peripheral_detach; | |
1010 | klass_device->poll = fu_unifying_peripheral_poll; | |
1011 | klass_device->to_string = fu_unifying_peripheral_to_string; | |
1012 | klass_device_udev->probe = fu_unifying_peripheral_probe; | |
1013 | } | |
1014 | ||
1015 | static void | |
1016 | fu_unifying_peripheral_init (FuUnifyingPeripheral *self) | |
1017 | { | |
1018 | self->hidpp_id = HIDPP_DEVICE_ID_UNSET; | |
1019 | self->feature_index = g_ptr_array_new_with_free_func (g_free); | |
1020 | fu_device_add_parent_guid (FU_DEVICE (self), "HIDRAW\\VEN_046D&DEV_C52B"); | |
1021 | fu_device_set_remove_delay (FU_DEVICE (self), FU_DEVICE_REMOVE_DELAY_RE_ENUMERATE); | |
1022 | ||
1023 | /* there are a lot of unifying peripherals, but not all respond | |
1024 | * well to opening -- so limit to ones with issued updates */ | |
1025 | fu_device_add_flag (FU_DEVICE (self), FWUPD_DEVICE_FLAG_ONLY_SUPPORTED); | |
1026 | } |
0 | /* | |
1 | * Copyright (C) 2017-2018 Richard Hughes <richard@hughsie.com> | |
2 | * | |
3 | * SPDX-License-Identifier: LGPL-2.1+ | |
4 | */ | |
5 | ||
6 | #pragma once | |
7 | ||
8 | #include "fu-udev-device.h" | |
9 | ||
10 | #define FU_TYPE_UNIFYING_PERIPHERAL (fu_unifying_peripheral_get_type ()) | |
11 | G_DECLARE_FINAL_TYPE (FuUnifyingPeripheral, fu_unifying_peripheral, FU, UNIFYING_PERIPHERAL, FuUdevDevice) |
0 | /* | |
1 | * Copyright (C) 2016-2018 Richard Hughes <richard@hughsie.com> | |
2 | * | |
3 | * SPDX-License-Identifier: LGPL-2.1+ | |
4 | */ | |
5 | ||
6 | #include "config.h" | |
7 | ||
8 | #include <string.h> | |
9 | ||
10 | #include "fu-unifying-common.h" | |
11 | #include "fu-unifying-runtime.h" | |
12 | #include "fu-unifying-hidpp.h" | |
13 | ||
14 | struct _FuUnifyingRuntime | |
15 | { | |
16 | FuUdevDevice parent_instance; | |
17 | guint8 version_bl_major; | |
18 | gboolean signed_firmware; | |
19 | FuIOChannel *io_channel; | |
20 | }; | |
21 | ||
22 | G_DEFINE_TYPE (FuUnifyingRuntime, fu_unifying_runtime, FU_TYPE_UDEV_DEVICE) | |
23 | ||
24 | #ifndef HAVE_GUDEV_232 | |
25 | G_DEFINE_AUTOPTR_CLEANUP_FUNC(GUdevDevice, g_object_unref) | |
26 | #endif | |
27 | ||
28 | static void | |
29 | fu_unifying_runtime_to_string (FuDevice *device, guint idt, GString *str) | |
30 | { | |
31 | FuUnifyingRuntime *self = FU_UNIFYING_RUNTIME (device); | |
32 | fu_common_string_append_kb (str, idt, "SignedFirmware", self->signed_firmware); | |
33 | } | |
34 | ||
35 | static gboolean | |
36 | fu_unifying_runtime_enable_notifications (FuUnifyingRuntime *self, GError **error) | |
37 | { | |
38 | g_autoptr(FuUnifyingHidppMsg) msg = fu_unifying_hidpp_msg_new (); | |
39 | msg->report_id = HIDPP_REPORT_ID_SHORT; | |
40 | msg->device_id = HIDPP_DEVICE_ID_RECEIVER; | |
41 | msg->sub_id = HIDPP_SUBID_SET_REGISTER; | |
42 | msg->function_id = HIDPP_REGISTER_HIDPP_NOTIFICATIONS; | |
43 | msg->data[0] = 0x00; | |
44 | msg->data[1] = 0x05; /* Wireless + SoftwarePresent */ | |
45 | msg->data[2] = 0x00; | |
46 | msg->hidpp_version = 1; | |
47 | return fu_unifying_hidpp_transfer (self->io_channel, msg, error); | |
48 | } | |
49 | ||
50 | static gboolean | |
51 | fu_unifying_runtime_close (FuDevice *device, GError **error) | |
52 | { | |
53 | FuUnifyingRuntime *self = FU_UNIFYING_RUNTIME (device); | |
54 | if (!fu_io_channel_shutdown (self->io_channel, error)) | |
55 | return FALSE; | |
56 | g_clear_object (&self->io_channel); | |
57 | return TRUE; | |
58 | } | |
59 | ||
60 | static gboolean | |
61 | fu_unifying_runtime_poll (FuDevice *device, GError **error) | |
62 | { | |
63 | FuUnifyingRuntime *self = FU_UNIFYING_RUNTIME (device); | |
64 | const guint timeout = 1; /* ms */ | |
65 | g_autoptr(GError) error_local = NULL; | |
66 | g_autoptr(FuUnifyingHidppMsg) msg = fu_unifying_hidpp_msg_new (); | |
67 | g_autoptr(FuDeviceLocker) locker = NULL; | |
68 | ||
69 | /* open */ | |
70 | locker = fu_device_locker_new (self, error); | |
71 | if (locker == NULL) | |
72 | return FALSE; | |
73 | ||
74 | /* is there any pending data to read */ | |
75 | msg->hidpp_version = 1; | |
76 | if (!fu_unifying_hidpp_receive (self->io_channel, msg, timeout, &error_local)) { | |
77 | if (g_error_matches (error_local, | |
78 | G_IO_ERROR, | |
79 | G_IO_ERROR_TIMED_OUT)) { | |
80 | return TRUE; | |
81 | } | |
82 | g_warning ("failed to get pending read: %s", error_local->message); | |
83 | return TRUE; | |
84 | } | |
85 | ||
86 | /* HID++1.0 error */ | |
87 | if (!fu_unifying_hidpp_msg_is_error (msg, &error_local)) { | |
88 | g_warning ("failed to get pending read: %s", error_local->message); | |
89 | return TRUE; | |
90 | } | |
91 | ||
92 | /* unifying receiver notification */ | |
93 | if (msg->report_id == HIDPP_REPORT_ID_SHORT) { | |
94 | switch (msg->sub_id) { | |
95 | case HIDPP_SUBID_DEVICE_CONNECTION: | |
96 | case HIDPP_SUBID_DEVICE_DISCONNECTION: | |
97 | case HIDPP_SUBID_DEVICE_LOCKING_CHANGED: | |
98 | g_debug ("device connection event, do something"); | |
99 | break; | |
100 | case HIDPP_SUBID_LINK_QUALITY: | |
101 | g_debug ("ignoring link quality message"); | |
102 | break; | |
103 | case HIDPP_SUBID_ERROR_MSG: | |
104 | g_debug ("ignoring link quality message"); | |
105 | break; | |
106 | default: | |
107 | g_debug ("unknown SubID %02x", msg->sub_id); | |
108 | break; | |
109 | } | |
110 | } | |
111 | return TRUE; | |
112 | } | |
113 | ||
114 | static gboolean | |
115 | fu_unifying_runtime_open (FuDevice *device, GError **error) | |
116 | { | |
117 | FuUnifyingRuntime *self = FU_UNIFYING_RUNTIME (device); | |
118 | GUdevDevice *udev_device = fu_udev_device_get_dev (FU_UDEV_DEVICE (device)); | |
119 | const gchar *devpath = g_udev_device_get_device_file (udev_device); | |
120 | ||
121 | /* open, but don't block */ | |
122 | self->io_channel = fu_io_channel_new_file (devpath, error); | |
123 | if (self->io_channel == NULL) | |
124 | return FALSE; | |
125 | ||
126 | /* poll for notifications */ | |
127 | fu_device_set_poll_interval (device, 5000); | |
128 | ||
129 | /* success */ | |
130 | return TRUE; | |
131 | } | |
132 | ||
133 | static gboolean | |
134 | fu_unifying_runtime_probe (FuUdevDevice *device, GError **error) | |
135 | { | |
136 | FuUnifyingRuntime *self = FU_UNIFYING_RUNTIME (device); | |
137 | GUdevDevice *udev_device = fu_udev_device_get_dev (FU_UDEV_DEVICE (device)); | |
138 | guint16 release = 0xffff; | |
139 | g_autoptr(GUdevDevice) udev_parent = NULL; | |
140 | ||
141 | /* set the physical ID */ | |
142 | if (!fu_udev_device_set_physical_id (device, "usb", error)) | |
143 | return FALSE; | |
144 | ||
145 | /* generate bootloader-specific GUID */ | |
146 | udev_parent = g_udev_device_get_parent_with_subsystem (udev_device, | |
147 | "usb", "usb_device"); | |
148 | if (udev_parent != NULL) { | |
149 | const gchar *release_str; | |
150 | release_str = g_udev_device_get_property (udev_parent, "ID_REVISION"); | |
151 | if (release_str != NULL) | |
152 | release = g_ascii_strtoull (release_str, NULL, 16); | |
153 | } | |
154 | if (release != 0xffff) { | |
155 | g_autofree gchar *devid2 = NULL; | |
156 | switch (release &= 0xff00) { | |
157 | case 0x1200: | |
158 | /* Nordic */ | |
159 | devid2 = g_strdup_printf ("USB\\VID_%04X&PID_%04X", | |
160 | (guint) FU_UNIFYING_DEVICE_VID, | |
161 | (guint) FU_UNIFYING_DEVICE_PID_BOOTLOADER_NORDIC); | |
162 | fu_device_add_counterpart_guid (FU_DEVICE (device), devid2); | |
163 | self->version_bl_major = 0x01; | |
164 | break; | |
165 | case 0x2400: | |
166 | /* Texas */ | |
167 | devid2 = g_strdup_printf ("USB\\VID_%04X&PID_%04X", | |
168 | (guint) FU_UNIFYING_DEVICE_VID, | |
169 | (guint) FU_UNIFYING_DEVICE_PID_BOOTLOADER_TEXAS); | |
170 | fu_device_add_counterpart_guid (FU_DEVICE (device), devid2); | |
171 | self->version_bl_major = 0x03; | |
172 | break; | |
173 | default: | |
174 | g_warning ("bootloader release %04x invalid", release); | |
175 | break; | |
176 | } | |
177 | } | |
178 | return TRUE; | |
179 | } | |
180 | ||
181 | static gboolean | |
182 | fu_unifying_runtime_setup_internal (FuDevice *device, GError **error) | |
183 | { | |
184 | FuUnifyingRuntime *self = FU_UNIFYING_RUNTIME (device); | |
185 | guint8 config[10]; | |
186 | g_autofree gchar *version_fw = NULL; | |
187 | ||
188 | /* read all 10 bytes of the version register */ | |
189 | memset (config, 0x00, sizeof (config)); | |
190 | for (guint i = 0x01; i < 0x05; i++) { | |
191 | g_autoptr(FuUnifyingHidppMsg) msg = fu_unifying_hidpp_msg_new (); | |
192 | ||
193 | /* workaround a bug in the 12.01 firmware, which fails with | |
194 | * INVALID_VALUE when reading MCU1_HW_VERSION */ | |
195 | if (i == 0x03) | |
196 | continue; | |
197 | ||
198 | msg->report_id = HIDPP_REPORT_ID_SHORT; | |
199 | msg->device_id = HIDPP_DEVICE_ID_RECEIVER; | |
200 | msg->sub_id = HIDPP_SUBID_GET_REGISTER; | |
201 | msg->function_id = HIDPP_REGISTER_DEVICE_FIRMWARE_INFORMATION; | |
202 | msg->data[0] = i; | |
203 | msg->hidpp_version = 1; | |
204 | if (!fu_unifying_hidpp_transfer (self->io_channel, msg, error)) { | |
205 | g_prefix_error (error, "failed to read device config: "); | |
206 | return FALSE; | |
207 | } | |
208 | if (!fu_memcpy_safe (config, sizeof(config), i * 2, /* dst */ | |
209 | msg->data, sizeof(msg->data), 0x1, /* src */ | |
210 | 2, error)) | |
211 | return FALSE; | |
212 | } | |
213 | ||
214 | /* get firmware version */ | |
215 | version_fw = fu_unifying_format_version ("RQR", | |
216 | config[2], | |
217 | config[3], | |
218 | (guint16) config[4] << 8 | | |
219 | config[5]); | |
220 | fu_device_set_version (device, version_fw, FWUPD_VERSION_FORMAT_PLAIN); | |
221 | ||
222 | /* get bootloader version */ | |
223 | if (self->version_bl_major > 0) { | |
224 | g_autofree gchar *version_bl = NULL; | |
225 | version_bl = fu_unifying_format_version ("BOT", | |
226 | self->version_bl_major, | |
227 | config[8], | |
228 | config[9]); | |
229 | fu_device_set_version_bootloader (FU_DEVICE (device), version_bl); | |
230 | ||
231 | /* is the dongle expecting signed firmware */ | |
232 | if ((self->version_bl_major == 0x01 && config[8] >= 0x04) || | |
233 | (self->version_bl_major == 0x03 && config[8] >= 0x02)) { | |
234 | self->signed_firmware = TRUE; | |
235 | } | |
236 | } | |
237 | ||
238 | /* enable HID++ notifications */ | |
239 | if (!fu_unifying_runtime_enable_notifications (self, error)) { | |
240 | g_prefix_error (error, "failed to enable notifications: "); | |
241 | return FALSE; | |
242 | } | |
243 | ||
244 | /* success */ | |
245 | return TRUE; | |
246 | } | |
247 | ||
248 | static gboolean | |
249 | fu_unifying_runtime_setup (FuDevice *device, GError **error) | |
250 | { | |
251 | g_autoptr(GError) error_local = NULL; | |
252 | for (guint i = 0; i < 5; i++) { | |
253 | /* HID++1.0 devices have to sleep to allow Solaar to talk to | |
254 | * the device first -- we can't use the SwID as this is a | |
255 | * HID++2.0 feature */ | |
256 | g_usleep (200*1000); | |
257 | if (fu_unifying_runtime_setup_internal (device, &error_local)) | |
258 | return TRUE; | |
259 | if (!g_error_matches (error_local, | |
260 | G_IO_ERROR, | |
261 | G_IO_ERROR_INVALID_DATA)) { | |
262 | g_propagate_error (error, g_steal_pointer (&error_local)); | |
263 | return FALSE; | |
264 | } | |
265 | g_clear_error (&error_local); | |
266 | } | |
267 | g_propagate_error (error, g_steal_pointer (&error_local)); | |
268 | return FALSE; | |
269 | } | |
270 | ||
271 | static gboolean | |
272 | fu_unifying_runtime_detach (FuDevice *device, GError **error) | |
273 | { | |
274 | FuUnifyingRuntime *self = FU_UNIFYING_RUNTIME (device); | |
275 | g_autoptr(FuUnifyingHidppMsg) msg = fu_unifying_hidpp_msg_new (); | |
276 | msg->report_id = HIDPP_REPORT_ID_SHORT; | |
277 | msg->device_id = HIDPP_DEVICE_ID_RECEIVER; | |
278 | msg->sub_id = HIDPP_SUBID_SET_REGISTER; | |
279 | msg->function_id = HIDPP_REGISTER_DEVICE_FIRMWARE_UPDATE_MODE; | |
280 | msg->data[0] = 'I'; | |
281 | msg->data[1] = 'C'; | |
282 | msg->data[2] = 'P'; | |
283 | msg->hidpp_version = 1; | |
284 | msg->flags = FU_UNIFYING_HIDPP_MSG_FLAG_LONGER_TIMEOUT; | |
285 | if (!fu_unifying_hidpp_send (self->io_channel, msg, FU_UNIFYING_DEVICE_TIMEOUT_MS, error)) { | |
286 | g_prefix_error (error, "failed to detach to bootloader: "); | |
287 | return FALSE; | |
288 | } | |
289 | fu_device_add_flag (device, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG); | |
290 | return TRUE; | |
291 | } | |
292 | ||
293 | static void | |
294 | fu_unifying_runtime_finalize (GObject *object) | |
295 | { | |
296 | G_OBJECT_CLASS (fu_unifying_runtime_parent_class)->finalize (object); | |
297 | } | |
298 | ||
299 | static void | |
300 | fu_unifying_runtime_class_init (FuUnifyingRuntimeClass *klass) | |
301 | { | |
302 | FuDeviceClass *klass_device = FU_DEVICE_CLASS (klass); | |
303 | FuUdevDeviceClass *klass_device_udev = FU_UDEV_DEVICE_CLASS (klass); | |
304 | GObjectClass *object_class = G_OBJECT_CLASS (klass); | |
305 | ||
306 | object_class->finalize = fu_unifying_runtime_finalize; | |
307 | klass_device->open = fu_unifying_runtime_open; | |
308 | klass_device_udev->probe = fu_unifying_runtime_probe; | |
309 | klass_device->setup = fu_unifying_runtime_setup; | |
310 | klass_device->close = fu_unifying_runtime_close; | |
311 | klass_device->detach = fu_unifying_runtime_detach; | |
312 | klass_device->poll = fu_unifying_runtime_poll; | |
313 | klass_device->to_string = fu_unifying_runtime_to_string; | |
314 | } | |
315 | ||
316 | static void | |
317 | fu_unifying_runtime_init (FuUnifyingRuntime *self) | |
318 | { | |
319 | fu_device_add_flag (FU_DEVICE (self), FWUPD_DEVICE_FLAG_UPDATABLE); | |
320 | fu_device_add_icon (FU_DEVICE (self), "preferences-desktop-keyboard"); | |
321 | fu_device_set_name (FU_DEVICE (self), "Unifying Receiver"); | |
322 | fu_device_set_summary (FU_DEVICE (self), "A miniaturised USB wireless receiver"); | |
323 | fu_device_set_remove_delay (FU_DEVICE (self), FU_DEVICE_REMOVE_DELAY_RE_ENUMERATE); | |
324 | } |
0 | /* | |
1 | * Copyright (C) 2016-2018 Richard Hughes <richard@hughsie.com> | |
2 | * | |
3 | * SPDX-License-Identifier: LGPL-2.1+ | |
4 | */ | |
5 | ||
6 | #pragma once | |
7 | ||
8 | #include "fu-udev-device.h" | |
9 | ||
10 | #define FU_TYPE_UNIFYING_RUNTIME (fu_unifying_runtime_get_type ()) | |
11 | G_DECLARE_FINAL_TYPE (FuUnifyingRuntime, fu_unifying_runtime, FU, UNIFYING_RUNTIME, FuUdevDevice) |
0 | /* | |
1 | * Copyright (C) 2017-2018 Richard Hughes <richard@hughsie.com> | |
2 | * | |
3 | * SPDX-License-Identifier: LGPL-2.1+ | |
4 | */ | |
5 | ||
6 | #include "config.h" | |
7 | ||
8 | #include <fwupd.h> | |
9 | #include <glib-object.h> | |
10 | ||
11 | #include "fu-unifying-common.h" | |
12 | ||
13 | static void | |
14 | fu_unifying_common (void) | |
15 | { | |
16 | guint8 u8; | |
17 | guint16 u16; | |
18 | g_autofree gchar *ver1 = NULL; | |
19 | ||
20 | u8 = fu_unifying_buffer_read_uint8 ("12"); | |
21 | g_assert_cmpint (u8, ==, 0x12); | |
22 | u16 = fu_unifying_buffer_read_uint16 ("1234"); | |
23 | g_assert_cmpint (u16, ==, 0x1234); | |
24 | ||
25 | ver1 = fu_unifying_format_version (" A ", 0x87, 0x65, 0x4321); | |
26 | g_assert_cmpstr (ver1, ==, "A87.65_B4321"); | |
27 | } | |
28 | ||
29 | int | |
30 | main (int argc, char **argv) | |
31 | { | |
32 | g_test_init (&argc, &argv, NULL); | |
33 | ||
34 | /* only critical and error are fatal */ | |
35 | g_log_set_fatal_mask (NULL, G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL); | |
36 | ||
37 | /* tests go here */ | |
38 | g_test_add_func ("/unifying/common", fu_unifying_common); | |
39 | return g_test_run (); | |
40 | } |
0 | cargs = ['-DG_LOG_DOMAIN="FuPluginUnifying"'] | |
1 | ||
2 | install_data([ | |
3 | 'unifying.quirk', | |
4 | ], | |
5 | install_dir: join_paths(datadir, 'fwupd', 'quirks.d') | |
6 | ) | |
7 | ||
8 | ||
9 | shared_module('fu_plugin_unifying', | |
10 | fu_hash, | |
11 | sources : [ | |
12 | 'fu-plugin-unifying.c', | |
13 | 'fu-unifying-bootloader.c', | |
14 | 'fu-unifying-bootloader-nordic.c', | |
15 | 'fu-unifying-bootloader-texas.c', | |
16 | 'fu-unifying-common.c', | |
17 | 'fu-unifying-hidpp.c', | |
18 | 'fu-unifying-hidpp-msg.c', | |
19 | 'fu-unifying-peripheral.c', | |
20 | 'fu-unifying-runtime.c', | |
21 | ], | |
22 | include_directories : [ | |
23 | include_directories('../..'), | |
24 | include_directories('../../src'), | |
25 | include_directories('../../libfwupd'), | |
26 | ], | |
27 | install : true, | |
28 | install_dir: plugin_dir, | |
29 | link_with : [ | |
30 | libfwupdprivate, | |
31 | ], | |
32 | c_args : cargs, | |
33 | dependencies : [ | |
34 | plugin_deps, | |
35 | ], | |
36 | ) | |
37 | ||
38 | if get_option('tests') | |
39 | e = executable( | |
40 | 'unifying-self-test', | |
41 | fu_hash, | |
42 | sources : [ | |
43 | 'fu-unifying-self-test.c', | |
44 | 'fu-unifying-common.c', | |
45 | ], | |
46 | include_directories : [ | |
47 | include_directories('../..'), | |
48 | include_directories('../../libfwupd'), | |
49 | ], | |
50 | dependencies : [ | |
51 | plugin_deps, | |
52 | ], | |
53 | link_with : [ | |
54 | libfwupdprivate, | |
55 | ], | |
56 | c_args : cargs, | |
57 | ) | |
58 | test('unifying-self-test', e) | |
59 | endif |
0 | # Unifying Receiver | |
1 | [DeviceInstanceId=HIDRAW\VEN_046D&DEV_C52B] | |
2 | Plugin = unifying | |
3 | GType = FuUnifyingRuntime | |
4 | VendorId=USB:0x046D | |
5 | InstallDuration = 30 | |
6 | ||
7 | # Nordic | |
8 | [DeviceInstanceId=USB\VID_046D&PID_AAAA] | |
9 | Plugin = unifying | |
10 | GType = FuUnifyingBootloaderNordic | |
11 | FirmwareSizeMin = 0x4000 | |
12 | CounterpartGuid = HIDRAW\VEN_046D&DEV_C52B | |
13 | InstallDuration = 30 | |
14 | ||
15 | # Nordic Pico | |
16 | [DeviceInstanceId=USB\VID_046D&PID_AAAE] | |
17 | Plugin = unifying | |
18 | GType = FuUnifyingBootloaderNordic | |
19 | FirmwareSizeMin = 0x4000 | |
20 | CounterpartGuid = HIDRAW\VEN_046D&DEV_C52B | |
21 | InstallDuration = 30 | |
22 | ||
23 | # Texas | |
24 | [DeviceInstanceId=USB\VID_046D&PID_AAAC] | |
25 | Plugin = unifying | |
26 | GType = FuUnifyingBootloaderTexas | |
27 | FirmwareSizeMin = 0x4000 | |
28 | CounterpartGuid = HIDRAW\VEN_046D&DEV_C52B | |
29 | InstallDuration = 18 | |
30 | ||
31 | # Texas Pico | |
32 | [DeviceInstanceId=USB\VID_046D&PID_AAAD] | |
33 | Plugin = unifying | |
34 | GType = FuUnifyingBootloaderTexas | |
35 | FirmwareSizeMin = 0x4000 | |
36 | CounterpartGuid = HIDRAW\VEN_046D&DEV_C52B | |
37 | InstallDuration = 18 | |
38 | ||
39 | # Possible HID++ v2.0 peripheral device | |
40 | [DeviceInstanceId=HIDRAW\VEN_046D] | |
41 | Plugin = unifying | |
42 | GType = FuUnifyingPeripheral |
13 | 13 | |
14 | 14 | This plugin supports the following protocol ID: |
15 | 15 | |
16 | * com.via.vli-usbhub | |
16 | * com.vli.usbhub | |
17 | 17 | |
18 | 18 | GUID Generation |
19 | 19 | --------------- |
29 | 29 | * `VLI_USBHUB\SPI_37303840` |
30 | 30 | * `VLI_USBHUB\SPI_3730` |
31 | 31 | * `VLI_USBHUB\SPI_37` |
32 | ||
33 | Optional PD child devices use just one extra GUID, e.g. | |
34 | ||
35 | * `VLI_USBHUB_PD\VID_17EF&PID_3083` | |
36 | ||
37 | Optional I²C child devices use just one extra GUID, e.g. | |
38 | ||
39 | * `VLI_USBHUB_I2C\MSP430` | |
32 | 40 | |
33 | 41 | Quirk Use |
34 | 42 | --------- |
9 | 9 | |
10 | 10 | #include "fu-vli-usbhub-device.h" |
11 | 11 | #include "fu-vli-usbhub-firmware.h" |
12 | #include "fu-vli-usbhub-pd-firmware.h" | |
12 | 13 | |
13 | 14 | void |
14 | 15 | fu_plugin_init (FuPlugin *plugin) |
15 | 16 | { |
16 | 17 | fu_plugin_set_build_hash (plugin, FU_BUILD_HASH); |
17 | fu_plugin_add_rule (plugin, FU_PLUGIN_RULE_SUPPORTS_PROTOCOL, "com.via.vli-usbhub"); | |
18 | fu_plugin_add_rule (plugin, FU_PLUGIN_RULE_SUPPORTS_PROTOCOL, "com.vli.usbhub"); | |
18 | 19 | fu_plugin_set_device_gtype (plugin, FU_TYPE_VLI_USBHUB_DEVICE); |
19 | 20 | fu_plugin_add_firmware_gtype (plugin, "vli-usbhub", FU_TYPE_VLI_USBHUB_FIRMWARE); |
21 | fu_plugin_add_firmware_gtype (plugin, "vli-usbhub-pd", FU_TYPE_VLI_USBHUB_PD_FIRMWARE); | |
20 | 22 | } |
23 | ||
24 | /* reboot the FuVliUsbhubDevice if we update the FuVliUsbhubPdDevice */ | |
25 | static FuDevice * | |
26 | fu_plugin_vli_usbhub_get_parent (GPtrArray *devices) | |
27 | { | |
28 | for (guint i = 0; i < devices->len; i++) { | |
29 | FuDevice *dev = g_ptr_array_index (devices, i); | |
30 | FuDevice *parent = fu_device_get_parent (dev); | |
31 | if (FU_IS_VLI_USBHUB_DEVICE (dev)) | |
32 | return g_object_ref (dev); | |
33 | if (parent != NULL && FU_IS_VLI_USBHUB_DEVICE (parent)) | |
34 | return g_object_ref (parent); | |
35 | } | |
36 | return NULL; | |
37 | } | |
38 | ||
39 | gboolean | |
40 | fu_plugin_composite_cleanup (FuPlugin *plugin, | |
41 | GPtrArray *devices, | |
42 | GError **error) | |
43 | { | |
44 | g_autoptr(FuDeviceLocker) locker = NULL; | |
45 | g_autoptr(FuDevice) parent = fu_plugin_vli_usbhub_get_parent (devices); | |
46 | if (parent == NULL) | |
47 | return TRUE; | |
48 | locker = fu_device_locker_new (parent, error); | |
49 | if (locker == NULL) | |
50 | return FALSE; | |
51 | return fu_device_attach (parent, error); | |
52 | } |
76 | 76 | GUINT16_FROM_BE(hdr->usb2_fw_sz)); |
77 | 77 | } |
78 | 78 | fu_common_string_append_kx (str, idt, "Usb3FwAddr", |
79 | ((guint32) hdr->usb3_fw_addr_high) << 16 | | |
79 | 80 | GUINT16_FROM_BE(hdr->usb3_fw_addr)); |
80 | 81 | fu_common_string_append_kx (str, idt, "Usb3FwSz", |
81 | 82 | GUINT16_FROM_BE(hdr->usb3_fw_sz)); |
72 | 72 | #define VLI_USBHUB_FLASHMAP_ADDR_HD1_BACKUP 0x1800 |
73 | 73 | #define VLI_USBHUB_FLASHMAP_ADDR_HD2 0x1000 |
74 | 74 | #define VLI_USBHUB_FLASHMAP_ADDR_FW 0x2000 |
75 | #define VLI_USBHUB_FLASHMAP_ADDR_PD_LEGACY 0x10000 | |
76 | #define VLI_USBHUB_FLASHMAP_ADDR_PD 0x20000 | |
77 | #define VLI_USBHUB_FLASHMAP_ADDR_PD_BACKUP 0x30000 | |
75 | 78 | |
76 | 79 | guint8 fu_vli_usbhub_header_crc8 (FuVliUsbhubHeader *hdr); |
77 | 80 | void fu_vli_usbhub_header_to_string (FuVliUsbhubHeader *hdr, |
14 | 14 | #include "fu-vli-usbhub-common.h" |
15 | 15 | #include "fu-vli-usbhub-device.h" |
16 | 16 | #include "fu-vli-usbhub-firmware.h" |
17 | #include "fu-vli-usbhub-i2c-device.h" | |
18 | #include "fu-vli-usbhub-pd-common.h" | |
19 | #include "fu-vli-usbhub-pd-device.h" | |
17 | 20 | |
18 | 21 | #define FU_VLI_USBHUB_DEVICE_TIMEOUT 3000 /* ms */ |
19 | 22 | #define FU_VLI_USBHUB_TXSIZE 0x20 /* bytes */ |
80 | 83 | } |
81 | 84 | } |
82 | 85 | |
86 | gboolean | |
87 | fu_vli_usbhub_device_i2c_read (FuVliUsbhubDevice *self, | |
88 | guint8 cmd, guint8 *buf, gsize bufsz, | |
89 | GError **error) | |
90 | { | |
91 | GUsbDevice *usb_device = fu_usb_device_get_dev (FU_USB_DEVICE (self)); | |
92 | guint16 value = ((guint16) FU_VLI_USBHUB_I2C_ADDR_WRITE << 8) | cmd; | |
93 | guint16 index = (guint16) FU_VLI_USBHUB_I2C_ADDR_READ << 8; | |
94 | if (!g_usb_device_control_transfer (usb_device, | |
95 | G_USB_DEVICE_DIRECTION_DEVICE_TO_HOST, | |
96 | G_USB_DEVICE_REQUEST_TYPE_VENDOR, | |
97 | G_USB_DEVICE_RECIPIENT_DEVICE, | |
98 | FU_VLI_USBHUB_I2C_R_VDR, value, index, | |
99 | buf, bufsz, NULL, | |
100 | FU_VLI_USBHUB_DEVICE_TIMEOUT, | |
101 | NULL, error)) { | |
102 | g_prefix_error (error, "failed to read I2C: "); | |
103 | return FALSE; | |
104 | } | |
105 | if (g_getenv ("FWUPD_VLI_USBHUB_VERBOSE") != NULL) | |
106 | fu_common_dump_raw (G_LOG_DOMAIN, "I2cReadData", buf, 0x1); | |
107 | return TRUE; | |
108 | } | |
109 | ||
110 | gboolean | |
111 | fu_vli_usbhub_device_i2c_read_status (FuVliUsbhubDevice *self, | |
112 | FuVliUsbhubI2cStatus *status, | |
113 | GError **error) | |
114 | { | |
115 | guint8 buf[1] = { 0xff }; | |
116 | if (!fu_vli_usbhub_device_i2c_read (self, | |
117 | FU_VLI_USBHUB_I2C_CMD_READ_STATUS, | |
118 | buf, sizeof(buf), | |
119 | error)) | |
120 | return FALSE; | |
121 | if (status != NULL) | |
122 | *status = buf[0]; | |
123 | return TRUE; | |
124 | } | |
125 | ||
126 | gboolean | |
127 | fu_vli_usbhub_device_i2c_write_data (FuVliUsbhubDevice *self, | |
128 | guint8 skip_s, | |
129 | guint8 skip_p, | |
130 | const guint8 *buf, | |
131 | gsize bufsz, | |
132 | GError **error) | |
133 | { | |
134 | GUsbDevice *usb_device = fu_usb_device_get_dev (FU_USB_DEVICE (self)); | |
135 | guint16 value = (((guint16) skip_s) << 8) | skip_p; | |
136 | if (g_getenv ("FWUPD_VLI_USBHUB_VERBOSE") != NULL) | |
137 | fu_common_dump_raw (G_LOG_DOMAIN, "I2cWriteData", buf, bufsz); | |
138 | if (!g_usb_device_control_transfer (usb_device, | |
139 | G_USB_DEVICE_DIRECTION_HOST_TO_DEVICE, | |
140 | G_USB_DEVICE_REQUEST_TYPE_VENDOR, | |
141 | G_USB_DEVICE_RECIPIENT_DEVICE, | |
142 | FU_VLI_USBHUB_I2C_W_VDR, value, 0x0, | |
143 | (guint8 *) buf, bufsz, NULL, | |
144 | FU_VLI_USBHUB_DEVICE_TIMEOUT, | |
145 | NULL, error)) { | |
146 | g_prefix_error (error, "failed to write I2C @0x%x: ", value); | |
147 | return FALSE; | |
148 | } | |
149 | return TRUE; | |
150 | } | |
151 | ||
152 | gboolean | |
153 | fu_vli_usbhub_device_i2c_write (FuVliUsbhubDevice *self, guint8 cmd, | |
154 | const guint8 *buf, gsize bufsz, GError **error) | |
155 | { | |
156 | guint8 buf2[10] = { FU_VLI_USBHUB_I2C_ADDR_WRITE, cmd, 0x0 }; | |
157 | if (!fu_memcpy_safe (buf2, sizeof(buf2), 0x2, | |
158 | buf, bufsz, 0x0, bufsz, error)) | |
159 | return FALSE; | |
160 | return fu_vli_usbhub_device_i2c_write_data (self, 0x0, 0x0, buf2, bufsz + 2, error); | |
161 | } | |
162 | ||
83 | 163 | static gboolean |
84 | 164 | fu_vli_usbhub_device_vdr_unlock_813 (FuVliUsbhubDevice *self, GError **error) |
85 | 165 | { |
468 | 548 | return TRUE; |
469 | 549 | } |
470 | 550 | |
471 | static gboolean | |
472 | fu_vli_usbhub_device_erase_sectors (FuVliUsbhubDevice *self, | |
473 | guint32 addr, | |
474 | gsize sz, | |
475 | GError **error) | |
551 | gboolean | |
552 | fu_vli_usbhub_device_spi_erase (FuVliUsbhubDevice *self, | |
553 | guint32 addr, | |
554 | gsize sz, | |
555 | GError **error) | |
476 | 556 | { |
477 | 557 | g_autoptr(GPtrArray) chunks = fu_chunk_array_new (NULL, sz, addr, 0x0, 0x1000); |
558 | g_debug ("erasing 0x%x bytes @0x%x", (guint) sz, addr); | |
478 | 559 | for (guint i = 0; i < chunks->len; i++) { |
479 | 560 | FuChunk *chunk = g_ptr_array_index (chunks, i); |
480 | g_debug ("erasing @0x%x", chunk->address); | |
561 | if (g_getenv ("FWUPD_VLI_USBHUB_VERBOSE") != NULL) | |
562 | g_debug ("erasing @0x%x", chunk->address); | |
481 | 563 | if (!fu_vli_usbhub_device_erase_sector (self, chunk->address, error)) { |
482 | 564 | g_prefix_error (error, |
483 | "failed to erase FW sector @0x%x", | |
565 | "failed to erase FW sector @0x%x: ", | |
484 | 566 | chunk->address); |
485 | 567 | return FALSE; |
486 | 568 | } |
652 | 734 | return TRUE; |
653 | 735 | } |
654 | 736 | |
655 | static GBytes * | |
656 | fu_vli_usbhub_device_dump_firmware (FuVliUsbhubDevice *self, gsize bufsz, GError **error) | |
737 | GBytes * | |
738 | fu_vli_usbhub_device_spi_read (FuVliUsbhubDevice *self, | |
739 | guint32 address, | |
740 | gsize bufsz, | |
741 | GError **error) | |
657 | 742 | { |
658 | 743 | g_autofree guint8 *buf = g_malloc0 (bufsz); |
659 | 744 | g_autoptr(GPtrArray) chunks = NULL; |
660 | 745 | |
661 | 746 | /* get data from hardware */ |
662 | chunks = fu_chunk_array_new (buf, bufsz, 0x0, 0x0, FU_VLI_USBHUB_TXSIZE); | |
747 | chunks = fu_chunk_array_new (buf, bufsz, address, 0x0, FU_VLI_USBHUB_TXSIZE); | |
663 | 748 | for (guint i = 0; i < chunks->len; i++) { |
664 | 749 | FuChunk *chk = g_ptr_array_index (chunks, i); |
665 | 750 | if (!fu_vli_usbhub_device_spi_read_data (self, |
679 | 764 | static gboolean |
680 | 765 | fu_vli_usbhub_device_probe (FuDevice *device, GError **error) |
681 | 766 | { |
767 | guint16 usbver = fu_usb_device_get_spec (FU_USB_DEVICE (device)); | |
768 | ||
682 | 769 | /* quirks now applied... */ |
683 | if (fu_device_has_custom_flag (device, "usb3")) { | |
770 | if (usbver > 0x0300 || fu_device_has_custom_flag (device, "usb3")) { | |
684 | 771 | fu_device_set_summary (device, "USB 3.x Hub"); |
685 | } else if (fu_device_has_custom_flag (device, "usb2")) { | |
772 | } else if (usbver > 0x0200 || fu_device_has_custom_flag (device, "usb2")) { | |
686 | 773 | fu_device_set_summary (device, "USB 2.x Hub"); |
687 | 774 | } else { |
688 | 775 | fu_device_set_summary (device, "USB Hub"); |
689 | 776 | } |
777 | return TRUE; | |
778 | } | |
779 | ||
780 | static gboolean | |
781 | fu_vli_usbhub_device_pd_setup (FuVliUsbhubDevice *self, GError **error) | |
782 | { | |
783 | FuVliUsbhubPdHdr hdr = { 0x0 }; | |
784 | g_autoptr(FuDevice) dev = NULL; | |
785 | g_autoptr(GError) error_local = NULL; | |
786 | ||
787 | /* legacy location */ | |
788 | if (!fu_vli_usbhub_device_spi_read_data (self, | |
789 | VLI_USBHUB_FLASHMAP_ADDR_PD_LEGACY + | |
790 | VLI_USBHUB_PD_FLASHMAP_ADDR_LEGACY, | |
791 | (guint8 *) &hdr, sizeof(hdr), error)) { | |
792 | g_prefix_error (error, "failed to read legacy PD header"); | |
793 | return FALSE; | |
794 | } | |
795 | ||
796 | /* new location */ | |
797 | if (GUINT16_FROM_LE (hdr.vid) != 0x2109) { | |
798 | g_debug ("PD VID was 0x%04x trying new location", | |
799 | GUINT16_FROM_LE (hdr.vid)); | |
800 | if (!fu_vli_usbhub_device_spi_read_data (self, | |
801 | VLI_USBHUB_FLASHMAP_ADDR_PD + | |
802 | VLI_USBHUB_PD_FLASHMAP_ADDR, | |
803 | (guint8 *) &hdr, sizeof(hdr), error)) { | |
804 | g_prefix_error (error, "failed to read PD header"); | |
805 | return FALSE; | |
806 | } | |
807 | } | |
808 | ||
809 | /* emulate until we get actual hardware */ | |
810 | if (g_getenv ("VLI_USBHUB_EMULATE_PD") != NULL) { | |
811 | gsize bufsz = 0; | |
812 | g_autofree gchar *buf = NULL; | |
813 | if (!g_file_get_contents (g_getenv ("VLI_USBHUB_EMULATE_PD"), | |
814 | &buf, &bufsz, error)) | |
815 | return FALSE; | |
816 | if (!fu_memcpy_safe ((guint8 *) &hdr, sizeof(hdr), 0x0, | |
817 | (const guint8 *) buf, bufsz, 0x0, sizeof(hdr), error)) { | |
818 | g_prefix_error (error, "failed to map emulated header: "); | |
819 | return FALSE; | |
820 | } | |
821 | } | |
822 | ||
823 | /* just empty space */ | |
824 | if (hdr.fwver == G_MAXUINT32) { | |
825 | g_debug ("no PD device header found"); | |
826 | return TRUE; | |
827 | } | |
828 | ||
829 | /* add child */ | |
830 | dev = fu_vli_usbhub_pd_device_new (&hdr); | |
831 | if (!fu_device_probe (dev, &error_local)) { | |
832 | g_warning ("cannot create PD device: %s", error_local->message); | |
833 | return TRUE; | |
834 | } | |
835 | fu_device_add_child (FU_DEVICE (self), dev); | |
836 | return TRUE; | |
837 | } | |
838 | ||
839 | static gboolean | |
840 | fu_vli_usbhub_device_i2c_setup (FuVliUsbhubDevice *self, GError **error) | |
841 | { | |
842 | g_autoptr(FuDevice) dev = NULL; | |
843 | g_autoptr(GError) error_local = NULL; | |
844 | ||
845 | /* add child */ | |
846 | dev = fu_vli_usbhub_i2c_device_new (self); | |
847 | if (!fu_device_probe (dev, error)) | |
848 | return FALSE; | |
849 | if (!fu_device_setup (dev, &error_local)) { | |
850 | if (g_error_matches (error_local, | |
851 | FWUPD_ERROR, | |
852 | FWUPD_ERROR_NOT_FOUND)) { | |
853 | g_debug ("%s", error_local->message); | |
854 | } else { | |
855 | g_warning ("cannot create I²C device: %s", | |
856 | error_local->message); | |
857 | } | |
858 | return TRUE; | |
859 | } | |
860 | fu_device_add_child (FU_DEVICE (self), dev); | |
690 | 861 | return TRUE; |
691 | 862 | } |
692 | 863 | |
791 | 962 | } |
792 | 963 | } |
793 | 964 | |
965 | /* detect the PD child */ | |
966 | if (!fu_vli_usbhub_device_pd_setup (self, error)) | |
967 | return FALSE; | |
968 | ||
969 | /* detect the PD child */ | |
970 | if (!fu_vli_usbhub_device_i2c_setup (self, error)) | |
971 | return FALSE; | |
972 | ||
794 | 973 | /* success */ |
795 | 974 | return TRUE; |
796 | 975 | } |
908 | 1087 | } |
909 | 1088 | |
910 | 1089 | /* write */ |
1090 | if (g_getenv ("FWUPD_VLI_USBHUB_VERBOSE") != NULL) | |
1091 | g_debug ("writing 0x%x block @0x%x", (guint) bufsz, address); | |
911 | 1092 | if (!fu_vli_usbhub_device_spi_write_enable (self, error)) { |
912 | 1093 | g_prefix_error (error, "enabling SPI write failed: "); |
913 | 1094 | return FALSE; |
926 | 1107 | return fu_common_bytes_compare_raw (buf, bufsz, buf_tmp, bufsz, error); |
927 | 1108 | } |
928 | 1109 | |
929 | static gboolean | |
930 | fu_vli_usbhub_device_write_blocks (FuVliUsbhubDevice *self, | |
931 | guint32 address, | |
932 | const guint8 *buf, | |
933 | gsize bufsz, | |
934 | GError **error) | |
1110 | gboolean | |
1111 | fu_vli_usbhub_device_spi_write (FuVliUsbhubDevice *self, | |
1112 | guint32 address, | |
1113 | const guint8 *buf, | |
1114 | gsize bufsz, | |
1115 | GError **error) | |
935 | 1116 | { |
936 | 1117 | FuChunk *chk; |
937 | 1118 | g_autoptr(GPtrArray) chunks = NULL; |
938 | 1119 | |
939 | 1120 | /* write SPI data, then CRC bytes last */ |
1121 | g_debug ("writing 0x%x bytes @0x%x", (guint) bufsz, address); | |
940 | 1122 | chunks = fu_chunk_array_new (buf, bufsz, 0x0, 0x0, FU_VLI_USBHUB_TXSIZE); |
941 | 1123 | if (chunks->len > 1) { |
942 | 1124 | for (guint i = 1; i < chunks->len; i++) { |
991 | 1173 | /* write in chunks */ |
992 | 1174 | fu_device_set_status (FU_DEVICE (self), FWUPD_STATUS_DEVICE_WRITE); |
993 | 1175 | buf = g_bytes_get_data (fw, &bufsz); |
994 | if (!fu_vli_usbhub_device_write_blocks (self, 0x0, buf, bufsz, error)) | |
1176 | if (!fu_vli_usbhub_device_spi_write (self, 0x0, buf, bufsz, error)) | |
995 | 1177 | return FALSE; |
996 | 1178 | |
997 | 1179 | /* success */ |
1016 | 1198 | |
1017 | 1199 | /* write in chunks */ |
1018 | 1200 | fu_device_set_status (FU_DEVICE (self), FWUPD_STATUS_DEVICE_WRITE); |
1019 | if (!fu_vli_usbhub_device_write_blocks (self, VLI_USBHUB_FLASHMAP_ADDR_HD1, | |
1020 | buf, bufsz, error)) | |
1201 | if (!fu_vli_usbhub_device_spi_write (self, VLI_USBHUB_FLASHMAP_ADDR_HD1, | |
1202 | buf, bufsz, error)) | |
1021 | 1203 | return FALSE; |
1022 | 1204 | |
1023 | 1205 | /* success */ |
1149 | 1331 | |
1150 | 1332 | /* make space */ |
1151 | 1333 | fu_device_set_status (FU_DEVICE (self), FWUPD_STATUS_DEVICE_ERASE); |
1152 | if (!fu_vli_usbhub_device_erase_sectors (self, hd2_fw_addr, hd2_fw_sz, error)) | |
1334 | if (!fu_vli_usbhub_device_spi_erase (self, hd2_fw_addr, hd2_fw_sz, error)) | |
1153 | 1335 | return FALSE; |
1154 | 1336 | |
1155 | 1337 | /* perform the actual write */ |
1156 | 1338 | fu_device_set_status (FU_DEVICE (self), FWUPD_STATUS_DEVICE_WRITE); |
1157 | if (!fu_vli_usbhub_device_write_blocks (self, | |
1158 | hd2_fw_addr, | |
1159 | buf_fw + hd2_fw_offset, | |
1160 | hd2_fw_sz, | |
1161 | error)) { | |
1339 | if (!fu_vli_usbhub_device_spi_write (self, | |
1340 | hd2_fw_addr, | |
1341 | buf_fw + hd2_fw_offset, | |
1342 | hd2_fw_sz, | |
1343 | error)) { | |
1162 | 1344 | g_prefix_error (error, "failed to write payload: "); |
1163 | 1345 | return FALSE; |
1164 | 1346 | } |
1190 | 1372 | } |
1191 | 1373 | |
1192 | 1374 | /* success */ |
1193 | fu_device_add_flag (FU_DEVICE (self), FWUPD_DEVICE_FLAG_IS_BOOTLOADER); | |
1194 | 1375 | return TRUE; |
1195 | 1376 | } |
1196 | 1377 | |
1200 | 1381 | FuVliUsbhubDevice *self = FU_VLI_USBHUB_DEVICE (device); |
1201 | 1382 | g_autoptr(GBytes) fw = NULL; |
1202 | 1383 | fu_device_set_status (FU_DEVICE (self), FWUPD_STATUS_DEVICE_VERIFY); |
1203 | fw = fu_vli_usbhub_device_dump_firmware (self, fu_device_get_firmware_size_max (device), error); | |
1384 | fw = fu_vli_usbhub_device_spi_read (self, 0x0, | |
1385 | fu_device_get_firmware_size_max (device), | |
1386 | error); | |
1204 | 1387 | if (fw == NULL) |
1205 | 1388 | return NULL; |
1206 | 1389 | return fu_firmware_new_from_bytes (fw); |
7 | 7 | |
8 | 8 | #include "fu-plugin.h" |
9 | 9 | |
10 | #include "fu-vli-usbhub-i2c-common.h" | |
11 | ||
10 | 12 | #define FU_TYPE_VLI_USBHUB_DEVICE (fu_vli_usbhub_device_get_type ()) |
11 | 13 | G_DECLARE_FINAL_TYPE (FuVliUsbhubDevice, fu_vli_usbhub_device, FU, VLI_USBHUB_DEVICE, FuUsbDevice) |
12 | 14 | |
14 | 16 | { |
15 | 17 | FuUsbDeviceClass parent_class; |
16 | 18 | }; |
19 | ||
20 | gboolean fu_vli_usbhub_device_spi_erase (FuVliUsbhubDevice *self, | |
21 | guint32 addr, | |
22 | gsize sz, | |
23 | GError **error); | |
24 | gboolean fu_vli_usbhub_device_spi_write (FuVliUsbhubDevice *self, | |
25 | guint32 address, | |
26 | const guint8 *buf, | |
27 | gsize bufsz, | |
28 | GError **error); | |
29 | GBytes *fu_vli_usbhub_device_spi_read (FuVliUsbhubDevice *self, | |
30 | guint32 address, | |
31 | gsize bufsz, | |
32 | GError **error); | |
33 | gboolean fu_vli_usbhub_device_i2c_read (FuVliUsbhubDevice *self, | |
34 | guint8 cmd, | |
35 | guint8 *buf, | |
36 | gsize bufsz, | |
37 | GError **error); | |
38 | gboolean fu_vli_usbhub_device_i2c_read_status (FuVliUsbhubDevice *self, | |
39 | FuVliUsbhubI2cStatus *status, | |
40 | GError **error); | |
41 | gboolean fu_vli_usbhub_device_i2c_write (FuVliUsbhubDevice *self, | |
42 | guint8 cmd, | |
43 | const guint8 *buf, | |
44 | gsize bufsz, | |
45 | GError **error); | |
46 | gboolean fu_vli_usbhub_device_i2c_write_data (FuVliUsbhubDevice *self, | |
47 | guint8 skip_s, | |
48 | guint8 skip_p, | |
49 | const guint8 *buf, | |
50 | gsize bufsz, | |
51 | GError **error); |
0 | /* | |
1 | * Copyright (C) 2017-2019 VIA Corporation | |
2 | * Copyright (C) 2019 Richard Hughes <richard@hughsie.com> | |
3 | * | |
4 | * SPDX-License-Identifier: LGPL-2.1+ | |
5 | */ | |
6 | ||
7 | #include "config.h" | |
8 | ||
9 | #include "fu-vli-usbhub-i2c-common.h" | |
10 | ||
11 | const gchar * | |
12 | fu_vli_usbhub_i2c_chip_to_string (FuVliUsbhubI2cChip chip) | |
13 | { | |
14 | if (chip == FU_VLI_USBHUB_I2C_CHIP_MSP430) | |
15 | return "MSP430"; | |
16 | return NULL; | |
17 | } | |
18 | ||
19 | gboolean | |
20 | fu_vli_usbhub_i2c_check_status (FuVliUsbhubI2cStatus status, GError **error) | |
21 | { | |
22 | if (status == FU_VLI_USBHUB_I2C_STATUS_OK) | |
23 | return TRUE; | |
24 | if (status == FU_VLI_USBHUB_I2C_STATUS_HEADER) { | |
25 | g_set_error_literal (error, | |
26 | FWUPD_ERROR, | |
27 | FWUPD_ERROR_INTERNAL, | |
28 | "Incorrect header value of data frame"); | |
29 | return FALSE; | |
30 | } | |
31 | if (status == FU_VLI_USBHUB_I2C_STATUS_COMMAND) { | |
32 | g_set_error_literal (error, | |
33 | FWUPD_ERROR, | |
34 | FWUPD_ERROR_INTERNAL, | |
35 | "Invalid command data"); | |
36 | return FALSE; | |
37 | } | |
38 | if (status == FU_VLI_USBHUB_I2C_STATUS_ADDRESS) { | |
39 | g_set_error_literal (error, | |
40 | FWUPD_ERROR, | |
41 | FWUPD_ERROR_INTERNAL, | |
42 | "Invalid address range"); | |
43 | return FALSE; | |
44 | } | |
45 | if (status == FU_VLI_USBHUB_I2C_STATUS_PACKETSIZE) { | |
46 | g_set_error_literal (error, | |
47 | FWUPD_ERROR, | |
48 | FWUPD_ERROR_INTERNAL, | |
49 | "Incorrect payload data length"); | |
50 | return FALSE; | |
51 | } | |
52 | if (status == FU_VLI_USBHUB_I2C_STATUS_CHECKSUM) { | |
53 | g_set_error_literal (error, | |
54 | FWUPD_ERROR, | |
55 | FWUPD_ERROR_INTERNAL, | |
56 | "Incorrect frame data checksum"); | |
57 | return FALSE; | |
58 | } | |
59 | g_set_error (error, | |
60 | FWUPD_ERROR, | |
61 | FWUPD_ERROR_INTERNAL, | |
62 | "Unknown error [0x%02x]", status); | |
63 | return FALSE; | |
64 | } |
0 | /* | |
1 | * Copyright (C) 2017-2019 VIA Corporation | |
2 | * Copyright (C) 2019 Richard Hughes <richard@hughsie.com> | |
3 | * | |
4 | * SPDX-License-Identifier: LGPL-2.1+ | |
5 | */ | |
6 | ||
7 | #pragma once | |
8 | ||
9 | #include "fu-plugin.h" | |
10 | ||
11 | typedef enum { | |
12 | FU_VLI_USBHUB_I2C_CHIP_UNKNOWN, | |
13 | FU_VLI_USBHUB_I2C_CHIP_MSP430, | |
14 | } FuVliUsbhubI2cChip; | |
15 | ||
16 | typedef enum { | |
17 | FU_VLI_USBHUB_I2C_STATUS_OK, | |
18 | FU_VLI_USBHUB_I2C_STATUS_HEADER, | |
19 | FU_VLI_USBHUB_I2C_STATUS_COMMAND, | |
20 | FU_VLI_USBHUB_I2C_STATUS_ADDRESS, | |
21 | FU_VLI_USBHUB_I2C_STATUS_PACKETSIZE, | |
22 | FU_VLI_USBHUB_I2C_STATUS_CHECKSUM, | |
23 | } FuVliUsbhubI2cStatus; | |
24 | ||
25 | /* Texas Instruments BSL */ | |
26 | #define FU_VLI_USBHUB_I2C_ADDR_WRITE 0x18 | |
27 | #define FU_VLI_USBHUB_I2C_ADDR_READ 0x19 | |
28 | ||
29 | #define FU_VLI_USBHUB_I2C_CMD_WRITE 0x32 | |
30 | #define FU_VLI_USBHUB_I2C_CMD_READ_STATUS 0x33 | |
31 | #define FU_VLI_USBHUB_I2C_CMD_UPGRADE 0x34 | |
32 | #define FU_VLI_USBHUB_I2C_CMD_READ_VERSIONS 0x40 | |
33 | ||
34 | #define FU_VLI_USBHUB_I2C_R_VDR 0xa0 /* read vendor comand */ | |
35 | #define FU_VLI_USBHUB_I2C_W_VDR 0xb0 /* write vendor comand */ | |
36 | ||
37 | const gchar *fu_vli_usbhub_i2c_chip_to_string (FuVliUsbhubI2cChip chip); | |
38 | gboolean fu_vli_usbhub_i2c_check_status (FuVliUsbhubI2cStatus status, | |
39 | GError **error); |
0 | /* | |
1 | * Copyright (C) 2017-2019 VIA Corporation | |
2 | * Copyright (C) 2019 Richard Hughes <richard@hughsie.com> | |
3 | * | |
4 | * SPDX-License-Identifier: LGPL-2.1+ | |
5 | */ | |
6 | ||
7 | #include "config.h" | |
8 | ||
9 | #include "fu-firmware-common.h" | |
10 | #include "fu-ihex-firmware.h" | |
11 | ||
12 | #include "fu-vli-usbhub-common.h" | |
13 | #include "fu-vli-usbhub-device.h" | |
14 | #include "fu-vli-usbhub-i2c-common.h" | |
15 | #include "fu-vli-usbhub-i2c-device.h" | |
16 | ||
17 | struct _FuVliUsbhubI2cDevice | |
18 | { | |
19 | FuDevice parent_instance; | |
20 | FuVliUsbhubDevice *parent; | |
21 | FuVliUsbhubI2cChip chip; | |
22 | }; | |
23 | ||
24 | G_DEFINE_TYPE (FuVliUsbhubI2cDevice, fu_vli_usbhub_i2c_device, FU_TYPE_DEVICE) | |
25 | ||
26 | static void | |
27 | fu_vli_usbhub_i2c_device_to_string (FuDevice *device, guint idt, GString *str) | |
28 | { | |
29 | FuVliUsbhubI2cDevice *self = FU_VLI_USBHUB_I2C_DEVICE (device); | |
30 | fu_common_string_append_kv (str, idt, "ChipId", | |
31 | fu_vli_usbhub_i2c_chip_to_string (self->chip)); | |
32 | } | |
33 | ||
34 | static gboolean | |
35 | fu_vli_usbhub_i2c_device_setup (FuDevice *device, GError **error) | |
36 | { | |
37 | FuVliUsbhubI2cDevice *self = FU_VLI_USBHUB_I2C_DEVICE (device); | |
38 | guint8 buf[11] = { 0x0 }; | |
39 | g_autofree gchar *instance_id = NULL; | |
40 | g_autofree gchar *version = NULL; | |
41 | g_autoptr(FuVliUsbhubDevice) parent = g_steal_pointer (&self->parent); | |
42 | ||
43 | /* get versions */ | |
44 | if (!fu_vli_usbhub_device_i2c_read (parent, | |
45 | FU_VLI_USBHUB_I2C_CMD_READ_VERSIONS, | |
46 | buf, sizeof(buf), error)) { | |
47 | g_prefix_error (error, "failed to read versions: "); | |
48 | return FALSE; | |
49 | } | |
50 | if ((buf[0] == 0x00 && buf[1] == 0x00 && buf[2] == 0x00) || | |
51 | (buf[0] == 0xff && buf[1] == 0xff && buf[2] == 0xff)) { | |
52 | g_set_error (error, | |
53 | FWUPD_ERROR, | |
54 | FWUPD_ERROR_NOT_FOUND, | |
55 | "no %s device detected", | |
56 | fu_vli_usbhub_i2c_chip_to_string (self->chip)); | |
57 | return FALSE; | |
58 | } | |
59 | ||
60 | /* add instance ID */ | |
61 | instance_id = g_strdup_printf ("VLI_USBHUB_I2C\\%s", | |
62 | fu_vli_usbhub_i2c_chip_to_string (self->chip)); | |
63 | fu_device_add_instance_id (device, instance_id); | |
64 | ||
65 | /* set version */ | |
66 | version = g_strdup_printf ("%x.%x.%x", buf[0], buf[1], buf[2]); | |
67 | fu_device_set_version (device, version, FWUPD_VERSION_FORMAT_TRIPLET); | |
68 | return TRUE; | |
69 | } | |
70 | ||
71 | static gboolean | |
72 | fu_vli_usbhub_i2c_device_detach (FuDevice *device, GError **error) | |
73 | { | |
74 | FuVliUsbhubDevice *parent = FU_VLI_USBHUB_DEVICE (fu_device_get_parent (device)); | |
75 | const guint8 buf[] = { | |
76 | FU_VLI_USBHUB_I2C_ADDR_WRITE, | |
77 | FU_VLI_USBHUB_I2C_CMD_UPGRADE, | |
78 | }; | |
79 | if (!fu_vli_usbhub_device_i2c_write_data (parent, 0, 0, buf, sizeof(buf), error)) | |
80 | return FALSE; | |
81 | ||
82 | /* avoid power instability */ | |
83 | fu_device_set_status (device, FWUPD_STATUS_DEVICE_RESTART); | |
84 | g_usleep (5000); | |
85 | ||
86 | /* success */ | |
87 | return TRUE; | |
88 | } | |
89 | ||
90 | static FuFirmware * | |
91 | fu_vli_usbhub_i2c_device_prepare_firmware (FuDevice *device, | |
92 | GBytes *fw, | |
93 | FwupdInstallFlags flags, | |
94 | GError **error) | |
95 | { | |
96 | g_autoptr(FuFirmware) firmware = fu_ihex_firmware_new (); | |
97 | fu_device_set_status (device, FWUPD_STATUS_DECOMPRESSING); | |
98 | if (!fu_firmware_tokenize (firmware, fw, flags, error)) | |
99 | return NULL; | |
100 | return g_steal_pointer (&firmware); | |
101 | } | |
102 | ||
103 | static gboolean | |
104 | fu_vli_usbhub_i2c_device_write_firmware (FuDevice *device, | |
105 | FuFirmware *firmware, | |
106 | FwupdInstallFlags flags, | |
107 | GError **error) | |
108 | { | |
109 | FuVliUsbhubDevice *parent = FU_VLI_USBHUB_DEVICE (fu_device_get_parent (device)); | |
110 | GPtrArray *records = fu_ihex_firmware_get_records (FU_IHEX_FIRMWARE (firmware)); | |
111 | guint16 usbver = fu_usb_device_get_spec (FU_USB_DEVICE (device)); | |
112 | g_autoptr(FuDeviceLocker) locker = NULL; | |
113 | ||
114 | /* open device */ | |
115 | locker = fu_device_locker_new (parent, error); | |
116 | if (locker == NULL) | |
117 | return FALSE; | |
118 | ||
119 | /* transfer by I²C write, and check status by I²C read */ | |
120 | fu_device_set_status (device, FWUPD_STATUS_DEVICE_WRITE); | |
121 | for (guint j = 0; j < records->len; j++) { | |
122 | FuIhexFirmwareRecord *rcd = g_ptr_array_index (records, j); | |
123 | const gchar *line = rcd->buf->str; | |
124 | gsize bufsz; | |
125 | guint8 buf[0x40] = { 0x0 }; | |
126 | guint8 req_len; | |
127 | guint retry; | |
128 | ||
129 | /* check there's enough data for the smallest possible record */ | |
130 | if (rcd->buf->len < 11) { | |
131 | g_set_error (error, | |
132 | FWUPD_ERROR, | |
133 | FWUPD_ERROR_INVALID_FILE, | |
134 | "line %u is incomplete, length %u", | |
135 | rcd->ln, (guint) rcd->buf->len); | |
136 | return FALSE; | |
137 | } | |
138 | ||
139 | /* check starting token */ | |
140 | if (line[0] != ':') { | |
141 | g_set_error (error, | |
142 | FWUPD_ERROR, | |
143 | FWUPD_ERROR_INVALID_FILE, | |
144 | "invalid starting token on line %u: %s", | |
145 | rcd->ln, line); | |
146 | return FALSE; | |
147 | } | |
148 | ||
149 | /* length, 16-bit address, type */ | |
150 | req_len = fu_firmware_strparse_uint8 (line + 1); | |
151 | if (req_len > 64) { | |
152 | g_set_error_literal (error, | |
153 | FWUPD_ERROR, | |
154 | FWUPD_ERROR_NOT_SUPPORTED, | |
155 | "max write is 64 bytes"); | |
156 | return FALSE; | |
157 | } | |
158 | if (9 + (guint) req_len * 2 > (guint) rcd->buf->len) { | |
159 | g_set_error (error, | |
160 | FWUPD_ERROR, | |
161 | FWUPD_ERROR_INVALID_FILE, | |
162 | "line %u malformed", rcd->ln); | |
163 | return FALSE; | |
164 | } | |
165 | ||
166 | /* write each record directly to the hardware */ | |
167 | buf[0] = FU_VLI_USBHUB_I2C_ADDR_WRITE; | |
168 | buf[1] = FU_VLI_USBHUB_I2C_CMD_WRITE; | |
169 | buf[2] = 0x3a; /* ':' */ | |
170 | buf[3] = req_len; | |
171 | buf[4] = fu_firmware_strparse_uint8 (line + 3); | |
172 | buf[5] = fu_firmware_strparse_uint8 (line + 5); | |
173 | buf[6] = fu_firmware_strparse_uint8 (line + 7); | |
174 | for (guint8 i = 0; i < req_len; i++) | |
175 | buf[7 + i] = fu_firmware_strparse_uint8 (line + 9 + (i * 2)); | |
176 | buf[7 + req_len] = fu_firmware_strparse_uint8 (line + 9+ (req_len * 2)); | |
177 | bufsz = req_len + 8; | |
178 | ||
179 | for (retry = 0; retry < 5; retry++) { | |
180 | FuVliUsbhubI2cStatus status = 0xff; | |
181 | g_autoptr(GError) error_local = NULL; | |
182 | ||
183 | g_usleep (5 * 1000); | |
184 | if (usbver >= 0x0300 || bufsz <= 32) { | |
185 | if (!fu_vli_usbhub_device_i2c_write_data (parent, | |
186 | 0, 0, | |
187 | buf, | |
188 | bufsz, | |
189 | error)) | |
190 | return FALSE; | |
191 | } else { | |
192 | /* for U2, hub data buffer <= 32 bytes */ | |
193 | if (!fu_vli_usbhub_device_i2c_write_data (parent, | |
194 | 0, 1, | |
195 | buf, | |
196 | 32, | |
197 | error)) | |
198 | return FALSE; | |
199 | if (!fu_vli_usbhub_device_i2c_write_data (parent, | |
200 | 1, 0, | |
201 | buf + 32, | |
202 | bufsz - 32, | |
203 | error)) | |
204 | return FALSE; | |
205 | } | |
206 | ||
207 | /* end of file, no need to check status */ | |
208 | if (req_len == 0 && buf[6] == 0x01 && buf[7] == 0xFF) | |
209 | break; | |
210 | ||
211 | /* read data to check status */ | |
212 | g_usleep (5 * 1000); | |
213 | if (!fu_vli_usbhub_device_i2c_read_status (parent, | |
214 | &status, | |
215 | error)) | |
216 | return FALSE; | |
217 | if (!fu_vli_usbhub_i2c_check_status (status, &error_local)) { | |
218 | g_warning ("error on try %u: %s", | |
219 | retry + 1, error_local->message); | |
220 | } else { | |
221 | break; | |
222 | } | |
223 | } | |
224 | if (retry >= 5) { | |
225 | g_set_error_literal (error, | |
226 | G_IO_ERROR, | |
227 | G_IO_ERROR_NOT_SUPPORTED, | |
228 | "I²C status retry failed"); | |
229 | return FALSE; | |
230 | } | |
231 | fu_device_set_progress_full (device, (gsize) j, (gsize) records->len); | |
232 | } | |
233 | ||
234 | /* success */ | |
235 | return TRUE; | |
236 | } | |
237 | ||
238 | static gboolean | |
239 | fu_vli_usbhub_i2c_device_probe (FuDevice *device, GError **error) | |
240 | { | |
241 | FuVliUsbhubI2cDevice *self = FU_VLI_USBHUB_I2C_DEVICE (device); | |
242 | self->chip = FU_VLI_USBHUB_I2C_CHIP_MSP430; | |
243 | fu_device_set_name (device, fu_vli_usbhub_i2c_chip_to_string (self->chip)); | |
244 | return TRUE; | |
245 | } | |
246 | ||
247 | static void | |
248 | fu_vli_usbhub_i2c_device_init (FuVliUsbhubI2cDevice *self) | |
249 | { | |
250 | fu_device_add_icon (FU_DEVICE (self), "audio-card"); | |
251 | fu_device_add_flag (FU_DEVICE (self), FWUPD_DEVICE_FLAG_UPDATABLE); | |
252 | fu_device_set_logical_id (FU_DEVICE (self), "I2C"); | |
253 | fu_device_set_summary (FU_DEVICE (self), "I²C Dock Management Device"); | |
254 | } | |
255 | ||
256 | static void | |
257 | fu_vli_usbhub_i2c_device_finalize (GObject *object) | |
258 | { | |
259 | FuVliUsbhubI2cDevice *self = FU_VLI_USBHUB_I2C_DEVICE (object); | |
260 | g_clear_object (&self->parent); | |
261 | G_OBJECT_CLASS (fu_vli_usbhub_i2c_device_parent_class)->finalize (object); | |
262 | } | |
263 | ||
264 | static void | |
265 | fu_vli_usbhub_i2c_device_class_init (FuVliUsbhubI2cDeviceClass *klass) | |
266 | { | |
267 | GObjectClass *object_class = G_OBJECT_CLASS (klass); | |
268 | FuDeviceClass *klass_device = FU_DEVICE_CLASS (klass); | |
269 | object_class->finalize = fu_vli_usbhub_i2c_device_finalize; | |
270 | klass_device->to_string = fu_vli_usbhub_i2c_device_to_string; | |
271 | klass_device->probe = fu_vli_usbhub_i2c_device_probe; | |
272 | klass_device->setup = fu_vli_usbhub_i2c_device_setup; | |
273 | klass_device->detach = fu_vli_usbhub_i2c_device_detach; | |
274 | klass_device->write_firmware = fu_vli_usbhub_i2c_device_write_firmware; | |
275 | klass_device->prepare_firmware = fu_vli_usbhub_i2c_device_prepare_firmware; | |
276 | } | |
277 | ||
278 | FuDevice * | |
279 | fu_vli_usbhub_i2c_device_new (FuVliUsbhubDevice *parent) | |
280 | { | |
281 | FuVliUsbhubI2cDevice *self = g_object_new (FU_TYPE_VLI_USBHUB_I2C_DEVICE, NULL); | |
282 | self->parent = g_object_ref (parent); | |
283 | return FU_DEVICE (self); | |
284 | } |
0 | /* | |
1 | * Copyright (C) 2019 Richard Hughes <richard@hughsie.com> | |
2 | * | |
3 | * SPDX-License-Identifier: LGPL-2.1+ | |
4 | */ | |
5 | ||
6 | #pragma once | |
7 | ||
8 | #include "fu-plugin.h" | |
9 | ||
10 | #define FU_TYPE_VLI_USBHUB_I2C_DEVICE (fu_vli_usbhub_i2c_device_get_type ()) | |
11 | G_DECLARE_FINAL_TYPE (FuVliUsbhubI2cDevice, fu_vli_usbhub_i2c_device, FU, VLI_USBHUB_I2C_DEVICE, FuDevice) | |
12 | ||
13 | struct _FuVliUsbhubI2cDeviceClass | |
14 | { | |
15 | FuDeviceClass parent_class; | |
16 | }; | |
17 | ||
18 | FuDevice *fu_vli_usbhub_i2c_device_new (FuVliUsbhubDevice *parent); |
0 | /* | |
1 | * Copyright (C) 2017-2019 VIA Corporation | |
2 | * Copyright (C) 2019 Richard Hughes <richard@hughsie.com> | |
3 | * | |
4 | * SPDX-License-Identifier: LGPL-2.1+ | |
5 | */ | |
6 | ||
7 | #include "config.h" | |
8 | ||
9 | #include "fu-vli-usbhub-pd-common.h" | |
10 | ||
11 | guint16 | |
12 | fu_vli_usbhub_pd_crc16 (const guint8 *buf, gsize bufsz) | |
13 | { | |
14 | guint16 crc = 0xffff; | |
15 | for (gsize len = bufsz; len > 0; len--) { | |
16 | crc = (guint16) (crc ^ (*buf++)); | |
17 | for (guint8 i = 0; i < 8; i++) { | |
18 | if (crc & 0x1) { | |
19 | crc = (crc >> 1) ^ 0xa001; | |
20 | } else { | |
21 | crc >>= 1; | |
22 | } | |
23 | } | |
24 | } | |
25 | return ~crc; | |
26 | } | |
27 | ||
28 | FuVliUsbhubPdChip | |
29 | fu_vli_usbhub_pd_guess_chip (guint32 fwver) | |
30 | { | |
31 | guint32 tmp = (fwver & 0x0f000000) >> 24; | |
32 | if (tmp == 0x01 || tmp == 0x02 || tmp == 0x03) | |
33 | return FU_VLI_USBHUB_PD_CHIP_VL100; | |
34 | if (tmp == 0x04 || tmp == 0x05 || tmp == 0x06) | |
35 | return FU_VLI_USBHUB_PD_CHIP_VL101; | |
36 | if (tmp == 0x07 || tmp == 0x08) | |
37 | return FU_VLI_USBHUB_PD_CHIP_VL102; | |
38 | if (tmp == 0x09 || tmp == 0x0a) | |
39 | return FU_VLI_USBHUB_PD_CHIP_VL103; | |
40 | if (tmp == 0x0b) | |
41 | return FU_VLI_USBHUB_PD_CHIP_VL104; | |
42 | if (tmp == 0x0c) | |
43 | return FU_VLI_USBHUB_PD_CHIP_VL105; | |
44 | return FU_VLI_USBHUB_PD_CHIP_UNKNOWN; | |
45 | } | |
46 | ||
47 | const gchar * | |
48 | fu_vli_usbhub_pd_chip_to_string (FuVliUsbhubPdChip chip) | |
49 | { | |
50 | if (chip == FU_VLI_USBHUB_PD_CHIP_VL100) | |
51 | return "VL100"; | |
52 | if (chip == FU_VLI_USBHUB_PD_CHIP_VL101) | |
53 | return "VL101"; | |
54 | if (chip == FU_VLI_USBHUB_PD_CHIP_VL102) | |
55 | return "VL102"; | |
56 | if (chip == FU_VLI_USBHUB_PD_CHIP_VL103) | |
57 | return "VL103"; | |
58 | if (chip == FU_VLI_USBHUB_PD_CHIP_VL104) | |
59 | return "VL104"; | |
60 | if (chip == FU_VLI_USBHUB_PD_CHIP_VL105) | |
61 | return "VL105"; | |
62 | return NULL; | |
63 | } | |
64 | ||
65 | guint32 | |
66 | fu_vli_usbhub_pd_chip_get_offset (FuVliUsbhubPdChip chip) | |
67 | { | |
68 | if (chip == FU_VLI_USBHUB_PD_CHIP_VL100) | |
69 | return 0x10000; | |
70 | if (chip == FU_VLI_USBHUB_PD_CHIP_VL101) | |
71 | return 0x10000; | |
72 | if (chip == FU_VLI_USBHUB_PD_CHIP_VL102) | |
73 | return 0x20000; | |
74 | if (chip == FU_VLI_USBHUB_PD_CHIP_VL103) | |
75 | return 0x20000; | |
76 | if (chip == FU_VLI_USBHUB_PD_CHIP_VL104) | |
77 | return 0x20000; | |
78 | if (chip == FU_VLI_USBHUB_PD_CHIP_VL105) | |
79 | return 0x20000; | |
80 | return 0x0; | |
81 | } | |
82 | ||
83 | guint32 | |
84 | fu_vli_usbhub_pd_chip_get_size (FuVliUsbhubPdChip chip) | |
85 | { | |
86 | if (chip == FU_VLI_USBHUB_PD_CHIP_VL100) | |
87 | return 0x8000; /* 32KB */ | |
88 | if (chip == FU_VLI_USBHUB_PD_CHIP_VL101) | |
89 | return 0xc000; /* 48KB */ | |
90 | if (chip == FU_VLI_USBHUB_PD_CHIP_VL102) | |
91 | return 0x8000; /* 32KB */ | |
92 | if (chip == FU_VLI_USBHUB_PD_CHIP_VL103) | |
93 | return 0x8000; /* 32KB */ | |
94 | if (chip == FU_VLI_USBHUB_PD_CHIP_VL104) | |
95 | return 0xc000; /* 48KB */ | |
96 | if (chip == FU_VLI_USBHUB_PD_CHIP_VL105) | |
97 | return 0xc000; /* 48KB */ | |
98 | return 0x0; | |
99 | } |
0 | /* | |
1 | * Copyright (C) 2017-2019 VIA Corporation | |
2 | * Copyright (C) 2019 Richard Hughes <richard@hughsie.com> | |
3 | * | |
4 | * SPDX-License-Identifier: LGPL-2.1+ | |
5 | */ | |
6 | ||
7 | #pragma once | |
8 | ||
9 | #include "fu-plugin.h" | |
10 | ||
11 | typedef enum { | |
12 | FU_VLI_USBHUB_PD_CHIP_UNKNOWN = 0x0, | |
13 | FU_VLI_USBHUB_PD_CHIP_VL100 = 0x100, | |
14 | FU_VLI_USBHUB_PD_CHIP_VL101 = 0x101, | |
15 | FU_VLI_USBHUB_PD_CHIP_VL102 = 0x102, | |
16 | FU_VLI_USBHUB_PD_CHIP_VL103 = 0x103, | |
17 | FU_VLI_USBHUB_PD_CHIP_VL104 = 0x104, | |
18 | FU_VLI_USBHUB_PD_CHIP_VL105 = 0x105, | |
19 | } FuVliUsbhubPdChip; | |
20 | ||
21 | typedef struct __attribute__ ((packed)) { | |
22 | guint32 fwver; /* BE */ | |
23 | guint16 vid; /* LE */ | |
24 | guint16 pid; /* LE */ | |
25 | } FuVliUsbhubPdHdr; | |
26 | ||
27 | #define VLI_USBHUB_PD_FLASHMAP_ADDR_LEGACY 0x4000 | |
28 | #define VLI_USBHUB_PD_FLASHMAP_ADDR 0x1003 | |
29 | ||
30 | guint16 fu_vli_usbhub_pd_crc16 (const guint8 *buf, | |
31 | gsize bufsz); | |
32 | guint32 fu_vli_usbhub_pd_chip_get_offset (FuVliUsbhubPdChip chip); | |
33 | guint32 fu_vli_usbhub_pd_chip_get_size (FuVliUsbhubPdChip chip); | |
34 | const gchar *fu_vli_usbhub_pd_chip_to_string (FuVliUsbhubPdChip chip); | |
35 | FuVliUsbhubPdChip fu_vli_usbhub_pd_guess_chip (guint32 fwver); |
0 | /* | |
1 | * Copyright (C) 2017-2019 VIA Corporation | |
2 | * Copyright (C) 2019 Richard Hughes <richard@hughsie.com> | |
3 | * | |
4 | * SPDX-License-Identifier: LGPL-2.1+ | |
5 | */ | |
6 | ||
7 | #include "config.h" | |
8 | ||
9 | #include <string.h> | |
10 | ||
11 | #include "fu-vli-usbhub-common.h" | |
12 | #include "fu-vli-usbhub-device.h" | |
13 | #include "fu-vli-usbhub-pd-common.h" | |
14 | #include "fu-vli-usbhub-pd-device.h" | |
15 | #include "fu-vli-usbhub-pd-firmware.h" | |
16 | ||
17 | struct _FuVliUsbhubPdDevice | |
18 | { | |
19 | FuDevice parent_instance; | |
20 | FuVliUsbhubPdHdr hdr; | |
21 | FuVliUsbhubPdChip chip; | |
22 | }; | |
23 | ||
24 | G_DEFINE_TYPE (FuVliUsbhubPdDevice, fu_vli_usbhub_pd_device, FU_TYPE_DEVICE) | |
25 | ||
26 | static void | |
27 | fu_vli_usbhub_pd_device_to_string (FuDevice *device, guint idt, GString *str) | |
28 | { | |
29 | FuVliUsbhubPdDevice *self = FU_VLI_USBHUB_PD_DEVICE (device); | |
30 | fu_common_string_append_kv (str, idt, "ChipId", | |
31 | fu_vli_usbhub_pd_chip_to_string (self->chip)); | |
32 | fu_common_string_append_kx (str, idt, "FwOffset", | |
33 | fu_vli_usbhub_pd_chip_get_offset (self->chip)); | |
34 | fu_common_string_append_kx (str, idt, "FwSize", | |
35 | fu_vli_usbhub_pd_chip_get_size (self->chip)); | |
36 | } | |
37 | ||
38 | static gboolean | |
39 | fu_vli_usbhub_pd_device_probe (FuDevice *device, GError **error) | |
40 | { | |
41 | FuVliUsbhubPdDevice *self = FU_VLI_USBHUB_PD_DEVICE (device); | |
42 | ||
43 | guint32 fwver; | |
44 | g_autofree gchar *fwver_str = NULL; | |
45 | g_autofree gchar *instance_id1 = NULL; | |
46 | ||
47 | /* get version */ | |
48 | fwver = GUINT32_FROM_BE (self->hdr.fwver); | |
49 | self->chip = fu_vli_usbhub_pd_guess_chip (fwver); | |
50 | if (self->chip == FU_VLI_USBHUB_PD_CHIP_UNKNOWN) { | |
51 | g_set_error (error, | |
52 | FWUPD_ERROR, | |
53 | FWUPD_ERROR_NOT_SUPPORTED, | |
54 | "PD version invalid [0x%x]", fwver); | |
55 | return FALSE; | |
56 | } | |
57 | fu_device_set_firmware_size (device, fu_vli_usbhub_pd_chip_get_size (self->chip)); | |
58 | fu_device_set_name (device, fu_vli_usbhub_pd_chip_to_string (self->chip)); | |
59 | ||
60 | /* use header to populate device info */ | |
61 | fwver_str = fu_common_version_from_uint32 (fwver, FWUPD_VERSION_FORMAT_QUAD); | |
62 | fu_device_set_version (device, fwver_str, FWUPD_VERSION_FORMAT_QUAD); | |
63 | instance_id1 = g_strdup_printf ("VLI_USBHUB_PD\\VID_%04X&PID_%04X", | |
64 | GUINT16_FROM_LE (self->hdr.vid), | |
65 | GUINT16_FROM_LE (self->hdr.pid)); | |
66 | fu_device_add_instance_id (device, instance_id1); | |
67 | ||
68 | /* these have a backup section */ | |
69 | if (fu_vli_usbhub_pd_chip_get_offset (self->chip) == VLI_USBHUB_FLASHMAP_ADDR_PD) | |
70 | fu_device_add_flag (FU_DEVICE (self), FWUPD_DEVICE_FLAG_SELF_RECOVERY); | |
71 | ||
72 | /* success */ | |
73 | return TRUE; | |
74 | } | |
75 | ||
76 | static FuFirmware * | |
77 | fu_vli_usbhub_pd_device_prepare_firmware (FuDevice *device, | |
78 | GBytes *fw, | |
79 | FwupdInstallFlags flags, | |
80 | GError **error) | |
81 | { | |
82 | FuVliUsbhubPdDevice *self = FU_VLI_USBHUB_PD_DEVICE (device); | |
83 | FuVliUsbhubPdChip chip; | |
84 | g_autoptr(FuFirmware) firmware = fu_vli_usbhub_pd_firmware_new (); | |
85 | ||
86 | /* check size */ | |
87 | if (g_bytes_get_size (fw) < fu_device_get_firmware_size_min (device)) { | |
88 | g_set_error (error, | |
89 | FWUPD_ERROR, | |
90 | FWUPD_ERROR_INVALID_FILE, | |
91 | "firmware too small, got 0x%x, expected >= 0x%x", | |
92 | (guint) g_bytes_get_size (fw), | |
93 | (guint) fu_device_get_firmware_size_min (device)); | |
94 | return NULL; | |
95 | } | |
96 | if (g_bytes_get_size (fw) > fu_device_get_firmware_size_max (device)) { | |
97 | g_set_error (error, | |
98 | FWUPD_ERROR, | |
99 | FWUPD_ERROR_INVALID_FILE, | |
100 | "firmware too large, got 0x%x, expected <= 0x%x", | |
101 | (guint) g_bytes_get_size (fw), | |
102 | (guint) fu_device_get_firmware_size_max (device)); | |
103 | return NULL; | |
104 | } | |
105 | ||
106 | /* check is compatible with firmware */ | |
107 | fu_device_set_status (device, FWUPD_STATUS_DECOMPRESSING); | |
108 | if (!fu_firmware_parse (firmware, fw, flags, error)) | |
109 | return NULL; | |
110 | chip = fu_vli_usbhub_pd_firmware_get_chip (FU_VLI_USBHUB_PD_FIRMWARE (firmware)); | |
111 | if (self->chip != chip) { | |
112 | g_set_error (error, | |
113 | FWUPD_ERROR, | |
114 | FWUPD_ERROR_INVALID_FILE, | |
115 | "firmware incompatible, got %s, expected %s", | |
116 | fu_vli_usbhub_pd_chip_to_string (chip), | |
117 | fu_vli_usbhub_pd_chip_to_string (self->chip)); | |
118 | return NULL; | |
119 | } | |
120 | ||
121 | /* we could check this against flags */ | |
122 | g_debug ("parsed version: %s", fu_firmware_get_version (firmware)); | |
123 | return g_steal_pointer (&firmware); | |
124 | } | |
125 | ||
126 | static FuFirmware * | |
127 | fu_vli_usbhub_pd_device_read_firmware (FuDevice *device, GError **error) | |
128 | { | |
129 | FuVliUsbhubDevice *parent = FU_VLI_USBHUB_DEVICE (fu_device_get_parent (device)); | |
130 | FuVliUsbhubPdDevice *self = FU_VLI_USBHUB_PD_DEVICE (device); | |
131 | g_autoptr(FuDeviceLocker) locker = NULL; | |
132 | g_autoptr(GBytes) fw = NULL; | |
133 | ||
134 | /* open device */ | |
135 | locker = fu_device_locker_new (parent, error); | |
136 | if (locker == NULL) | |
137 | return FALSE; | |
138 | ||
139 | /* read */ | |
140 | fu_device_set_status (FU_DEVICE (device), FWUPD_STATUS_DEVICE_VERIFY); | |
141 | fw = fu_vli_usbhub_device_spi_read (parent, | |
142 | fu_vli_usbhub_pd_chip_get_offset (self->chip), | |
143 | fu_device_get_firmware_size_max (device), | |
144 | error); | |
145 | if (fw == NULL) | |
146 | return NULL; | |
147 | return fu_firmware_new_from_bytes (fw); | |
148 | } | |
149 | ||
150 | static gboolean | |
151 | fu_vli_usbhub_pd_device_write_firmware (FuDevice *device, | |
152 | FuFirmware *firmware, | |
153 | FwupdInstallFlags flags, | |
154 | GError **error) | |
155 | { | |
156 | FuVliUsbhubPdDevice *self = FU_VLI_USBHUB_PD_DEVICE (device); | |
157 | FuVliUsbhubDevice *parent = FU_VLI_USBHUB_DEVICE (fu_device_get_parent (device)); | |
158 | gsize bufsz = 0; | |
159 | const guint8 *buf; | |
160 | g_autoptr(FuDeviceLocker) locker = NULL; | |
161 | g_autoptr(GBytes) fw = NULL; | |
162 | ||
163 | /* simple image */ | |
164 | fw = fu_firmware_get_image_default_bytes (firmware, error); | |
165 | if (fw == NULL) | |
166 | return FALSE; | |
167 | ||
168 | /* open device */ | |
169 | locker = fu_device_locker_new (parent, error); | |
170 | if (locker == NULL) | |
171 | return FALSE; | |
172 | ||
173 | /* erase */ | |
174 | fu_device_set_status (device, FWUPD_STATUS_DEVICE_ERASE); | |
175 | buf = g_bytes_get_data (fw, &bufsz); | |
176 | if (!fu_vli_usbhub_device_spi_erase (parent, | |
177 | fu_vli_usbhub_pd_chip_get_offset (self->chip), | |
178 | bufsz, | |
179 | error)) | |
180 | return FALSE; | |
181 | ||
182 | /* write */ | |
183 | fu_device_set_status (device, FWUPD_STATUS_DEVICE_WRITE); | |
184 | if (!fu_vli_usbhub_device_spi_write (parent, | |
185 | fu_vli_usbhub_pd_chip_get_offset (self->chip), | |
186 | buf, | |
187 | bufsz, | |
188 | error)) | |
189 | return FALSE; | |
190 | ||
191 | /* success */ | |
192 | return TRUE; | |
193 | } | |
194 | ||
195 | static void | |
196 | fu_vli_usbhub_pd_device_init (FuVliUsbhubPdDevice *self) | |
197 | { | |
198 | fu_device_add_icon (FU_DEVICE (self), "audio-card"); | |
199 | fu_device_add_flag (FU_DEVICE (self), FWUPD_DEVICE_FLAG_UPDATABLE); | |
200 | fu_device_add_flag (FU_DEVICE (self), FWUPD_DEVICE_FLAG_CAN_VERIFY_IMAGE); | |
201 | fu_device_set_install_duration (FU_DEVICE (self), 15); /* seconds */ | |
202 | fu_device_set_logical_id (FU_DEVICE (self), "PD"); | |
203 | fu_device_set_summary (FU_DEVICE (self), "USB-C Power Delivery Device"); | |
204 | } | |
205 | ||
206 | static void | |
207 | fu_vli_usbhub_pd_device_class_init (FuVliUsbhubPdDeviceClass *klass) | |
208 | { | |
209 | FuDeviceClass *klass_device = FU_DEVICE_CLASS (klass); | |
210 | klass_device->to_string = fu_vli_usbhub_pd_device_to_string; | |
211 | klass_device->probe = fu_vli_usbhub_pd_device_probe; | |
212 | klass_device->read_firmware = fu_vli_usbhub_pd_device_read_firmware; | |
213 | klass_device->write_firmware = fu_vli_usbhub_pd_device_write_firmware; | |
214 | klass_device->prepare_firmware = fu_vli_usbhub_pd_device_prepare_firmware; | |
215 | } | |
216 | ||
217 | FuDevice * | |
218 | fu_vli_usbhub_pd_device_new (FuVliUsbhubPdHdr *hdr) | |
219 | { | |
220 | FuVliUsbhubPdDevice *self = g_object_new (FU_TYPE_VLI_USBHUB_PD_DEVICE, NULL); | |
221 | memcpy (&self->hdr, hdr, sizeof(self->hdr)); | |
222 | return FU_DEVICE (self); | |
223 | } |
0 | /* | |
1 | * Copyright (C) 2019 Richard Hughes <richard@hughsie.com> | |
2 | * | |
3 | * SPDX-License-Identifier: LGPL-2.1+ | |
4 | */ | |
5 | ||
6 | #pragma once | |
7 | ||
8 | #include "fu-plugin.h" | |
9 | ||
10 | #define FU_TYPE_VLI_USBHUB_PD_DEVICE (fu_vli_usbhub_pd_device_get_type ()) | |
11 | G_DECLARE_FINAL_TYPE (FuVliUsbhubPdDevice, fu_vli_usbhub_pd_device, FU, VLI_USBHUB_PD_DEVICE, FuDevice) | |
12 | ||
13 | struct _FuVliUsbhubPdDeviceClass | |
14 | { | |
15 | FuDeviceClass parent_class; | |
16 | }; | |
17 | ||
18 | FuDevice *fu_vli_usbhub_pd_device_new (FuVliUsbhubPdHdr *hdr); |
0 | /* | |
1 | * Copyright (C) 2017-2019 VIA Corporation | |
2 | * Copyright (C) 2019 Richard Hughes <richard@hughsie.com> | |
3 | * | |
4 | * SPDX-License-Identifier: LGPL-2.1+ | |
5 | */ | |
6 | ||
7 | #include "config.h" | |
8 | ||
9 | #include "fu-vli-usbhub-pd-firmware.h" | |
10 | ||
11 | struct _FuVliUsbhubPdFirmware { | |
12 | FuFirmwareClass parent_instance; | |
13 | FuVliUsbhubPdChip chip; | |
14 | FuVliUsbhubPdHdr hdr; | |
15 | }; | |
16 | ||
17 | G_DEFINE_TYPE (FuVliUsbhubPdFirmware, fu_vli_usbhub_pd_firmware, FU_TYPE_FIRMWARE) | |
18 | ||
19 | FuVliUsbhubPdChip | |
20 | fu_vli_usbhub_pd_firmware_get_chip (FuVliUsbhubPdFirmware *self) | |
21 | { | |
22 | g_return_val_if_fail (FU_IS_VLI_USBHUB_PD_FIRMWARE (self), 0); | |
23 | return self->chip; | |
24 | } | |
25 | ||
26 | guint16 | |
27 | fu_vli_usbhub_pd_firmware_get_vid (FuVliUsbhubPdFirmware *self) | |
28 | { | |
29 | g_return_val_if_fail (FU_IS_VLI_USBHUB_PD_FIRMWARE (self), 0); | |
30 | return GUINT16_FROM_LE (self->hdr.vid); | |
31 | } | |
32 | ||
33 | guint16 | |
34 | fu_vli_usbhub_pd_firmware_get_pid (FuVliUsbhubPdFirmware *self) | |
35 | { | |
36 | g_return_val_if_fail (FU_IS_VLI_USBHUB_PD_FIRMWARE (self), 0); | |
37 | return GUINT16_FROM_LE (self->hdr.pid); | |
38 | } | |
39 | ||
40 | static void | |
41 | fu_vli_usbhub_pd_firmware_to_string (FuFirmware *firmware, guint idt, GString *str) | |
42 | { | |
43 | FuVliUsbhubPdFirmware *self = FU_VLI_USBHUB_PD_FIRMWARE (firmware); | |
44 | fu_common_string_append_kv (str, idt, "ChipId", | |
45 | fu_vli_usbhub_pd_chip_to_string (self->chip)); | |
46 | fu_common_string_append_kx (str, idt, "VID", | |
47 | fu_vli_usbhub_pd_firmware_get_vid (self)); | |
48 | fu_common_string_append_kx (str, idt, "PID", | |
49 | fu_vli_usbhub_pd_firmware_get_pid (self)); | |
50 | } | |
51 | ||
52 | static gboolean | |
53 | fu_vli_usbhub_pd_firmware_parse (FuFirmware *firmware, | |
54 | GBytes *fw, | |
55 | guint64 addr_start, | |
56 | guint64 addr_end, | |
57 | FwupdInstallFlags flags, | |
58 | GError **error) | |
59 | { | |
60 | FuVliUsbhubPdFirmware *self = FU_VLI_USBHUB_PD_FIRMWARE (firmware); | |
61 | gsize bufsz = 0; | |
62 | guint32 fwver; | |
63 | const guint8 *buf = g_bytes_get_data (fw, &bufsz); | |
64 | g_autofree gchar *fwver_str = NULL; | |
65 | g_autoptr(FuFirmwareImage) img = fu_firmware_image_new (fw); | |
66 | ||
67 | /* map into header */ | |
68 | if (!fu_memcpy_safe ((guint8 *) &self->hdr, sizeof(self->hdr), 0x0, | |
69 | buf, bufsz, VLI_USBHUB_PD_FLASHMAP_ADDR_LEGACY, | |
70 | sizeof(self->hdr), error)) { | |
71 | g_prefix_error (error, "failed to read header @0x%x: ", (guint) 0x4000); | |
72 | return FALSE; | |
73 | } | |
74 | ||
75 | /* look for info @0x1000 (for anything newer) */ | |
76 | if (GUINT16_FROM_LE (self->hdr.vid) != 0x2109) { | |
77 | g_debug ("VID was 0x%04x trying new location", | |
78 | GUINT16_FROM_LE (self->hdr.vid)); | |
79 | if (!fu_memcpy_safe ((guint8 *) &self->hdr, sizeof(self->hdr), 0x0, | |
80 | buf, bufsz, VLI_USBHUB_PD_FLASHMAP_ADDR, | |
81 | sizeof(self->hdr), error)) { | |
82 | g_prefix_error (error, "failed to read header @0x%x: ", (guint) 0x1003); | |
83 | return FALSE; | |
84 | } | |
85 | } | |
86 | fwver = GUINT32_FROM_BE (self->hdr.fwver); | |
87 | self->chip = fu_vli_usbhub_pd_guess_chip (fwver); | |
88 | if (self->chip == FU_VLI_USBHUB_PD_CHIP_UNKNOWN) { | |
89 | g_set_error (error, | |
90 | FWUPD_ERROR, | |
91 | FWUPD_ERROR_INVALID_FILE, | |
92 | "version invalid, using 0x%x", fwver); | |
93 | return FALSE; | |
94 | } | |
95 | fwver_str = fu_common_version_from_uint32 (fwver, FWUPD_VERSION_FORMAT_QUAD); | |
96 | fu_firmware_set_version (firmware, fwver_str); | |
97 | ||
98 | /* check size */ | |
99 | if (bufsz != fu_vli_usbhub_pd_chip_get_size (self->chip)) { | |
100 | g_set_error (error, | |
101 | FWUPD_ERROR, | |
102 | FWUPD_ERROR_INVALID_FILE, | |
103 | "size invalid, got 0x%x expected 0x%x", | |
104 | (guint) bufsz, | |
105 | fu_vli_usbhub_pd_chip_get_size (self->chip)); | |
106 | return FALSE; | |
107 | } | |
108 | ||
109 | /* check CRC */ | |
110 | if ((flags & FWUPD_INSTALL_FLAG_FORCE) == 0) { | |
111 | guint16 crc_actual; | |
112 | guint16 crc_file = 0x0; | |
113 | if (!fu_common_read_uint16_safe (buf, bufsz, bufsz - 2, &crc_file, | |
114 | G_LITTLE_ENDIAN, error)) { | |
115 | g_prefix_error (error, "failed to read file CRC: "); | |
116 | return FALSE; | |
117 | } | |
118 | crc_actual = fu_vli_usbhub_pd_crc16 (buf, bufsz - 2); | |
119 | if (crc_actual != crc_file) { | |
120 | g_set_error (error, | |
121 | FWUPD_ERROR, | |
122 | FWUPD_ERROR_INVALID_FILE, | |
123 | "CRC invalid, got 0x%x expected 0x%x", | |
124 | crc_file, crc_actual); | |
125 | return FALSE; | |
126 | } | |
127 | } | |
128 | ||
129 | /* whole image */ | |
130 | fu_firmware_add_image (firmware, img); | |
131 | return TRUE; | |
132 | } | |
133 | ||
134 | static void | |
135 | fu_vli_usbhub_pd_firmware_init (FuVliUsbhubPdFirmware *self) | |
136 | { | |
137 | } | |
138 | ||
139 | static void | |
140 | fu_vli_usbhub_pd_firmware_class_init (FuVliUsbhubPdFirmwareClass *klass) | |
141 | { | |
142 | FuFirmwareClass *klass_firmware = FU_FIRMWARE_CLASS (klass); | |
143 | klass_firmware->parse = fu_vli_usbhub_pd_firmware_parse; | |
144 | klass_firmware->to_string = fu_vli_usbhub_pd_firmware_to_string; | |
145 | } | |
146 | ||
147 | FuFirmware * | |
148 | fu_vli_usbhub_pd_firmware_new (void) | |
149 | { | |
150 | return FU_FIRMWARE (g_object_new (FU_TYPE_VLI_USBHUB_PD_FIRMWARE, NULL)); | |
151 | } |
0 | /* | |
1 | * Copyright (C) 2017-2019 VIA Corporation | |
2 | * Copyright (C) 2019 Richard Hughes <richard@hughsie.com> | |
3 | * | |
4 | * SPDX-License-Identifier: LGPL-2.1+ | |
5 | */ | |
6 | ||
7 | #pragma once | |
8 | ||
9 | #include "fu-firmware.h" | |
10 | ||
11 | #include "fu-vli-usbhub-pd-common.h" | |
12 | ||
13 | #define FU_TYPE_VLI_USBHUB_PD_FIRMWARE (fu_vli_usbhub_pd_firmware_get_type ()) | |
14 | G_DECLARE_FINAL_TYPE (FuVliUsbhubPdFirmware, fu_vli_usbhub_pd_firmware, FU, VLI_USBHUB_PD_FIRMWARE, FuFirmware) | |
15 | ||
16 | FuFirmware *fu_vli_usbhub_pd_firmware_new (void); | |
17 | FuVliUsbhubPdChip fu_vli_usbhub_pd_firmware_get_chip (FuVliUsbhubPdFirmware *self); | |
18 | guint16 fu_vli_usbhub_pd_firmware_get_vid (FuVliUsbhubPdFirmware *self); | |
19 | guint16 fu_vli_usbhub_pd_firmware_get_pid (FuVliUsbhubPdFirmware *self); |
13 | 13 | 'fu-vli-usbhub-common.c', |
14 | 14 | 'fu-vli-usbhub-device.c', |
15 | 15 | 'fu-vli-usbhub-firmware.c', |
16 | 'fu-vli-usbhub-i2c-common.c', | |
17 | 'fu-vli-usbhub-i2c-device.c', | |
18 | 'fu-vli-usbhub-pd-common.c', | |
19 | 'fu-vli-usbhub-pd-device.c', | |
20 | 'fu-vli-usbhub-pd-firmware.c', | |
16 | 21 | ], |
17 | 22 | include_directories : [ |
18 | 23 | include_directories('../..'), |
10 | 10 | --------------- |
11 | 11 | |
12 | 12 | The HID DeviceInstanceId values are used, e.g. `HIDRAW\VEN_056A&DEV_4875`. |
13 | ||
14 | Additionally, for supported AES devices an extra GUID is added for the hardware | |
15 | ID (e.g. `WACOM\HWID_%04X`) to further disambiguate the panels. | |
16 | 13 | |
17 | 14 | Firmware Format |
18 | 15 | --------------- |
12 | 12 | #include "fu-wacom-common.h" |
13 | 13 | #include "fu-wacom-aes-device.h" |
14 | 14 | |
15 | typedef struct __attribute__((packed)) { | |
16 | guint8 report_id; | |
17 | guint8 cmd; | |
18 | guint8 echo; | |
19 | guint32 addr; | |
20 | guint8 size8; | |
21 | guint8 data[128]; | |
22 | } FuWacomRawVerifyResponse; | |
23 | ||
15 | 24 | struct _FuWacomAesDevice { |
16 | 25 | FuWacomDevice parent_instance; |
17 | guint32 hwid; | |
18 | 26 | }; |
19 | 27 | |
20 | 28 | G_DEFINE_TYPE (FuWacomAesDevice, fu_wacom_aes_device, FU_TYPE_WACOM_DEVICE) |
21 | 29 | |
22 | 30 | static gboolean |
23 | fu_wacom_aes_device_obtain_hwid (FuWacomAesDevice *self, GError **error) | |
24 | { | |
25 | guint8 cmd[FU_WACOM_RAW_FW_MAINTAIN_REPORT_SZ] = { 0x0 }; | |
26 | guint8 buf[FU_WACOM_RAW_FW_MAINTAIN_REPORT_SZ] = { 0x0 }; | |
27 | ||
28 | cmd[0] = FU_WACOM_RAW_FW_MAINTAIN_REPORT_ID; | |
29 | cmd[1] = 0x01; /* ?? */ | |
30 | cmd[2] = 0x01; /* ?? */ | |
31 | cmd[3] = 0x0f; /* ?? */ | |
32 | ||
33 | if (!fu_wacom_device_set_feature (FU_WACOM_DEVICE (self), | |
34 | cmd, sizeof(cmd), error)) { | |
31 | fu_wacom_aes_add_recovery_hwid (FuDevice *device, GError **error) | |
32 | { | |
33 | FuWacomRawRequest cmd = { | |
34 | .report_id = FU_WACOM_RAW_BL_REPORT_ID_SET, | |
35 | .cmd = FU_WACOM_RAW_BL_CMD_VERIFY_FLASH, | |
36 | .echo = 0x01, | |
37 | .addr = FU_WACOM_RAW_BL_START_ADDR, | |
38 | .size8 = FU_WACOM_RAW_BL_BYTES_CHECK/8, | |
39 | }; | |
40 | FuWacomRawVerifyResponse rsp = { | |
41 | .report_id = FU_WACOM_RAW_BL_REPORT_ID_GET, | |
42 | .size8 = 0x00, | |
43 | .data = { 0x00 } | |
44 | }; | |
45 | g_autofree gchar *devid1 = NULL; | |
46 | g_autofree gchar *devid2 = NULL; | |
47 | guint16 pid; | |
48 | ||
49 | ||
50 | if (!fu_wacom_device_set_feature (FU_WACOM_DEVICE (device), | |
51 | (guint8*) &cmd, sizeof(cmd), error)) { | |
35 | 52 | g_prefix_error (error, "failed to send: "); |
36 | 53 | return FALSE; |
37 | 54 | } |
38 | buf[0] = FU_WACOM_RAW_FW_MAINTAIN_REPORT_ID; | |
39 | if (!fu_wacom_device_get_feature (FU_WACOM_DEVICE (self), | |
40 | buf, sizeof(buf), error)) { | |
55 | if (!fu_wacom_device_get_feature (FU_WACOM_DEVICE (device), | |
56 | (guint8*) &rsp, sizeof(rsp), error)) { | |
41 | 57 | g_prefix_error (error, "failed to receive: "); |
42 | 58 | return FALSE; |
43 | 59 | } |
44 | if (buf[1] == 0xff) { | |
60 | if (rsp.size8 != cmd.size8) { | |
45 | 61 | g_set_error_literal (error, |
46 | 62 | G_IO_ERROR, |
47 | 63 | G_IO_ERROR_NOT_SUPPORTED, |
49 | 65 | return FALSE; |
50 | 66 | } |
51 | 67 | |
52 | /* check magic number */ | |
53 | if (memcmp (buf, "\x34\x12\x78\x56\x65\x87\x21\x43", 8) != 0) { | |
54 | g_set_error_literal (error, | |
55 | G_IO_ERROR, | |
56 | G_IO_ERROR_NOT_SUPPORTED, | |
57 | "incorrect magic number"); | |
58 | return FALSE; | |
59 | } | |
60 | ||
61 | /* format the value */ | |
62 | self->hwid = ((guint32) buf[9]) << 24 | | |
63 | ((guint32) buf[8]) << 16 | | |
64 | ((guint32) buf[11]) << 8 | | |
65 | ((guint32) buf[10]); | |
68 | pid = (rsp.data[7] << 8) + (rsp.data[6]); | |
69 | if( (pid == 0xFFFF) || (pid == 0x0000) ) { | |
70 | g_set_error (error, | |
71 | G_IO_ERROR, | |
72 | G_IO_ERROR_NOT_SUPPORTED, | |
73 | "invalid recovery product ID %04x", pid); | |
74 | return FALSE; | |
75 | } | |
76 | ||
77 | devid1 = g_strdup_printf ("HIDRAW\\VEN_2D1F&DEV_%04X", pid); | |
78 | devid2 = g_strdup_printf ("HIDRAW\\VEN_056A&DEV_%04X", pid); | |
79 | fu_device_add_instance_id (device, devid1); | |
80 | fu_device_add_instance_id (device, devid2); | |
81 | ||
66 | 82 | return TRUE; |
67 | 83 | |
68 | 84 | } |
100 | 116 | fu_wacom_aes_device_setup (FuDevice *device, GError **error) |
101 | 117 | { |
102 | 118 | FuWacomAesDevice *self = FU_WACOM_AES_DEVICE (device); |
119 | g_autoptr(GError) error_local = NULL; | |
103 | 120 | |
104 | 121 | /* find out if in bootloader mode already */ |
105 | 122 | if (!fu_wacom_aes_query_operation_mode (self, error)) |
108 | 125 | /* get firmware version */ |
109 | 126 | if (fu_device_has_flag (device, FWUPD_DEVICE_FLAG_IS_BOOTLOADER)) { |
110 | 127 | fu_device_set_version (device, "0.0", FWUPD_VERSION_FORMAT_PAIR); |
128 | /* get the recovery PID if supported */ | |
129 | if (!fu_wacom_aes_add_recovery_hwid (device, &error_local)) | |
130 | g_debug ("failed to get HwID: %s", error_local->message); | |
111 | 131 | } else { |
112 | 132 | guint32 fw_ver; |
113 | 133 | guint8 data[FU_WACOM_RAW_STATUS_REPORT_SZ] = { |
115 | 135 | 0x0 |
116 | 136 | }; |
117 | 137 | g_autofree gchar *version = NULL; |
118 | g_autoptr(GError) error_local = NULL; | |
119 | 138 | |
120 | 139 | if (!fu_wacom_device_get_feature (FU_WACOM_DEVICE (self), |
121 | 140 | data, sizeof(data), error)) |
123 | 142 | fw_ver = fu_common_read_uint16 (data + 11, G_LITTLE_ENDIAN); |
124 | 143 | version = g_strdup_printf ("%04x.%02x", fw_ver, data[13]); |
125 | 144 | fu_device_set_version (device, version, FWUPD_VERSION_FORMAT_PAIR); |
126 | ||
127 | /* get the optional 32 byte HWID and add it as a GUID */ | |
128 | if (!fu_wacom_aes_device_obtain_hwid (self, &error_local)) { | |
129 | g_debug ("failed to get HwID: %s", error_local->message); | |
130 | } else { | |
131 | g_autofree gchar *devid = NULL; | |
132 | devid = g_strdup_printf ("WACOM\\HWID_%04X", self->hwid); | |
133 | fu_device_add_instance_id (device, devid); | |
134 | } | |
135 | 145 | } |
136 | 146 | |
137 | 147 | /* success */ |
224 | 234 | static void |
225 | 235 | fu_wacom_aes_device_init (FuWacomAesDevice *self) |
226 | 236 | { |
227 | fu_device_set_name (FU_DEVICE (self), "Embedded Wacom AES Device"); | |
237 | fu_device_set_name (FU_DEVICE (self), "Wacom AES Device"); | |
228 | 238 | } |
229 | 239 | |
230 | 240 | static void |
17 | 17 | #define FU_WACOM_RAW_FW_CMD_DETACH 0x02 |
18 | 18 | #define FU_WACOM_RAW_FW_REPORT_SZ 2 |
19 | 19 | |
20 | #define FU_WACOM_RAW_FW_MAINTAIN_REPORT_ID 0x09 | |
21 | #define FU_WACOM_RAW_FW_MAINTAIN_REPORT_SZ 64 | |
20 | #define FU_WACOM_RAW_BL_START_ADDR (0x11FF8) | |
21 | #define FU_WACOM_RAW_BL_BYTES_CHECK 8 | |
22 | 22 | |
23 | 23 | #define FU_WACOM_RAW_BL_REPORT_ID_SET 0x07 |
24 | 24 | #define FU_WACOM_RAW_BL_REPORT_ID_GET 0x08 |
339 | 339 | fu_wacom_device_init (FuWacomDevice *self) |
340 | 340 | { |
341 | 341 | fu_device_add_flag (FU_DEVICE (self), FWUPD_DEVICE_FLAG_UPDATABLE); |
342 | fu_device_add_flag (FU_DEVICE (self), FWUPD_DEVICE_FLAG_INTERNAL); | |
342 | 343 | } |
343 | 344 | |
344 | 345 | static void |
224 | 224 | static void |
225 | 225 | fu_wacom_emr_device_init (FuWacomEmrDevice *self) |
226 | 226 | { |
227 | fu_device_set_name (FU_DEVICE (self), "Embedded Wacom EMR Device"); | |
227 | fu_device_set_name (FU_DEVICE (self), "Wacom EMR Device"); | |
228 | 228 | } |
229 | 229 | |
230 | 230 | static void |
0 | 0 | # Devices that do "replug" and thus don't change VID:PID to the bootloader |
1 | 1 | # need to have an extra GUID of WacomAES or WacomEMR added so that the flash |
2 | 2 | # constants are set correctly. |
3 | ||
4 | # Dell Chromebook Enterprise 5300 | |
5 | [DeviceInstanceId=HIDRAW\VEN_2D1F&DEV_4946] | |
6 | Plugin = wacom_raw | |
7 | Guid = WacomAES | |
8 | ||
9 | # Moffet 14-LGD-TPK | |
10 | [DeviceInstanceId=HIDRAW\VEN_2D1F&DEV_4970] | |
11 | Plugin = wacom_raw | |
12 | Guid = WacomAES | |
13 | ||
14 | # Moffet 14-Sharp-HH | |
15 | [DeviceInstanceId=HIDRAW\VEN_2D1F&DEV_4971] | |
16 | Plugin = wacom_raw | |
17 | Guid = WacomAES | |
18 | ||
19 | # Moffet 14-Sharp-VIA | |
20 | [DeviceInstanceId=HIDRAW\VEN_2D1F&DEV_4972] | |
21 | Plugin = wacom_raw | |
22 | Guid = WacomAES | |
23 | ||
24 | # Moffet 14-Coretronic-TPK | |
25 | [DeviceInstanceId=HIDRAW\VEN_2D1F&DEV_4973] | |
26 | Plugin = wacom_raw | |
27 | Guid = WacomAES | |
28 | ||
29 | # Moffet 14-Coretronic-HH | |
30 | [DeviceInstanceId=HIDRAW\VEN_2D1F&DEV_4974] | |
31 | Plugin = wacom_raw | |
32 | Guid = WacomAES | |
33 | ||
34 | # Dell Latitude 5175 | |
35 | [DeviceInstanceId=HIDRAW\VEN_056A&DEV_4807] | |
36 | Plugin = wacom_raw | |
37 | Guid = WacomAES | |
38 | ||
39 | # Dell XPS 12 9250 | |
40 | [DeviceInstanceId=HIDRAW\VEN_056A&DEV_4822] | |
41 | Plugin = wacom_raw | |
42 | Guid = WacomAES | |
43 | ||
44 | # Dell Venue 8 Pro 5855 | |
45 | [DeviceInstanceId=HIDRAW\VEN_056A&DEV_4824] | |
46 | Plugin = wacom_raw | |
47 | Guid = WacomAES | |
48 | ||
49 | # Dell XPS 13 9365 | |
50 | [DeviceInstanceId=HIDRAW\VEN_056A&DEV_4831] | |
51 | Plugin = wacom_raw | |
52 | Guid = WacomAES | |
53 | ||
54 | # Dell Latitude 5285 | |
55 | [DeviceInstanceId=HIDRAW\VEN_056A&DEV_484C] | |
56 | Plugin = wacom_raw | |
57 | Guid = WacomAES | |
58 | ||
59 | # Dell Latitude 7390 2-in-1 | |
60 | [DeviceInstanceId=HIDRAW\VEN_056A&DEV_4841] | |
61 | Plugin = wacom_raw | |
62 | Guid = WacomAES | |
3 | 63 | |
4 | 64 | # Dell XPS-15 9575 |
5 | 65 | [DeviceInstanceId=HIDRAW\VEN_056A&DEV_4875] |
6 | 66 | Plugin = wacom_raw |
7 | 67 | Guid = WacomAES |
8 | 68 | |
69 | # Dell Latitude 7400 2-in-1 | |
70 | [DeviceInstanceId=HIDRAW\VEN_056A&DEV_48C9] | |
71 | Plugin = wacom_raw | |
72 | Guid = WacomAES | |
73 | ||
74 | # Dell XPS-15 9570 | |
75 | [DeviceInstanceId=HIDRAW\VEN_056A&DEV_488F] | |
76 | Plugin = wacom_raw | |
77 | Guid = WacomAES | |
78 | ||
79 | # Dell XPS 13 7390 2-in-1 | |
80 | [DeviceInstanceId=HIDRAW\VEN_056A&DEV_48ED] | |
81 | Plugin = wacom_raw | |
82 | Guid = WacomAES | |
83 | ||
9 | 84 | # AES bootloader mode |
10 | 85 | [DeviceInstanceId=HIDRAW\VEN_056A&DEV_0094] |
11 | Plugin = wacom_raw | |
12 | Guid = WacomAES | |
13 | Flags = is-bootloader | |
14 | ||
15 | # AES bootloader mode | |
16 | [DeviceInstanceId=HIDRAW\VEN_2D1F&DEV_0094] | |
17 | 86 | Plugin = wacom_raw |
18 | 87 | Guid = WacomAES |
19 | 88 | Flags = is-bootloader |
80 | 80 | msgid "%s Update" |
81 | 81 | msgstr "Actualització de %s" |
82 | 82 | |
83 | #. TRANSLATORS: warn the user before updating, %1 is a device name | |
84 | #, c-format | |
85 | msgid "%s and all connected devices may not be usable while updating." | |
86 | msgstr "%s i tots els dispositius connectats poden no ser usables en actualitzar." | |
87 | ||
88 | #. TRANSLATORS: warn the user before updating, %1 is a device name | |
89 | #, c-format | |
90 | msgid "%s must remain connected for the duration of the update to avoid damage." | |
91 | msgstr "%s haurà d'estar connectat durant tota l'actualització per evitar danys." | |
92 | ||
93 | #. TRANSLATORS: warn the user before updating, %1 is a machine name | |
94 | #, c-format | |
95 | msgid "%s must remain plugged into a power source for the duration of the update to avoid damage." | |
96 | msgstr "%s haurà de romandre connectat a una font d'alimentació durant tota l'actualització per evitar danys." | |
97 | ||
83 | 98 | #. TRANSLATORS: duration in days! |
84 | 99 | #, c-format |
85 | 100 | msgid "%u day" |
259 | 274 | msgid "Changed" |
260 | 275 | msgstr "S'ha canviat" |
261 | 276 | |
277 | #. TRANSLATORS: command description | |
278 | msgid "Checks cryptographic hash matches firmware" | |
279 | msgstr "Comprova que la suma criptogràfica coincideix amb el microprogramari" | |
280 | ||
262 | 281 | #. TRANSLATORS: remote checksum |
263 | 282 | msgid "Checksum" |
264 | 283 | msgstr "Suma de comprovació" |
268 | 287 | msgstr "Trieu un dispositiu:" |
269 | 288 | |
270 | 289 | #. TRANSLATORS: get interactive prompt |
290 | msgid "Choose a firmware type:" | |
291 | msgstr "Trieu un tipus de microprogramari:" | |
292 | ||
293 | #. TRANSLATORS: get interactive prompt | |
271 | 294 | msgid "Choose a release:" |
272 | 295 | msgstr "Trieu un alliberament:" |
273 | 296 | |
283 | 306 | msgid "Command not found" |
284 | 307 | msgstr "No s'ha trobat cap ordre" |
285 | 308 | |
309 | #. TRANSLATORS: prompt to apply the update | |
310 | msgid "Continue with update?" | |
311 | msgstr "Continuo amb l'actualització?" | |
312 | ||
286 | 313 | #. TRANSLATORS: command description |
287 | 314 | msgid "Convert firmware to DFU format" |
288 | 315 | msgstr "Converteix el microprogramari al format DFU" |
316 | ||
317 | #. TRANSLATORS: Device supports some form of checksum verification | |
318 | msgid "Cryptographic hash verification is available" | |
319 | msgstr "Està disponible la verificació de la suma criptogràfica" | |
289 | 320 | |
290 | 321 | #. TRANSLATORS: version number of current firmware |
291 | 322 | msgid "Current version" |
315 | 346 | msgid "Details" |
316 | 347 | msgstr "Detalls" |
317 | 348 | |
349 | #. TRANSLATORS: description of device ability | |
350 | msgid "Device Flags" | |
351 | msgstr "Etiquetes del dispositiu" | |
352 | ||
318 | 353 | #. TRANSLATORS: ID for hardware, typically a SHA1 sum |
319 | 354 | msgid "Device ID" |
320 | 355 | msgstr "ID del dispositiu" |
323 | 358 | msgid "Device added:" |
324 | 359 | msgstr "S'ha afegit el dispositiu:" |
325 | 360 | |
361 | #. TRANSLATORS: Device supports a safety mechanism for flashing | |
362 | msgid "Device can recover flash failures" | |
363 | msgstr "El dispositiu pot recuperar fallades de flaix" | |
364 | ||
326 | 365 | #. TRANSLATORS: this is when a device has been updated |
327 | 366 | msgid "Device changed:" |
328 | 367 | msgstr "S'ha canviat el dispositiu:" |
329 | 368 | |
369 | #. TRANSLATORS: Is locked and can be unlocked | |
370 | msgid "Device is locked" | |
371 | msgstr "El dispositiu està bloquejat" | |
372 | ||
373 | #. TRANSLATORS: Device remains usable during update | |
374 | msgid "Device is usable for the duration of the update" | |
375 | msgstr "El dispositiu es podrà usar durant tota l'actualització" | |
376 | ||
330 | 377 | #. TRANSLATORS: this is when a device is hotplugged |
331 | 378 | msgid "Device removed:" |
332 | 379 | msgstr "S'ha eliminat el dispositiu:" |
333 | 380 | |
381 | #. TRANSLATORS: Device supports a safety mechanism for flashing | |
382 | msgid "Device stages updates" | |
383 | msgstr "Etapes d'actualització del dispositiu" | |
384 | ||
385 | #. TRANSLATORS: Device update needs to be separately activated | |
386 | msgid "Device update needs activation" | |
387 | msgstr "Cal activar l'actualització del dispositiu" | |
388 | ||
389 | #. TRANSLATORS: Device will not return after update completes | |
390 | msgid "Device will not re-appear after update completes" | |
391 | msgstr "El dispositiu no tornarà a aparèixer un cop finalitzada l'actualització" | |
392 | ||
334 | 393 | #. TRANSLATORS: a list of successful updates |
335 | 394 | msgid "Devices that have been updated successfully:" |
336 | 395 | msgstr "Els dispositius que s'han actualitzat correctament:" |
369 | 428 | #. TRANSLATORS: turn on all debugging |
370 | 429 | msgid "Do not include timestamp prefix" |
371 | 430 | msgstr "No incloure el prefix de la marca de temps" |
431 | ||
432 | #. TRANSLATORS: command line option | |
433 | msgid "Do not perform device safety checks" | |
434 | msgstr "No realitzar les comprovacions de seguretat al dispositiu" | |
372 | 435 | |
373 | 436 | msgid "Do not upload report at this time, but prompt again for future updates" |
374 | 437 | msgid_plural "Do not upload reports at this time, but prompt again for future updates" |
616 | 679 | msgid "Gets the results from the last update" |
617 | 680 | msgstr "Obté els resultats de l'última actualització" |
618 | 681 | |
682 | #. TRANSLATORS: The hardware is waiting to be replugged | |
683 | msgid "Hardware is waiting to be replugged" | |
684 | msgstr "El maquinari està esperant a ser endollat" | |
685 | ||
619 | 686 | #. TRANSLATORS: daemon is inactive |
620 | 687 | msgid "Idle…" |
621 | 688 | msgstr "Està ociós..." |
624 | 691 | msgid "Ignore SSL strict checks when downloading files" |
625 | 692 | msgstr "Ignora les comprovacions estrictes de SSL quan es baixin els fitxers" |
626 | 693 | |
694 | #. TRANSLATORS: Ignore validation safety checks when flashing this device | |
695 | msgid "Ignore validation safety checks" | |
696 | msgstr "Ignora les comprovacions de seguretat de validació" | |
697 | ||
627 | 698 | #. TRANSLATORS: length of time the update takes to apply |
628 | 699 | msgid "Install Duration" |
629 | 700 | msgstr "Durada de la instal·lació" |
645 | 716 | msgid "Install signed system firmware" |
646 | 717 | msgstr "Instal·la microprogramari signat per al sistema" |
647 | 718 | |
719 | #. TRANSLATORS: Install composite firmware on the parent before the child | |
720 | msgid "Install to parent device first" | |
721 | msgstr "Instal·la primer al dispositiu pare" | |
722 | ||
648 | 723 | msgid "Install unsigned device firmware" |
649 | 724 | msgstr "Instal·la microprogramari sense signar per al dispositiu" |
650 | 725 | |
663 | 738 | #, c-format |
664 | 739 | msgid "Installing on %s…" |
665 | 740 | msgstr "S'està intal·lant a %s…" |
741 | ||
742 | #. TRANSLATORS: Device cannot be removed easily | |
743 | msgid "Internal device" | |
744 | msgstr "Dispositiu intern" | |
745 | ||
746 | #. TRANSLATORS: Is currently in bootloader mode | |
747 | msgid "Is in bootloader mode" | |
748 | msgstr "Està en el mode carregador d'arrencada" | |
666 | 749 | |
667 | 750 | #. TRANSLATORS: issue fixed with the release, e.g. CVE |
668 | 751 | msgid "Issue" |
695 | 778 | msgid "List supported firmware updates" |
696 | 779 | msgstr "Llista les actualitzacions de microprogramari compatibles" |
697 | 780 | |
781 | #. TRANSLATORS: command description | |
782 | msgid "List the available firmware types" | |
783 | msgstr "Llista els tipus de microprogramari disponibles" | |
784 | ||
698 | 785 | #. TRANSLATORS: parsing the firmware information |
699 | 786 | msgid "Loading…" |
700 | 787 | msgstr "S'està carregant..." |
746 | 833 | msgid "Monitor the daemon for events" |
747 | 834 | msgstr "Monitora el dimoni pels esdeveniments" |
748 | 835 | |
836 | #. TRANSLATORS: Requires a reboot to apply firmware or to reload hardware | |
837 | msgid "Needs a reboot after installation" | |
838 | msgstr "Requereix un reinici després de la instal·lació" | |
839 | ||
840 | #. TRANSLATORS: Requires system shutdown to apply firmware | |
841 | msgid "Needs shutdown after installation" | |
842 | msgstr "Requereix aturar després de la instal·lació" | |
843 | ||
749 | 844 | #. TRANSLATORS: version number of new firmware |
750 | 845 | msgid "New version" |
751 | 846 | msgstr "Versió nova" |
753 | 848 | msgid "No action specified!" |
754 | 849 | msgstr "No s'ha especificat cap acció!" |
755 | 850 | |
851 | #. TRANSLATORS: nothing found | |
852 | msgid "No firmware IDs found" | |
853 | msgstr "No s'ha trobat cap ID de microprogramari" | |
854 | ||
756 | 855 | #. TRANSLATORS: nothing attached that can be upgraded |
757 | 856 | msgid "No hardware detected with firmware update capability" |
758 | 857 | msgstr "No s'ha detectat cap maquinari amb capacitat per a l'actualització del microprogramari" |
789 | 888 | msgid "Override warnings and force the action" |
790 | 889 | msgstr "Anul·la els avisos i força l'acció" |
791 | 890 | |
891 | #. TRANSLATORS: command description | |
892 | msgid "Parse and show details about a firmware file" | |
893 | msgstr "Analitza i mostra els detalls sobre un fitxer de microprogramari" | |
894 | ||
792 | 895 | #. TRANSLATORS: remote filename base |
793 | 896 | msgid "Password" |
794 | 897 | msgstr "Contrasenya" |
831 | 934 | msgstr "Consulta el suport per a l'actualització del microprogramari" |
832 | 935 | |
833 | 936 | #. TRANSLATORS: command description |
937 | msgid "Read a firmware blob from a device" | |
938 | msgstr "Llegeix un blob de microprogramari des d'un dispositiu" | |
939 | ||
940 | #. TRANSLATORS: command description | |
834 | 941 | msgid "Read firmware from device into a file" |
835 | 942 | msgstr "Llegeix el microprogramari des del dispositiu a un fitxer" |
836 | 943 | |
837 | 944 | #. TRANSLATORS: command description |
838 | 945 | msgid "Read firmware from one partition into a file" |
839 | 946 | msgstr "Llegeix el microprogramari des d'una partició a un fitxer" |
947 | ||
948 | #. TRANSLATORS: %1 is a device name | |
949 | #, c-format | |
950 | msgid "Reading from %s…" | |
951 | msgstr "Llegint des de %s…" | |
840 | 952 | |
841 | 953 | #. TRANSLATORS: reading from the flash chips |
842 | 954 | msgid "Reading…" |
878 | 990 | msgid "Report URI" |
879 | 991 | msgstr "URI de l'informe" |
880 | 992 | |
993 | #. TRANSLATORS: Has been reported to a metadata server | |
994 | msgid "Reported to remote server" | |
995 | msgstr "Informat al servidor remot" | |
996 | ||
997 | #. TRANSLATORS: Must be plugged in to an outlet | |
998 | msgid "Requires AC power" | |
999 | msgstr "Requereix una font d'alimentació" | |
1000 | ||
1001 | #. TRANSLATORS: Requires a bootloader mode to be manually enabled by the user | |
1002 | msgid "Requires a bootloader" | |
1003 | msgstr "Requereix un carregador d’arrencada" | |
1004 | ||
881 | 1005 | #. TRANSLATORS: metadata is downloaded from the Internet |
882 | 1006 | msgid "Requires internet connection" |
883 | 1007 | msgstr "Requereix connexió a Internet" |
917 | 1041 | #. TRANSLATORS: scheduing an update to be done on the next boot |
918 | 1042 | msgid "Scheduling…" |
919 | 1043 | msgstr "Planificació..." |
1044 | ||
1045 | #. TRANSLATORS: Device has been chosen by the daemon for the user | |
1046 | msgid "Selected device" | |
1047 | msgstr "Dispositiu seleccionat" | |
920 | 1048 | |
921 | 1049 | #. TRANSLATORS: serial number of hardware |
922 | 1050 | msgid "Serial Number" |
1052 | 1180 | msgid "Successfully modified configuration value" |
1053 | 1181 | msgstr "S'ha modificat amb èxit el valor de la configuració" |
1054 | 1182 | |
1183 | #. TRANSLATORS: success message for a per-remote setting change | |
1184 | msgid "Successfully modified remote" | |
1185 | msgstr "El remot ha estat modificat amb èxit" | |
1186 | ||
1055 | 1187 | #. TRANSLATORS: success message -- the user can do this by-hand too |
1056 | 1188 | msgid "Successfully refreshed metadata manually" |
1057 | 1189 | msgstr "S'han refrescat manualment amb èxit les metadades" |
1190 | ||
1191 | #. TRANSLATORS: success message when user refreshes device checksums | |
1192 | msgid "Successfully updated device checksums" | |
1193 | msgstr "S'han actualitzat correctament les sumes de verificació del dispositiu" | |
1058 | 1194 | |
1059 | 1195 | #. TRANSLATORS: success message -- where the user has uploaded |
1060 | 1196 | #. * success and/or failure reports to the remote server |
1064 | 1200 | msgstr[0] "S'ha enviat %u informe correctament" |
1065 | 1201 | msgstr[1] "S'han enviat %u informes correctament" |
1066 | 1202 | |
1203 | #. TRANSLATORS: success message when user verified device checksums | |
1204 | msgid "Successfully verified device checksums" | |
1205 | msgstr "S'han verificat correctament les sumes de verificació del dispositiu" | |
1206 | ||
1067 | 1207 | #. TRANSLATORS: one line summary of device |
1068 | 1208 | msgid "Summary" |
1069 | 1209 | msgstr "Resum" |
1210 | ||
1211 | #. TRANSLATORS: Is found in current metadata | |
1212 | msgid "Supported on remote server" | |
1213 | msgstr "Admès en el servidor remot" | |
1070 | 1214 | |
1071 | 1215 | msgid "Target" |
1072 | 1216 | msgstr "Objectiu" |
1124 | 1268 | msgid "Unsupported daemon version %s, client version is %s" |
1125 | 1269 | msgstr "Versió %s no admesa del dimoni, la versió del client és %s" |
1126 | 1270 | |
1271 | #. TRANSLATORS: Device is updatable in this or any other mode | |
1272 | msgid "Updatable" | |
1273 | msgstr "Actualitzable" | |
1274 | ||
1127 | 1275 | #. TRANSLATORS: error message from last update attempt |
1128 | 1276 | msgid "Update Error" |
1129 | 1277 | msgstr "Error en actualitzar" |
1149 | 1297 | msgid "Update now?" |
1150 | 1298 | msgstr "Actualitzo ara?" |
1151 | 1299 | |
1300 | #. TRANSLATORS: Update can only be done from offline mode | |
1301 | msgid "Update requires a reboot" | |
1302 | msgstr "L'actualització requereix un reinici" | |
1303 | ||
1304 | #. TRANSLATORS: command description | |
1305 | msgid "Update the stored cryptographic hash with current ROM contents" | |
1306 | msgstr "Actualitza la suma criptogràfica emmagatzemada amb el contingut actual de la ROM" | |
1307 | ||
1152 | 1308 | msgid "Update the stored device verification information" |
1153 | 1309 | msgstr "Actualitza la informació de verificació dels dispositius emmagatzemats" |
1154 | 1310 | |
1171 | 1327 | #, c-format |
1172 | 1328 | msgid "Updating %s…" |
1173 | 1329 | msgstr "S'està actualitzant %s…" |
1330 | ||
1331 | #. TRANSLATORS: message letting the user know an upgrade is available | |
1332 | #. * %1 is the device name and %2 and %3 are version strings | |
1333 | #, c-format | |
1334 | msgid "Upgrade available for %s from %s to %s" | |
1335 | msgstr "Actualització disponible per a %s des de %s a %s" | |
1174 | 1336 | |
1175 | 1337 | #. TRANSLATORS: the server sent the user a small message |
1176 | 1338 | msgid "Upload message:" |
1198 | 1360 | msgid "Use quirk flags when installing firmware" |
1199 | 1361 | msgstr "Usa les etiquetes peculiars en instal·lar el microprogramari" |
1200 | 1362 | |
1363 | #. TRANSLATORS: User has been notified | |
1364 | msgid "User has been notified" | |
1365 | msgstr "S'ha notificat a l'usuari" | |
1366 | ||
1201 | 1367 | #. TRANSLATORS: remote filename base |
1202 | 1368 | msgid "Username" |
1203 | 1369 | msgstr "Nom d'usuari" |
80 | 80 | msgid "%s Update" |
81 | 81 | msgstr "Opdatering for %s" |
82 | 82 | |
83 | #. TRANSLATORS: warn the user before updating, %1 is a device name | |
84 | #, c-format | |
85 | msgid "%s and all connected devices may not be usable while updating." | |
86 | msgstr "%s og alle tilsluttede enheder vil måske ikke være anvendelige under opdatering." | |
87 | ||
88 | #. TRANSLATORS: warn the user before updating, %1 is a device name | |
89 | #, c-format | |
90 | msgid "%s must remain connected for the duration of the update to avoid damage." | |
91 | msgstr "%s skal være tilsluttet under hele opdateringen, for at undgå skade." | |
92 | ||
93 | #. TRANSLATORS: warn the user before updating, %1 is a machine name | |
94 | #, c-format | |
95 | msgid "%s must remain plugged into a power source for the duration of the update to avoid damage." | |
96 | msgstr "%s skal være tilsluttet en strømkilde under hele opdateringen, for at undgå skade." | |
97 | ||
83 | 98 | #. TRANSLATORS: duration in days! |
84 | 99 | #, c-format |
85 | 100 | msgid "%u day" |
259 | 274 | msgid "Changed" |
260 | 275 | msgstr "Ændret" |
261 | 276 | |
277 | #. TRANSLATORS: command description | |
278 | msgid "Checks cryptographic hash matches firmware" | |
279 | msgstr "Tjekker om den kryptografiske hash passer med firmwaren" | |
280 | ||
262 | 281 | #. TRANSLATORS: remote checksum |
263 | 282 | msgid "Checksum" |
264 | 283 | msgstr "Checksum" |
268 | 287 | msgstr "Vælg en enhed:" |
269 | 288 | |
270 | 289 | #. TRANSLATORS: get interactive prompt |
290 | msgid "Choose a firmware type:" | |
291 | msgstr "Vælg en firmwaretype:" | |
292 | ||
293 | #. TRANSLATORS: get interactive prompt | |
271 | 294 | msgid "Choose a release:" |
272 | 295 | msgstr "Vælg en udgivelse:" |
273 | 296 | |
283 | 306 | msgid "Command not found" |
284 | 307 | msgstr "Kommandoen blev ikke fundet" |
285 | 308 | |
309 | #. TRANSLATORS: prompt to apply the update | |
310 | msgid "Continue with update?" | |
311 | msgstr "Fortsæt opdateringen?" | |
312 | ||
286 | 313 | #. TRANSLATORS: command description |
287 | 314 | msgid "Convert firmware to DFU format" |
288 | 315 | msgstr "Konverter firmware til DFU-format" |
316 | ||
317 | #. TRANSLATORS: Device supports some form of checksum verification | |
318 | msgid "Cryptographic hash verification is available" | |
319 | msgstr "Bekræftelse af kryptografisk hash er tilgængelig" | |
289 | 320 | |
290 | 321 | #. TRANSLATORS: version number of current firmware |
291 | 322 | msgid "Current version" |
315 | 346 | msgid "Details" |
316 | 347 | msgstr "Detaljer" |
317 | 348 | |
349 | #. TRANSLATORS: description of device ability | |
350 | msgid "Device Flags" | |
351 | msgstr "Enhedsflag" | |
352 | ||
318 | 353 | #. TRANSLATORS: ID for hardware, typically a SHA1 sum |
319 | 354 | msgid "Device ID" |
320 | 355 | msgstr "Enheds-id" |
323 | 358 | msgid "Device added:" |
324 | 359 | msgstr "Enhed tilføjet:" |
325 | 360 | |
361 | #. TRANSLATORS: Device supports a safety mechanism for flashing | |
362 | msgid "Device can recover flash failures" | |
363 | msgstr "Enheden kan gendannes efter mislykkedes flash" | |
364 | ||
326 | 365 | #. TRANSLATORS: this is when a device has been updated |
327 | 366 | msgid "Device changed:" |
328 | 367 | msgstr "Enhed ændret:" |
329 | 368 | |
369 | #. TRANSLATORS: Is locked and can be unlocked | |
370 | msgid "Device is locked" | |
371 | msgstr "Enheden er låst" | |
372 | ||
373 | #. TRANSLATORS: Device remains usable during update | |
374 | msgid "Device is usable for the duration of the update" | |
375 | msgstr "Enheden kan anvendes under hele opdateringen" | |
376 | ||
330 | 377 | #. TRANSLATORS: this is when a device is hotplugged |
331 | 378 | msgid "Device removed:" |
332 | 379 | msgstr "Enhed fjernet:" |
333 | 380 | |
381 | #. TRANSLATORS: Device supports a safety mechanism for flashing | |
382 | msgid "Device stages updates" | |
383 | msgstr "Enhedstrin-opdateringer" | |
384 | ||
385 | #. TRANSLATORS: Device update needs to be separately activated | |
386 | msgid "Device update needs activation" | |
387 | msgstr "Enhedsopdatering behøver aktivering" | |
388 | ||
389 | #. TRANSLATORS: Device will not return after update completes | |
390 | msgid "Device will not re-appear after update completes" | |
391 | msgstr "Enheden vises ikke igen når opdateringen er færdig" | |
392 | ||
334 | 393 | #. TRANSLATORS: a list of successful updates |
335 | 394 | msgid "Devices that have been updated successfully:" |
336 | 395 | msgstr "Enheder som det lykkedes at opdatere:" |
369 | 428 | #. TRANSLATORS: turn on all debugging |
370 | 429 | msgid "Do not include timestamp prefix" |
371 | 430 | msgstr "Medtag ikke tidsstempelpræfiks" |
431 | ||
432 | #. TRANSLATORS: command line option | |
433 | msgid "Do not perform device safety checks" | |
434 | msgstr "Udfør ikke sikkerhedstjek af enhed" | |
372 | 435 | |
373 | 436 | msgid "Do not upload report at this time, but prompt again for future updates" |
374 | 437 | msgid_plural "Do not upload reports at this time, but prompt again for future updates" |
616 | 679 | msgid "Gets the results from the last update" |
617 | 680 | msgstr "Henter resultaterne fra den sidste opdatering" |
618 | 681 | |
682 | #. TRANSLATORS: The hardware is waiting to be replugged | |
683 | msgid "Hardware is waiting to be replugged" | |
684 | msgstr "Hardwaren venter på at bliver gentilkoblet" | |
685 | ||
619 | 686 | #. TRANSLATORS: daemon is inactive |
620 | 687 | msgid "Idle…" |
621 | 688 | msgstr "Inaktiv …" |
624 | 691 | msgid "Ignore SSL strict checks when downloading files" |
625 | 692 | msgstr "Ignorer strikse SSL-tjek ved download af filer" |
626 | 693 | |
694 | #. TRANSLATORS: Ignore validation safety checks when flashing this device | |
695 | msgid "Ignore validation safety checks" | |
696 | msgstr "Ignorer sikkerhedstjek af bekræftelse" | |
697 | ||
627 | 698 | #. TRANSLATORS: length of time the update takes to apply |
628 | 699 | msgid "Install Duration" |
629 | 700 | msgstr "Varighed for installation" |
645 | 716 | msgid "Install signed system firmware" |
646 | 717 | msgstr "Installer systemfirmware der er underskrevet" |
647 | 718 | |
719 | #. TRANSLATORS: Install composite firmware on the parent before the child | |
720 | msgid "Install to parent device first" | |
721 | msgstr "Installer først til forælderenhed" | |
722 | ||
648 | 723 | msgid "Install unsigned device firmware" |
649 | 724 | msgstr "Installer enhedsfirmware der ikke er underskrevet" |
650 | 725 | |
663 | 738 | #, c-format |
664 | 739 | msgid "Installing on %s…" |
665 | 740 | msgstr "Installerer på %s …" |
741 | ||
742 | #. TRANSLATORS: Device cannot be removed easily | |
743 | msgid "Internal device" | |
744 | msgstr "Intern enhed" | |
745 | ||
746 | #. TRANSLATORS: Is currently in bootloader mode | |
747 | msgid "Is in bootloader mode" | |
748 | msgstr "Er i opstartsindlæsertilstand" | |
666 | 749 | |
667 | 750 | #. TRANSLATORS: issue fixed with the release, e.g. CVE |
668 | 751 | msgid "Issue" |
695 | 778 | msgid "List supported firmware updates" |
696 | 779 | msgstr "Vis understøttede firmwareopdateringer" |
697 | 780 | |
781 | #. TRANSLATORS: command description | |
782 | msgid "List the available firmware types" | |
783 | msgstr "Vis de tilgængelige firmwaretyper" | |
784 | ||
698 | 785 | #. TRANSLATORS: parsing the firmware information |
699 | 786 | msgid "Loading…" |
700 | 787 | msgstr "Indlæser …" |
746 | 833 | msgid "Monitor the daemon for events" |
747 | 834 | msgstr "Overvåg dæmonen for hændelser" |
748 | 835 | |
836 | #. TRANSLATORS: Requires a reboot to apply firmware or to reload hardware | |
837 | msgid "Needs a reboot after installation" | |
838 | msgstr "Genstart efter installation er nødvendig" | |
839 | ||
840 | #. TRANSLATORS: Requires system shutdown to apply firmware | |
841 | msgid "Needs shutdown after installation" | |
842 | msgstr "Nedlukning efter installation er nødvendig" | |
843 | ||
749 | 844 | #. TRANSLATORS: version number of new firmware |
750 | 845 | msgid "New version" |
751 | 846 | msgstr "Ny version" |
753 | 848 | msgid "No action specified!" |
754 | 849 | msgstr "Der er ikke angivet nogen handling!" |
755 | 850 | |
851 | #. TRANSLATORS: nothing found | |
852 | msgid "No firmware IDs found" | |
853 | msgstr "Fandt ingen firmware-id'er" | |
854 | ||
756 | 855 | #. TRANSLATORS: nothing attached that can be upgraded |
757 | 856 | msgid "No hardware detected with firmware update capability" |
758 | 857 | msgstr "Der blev ikke fundet nogen hardware med firmware som kan opdateres" |
789 | 888 | msgid "Override warnings and force the action" |
790 | 889 | msgstr "Tilsidesæt advarsler og gennemtving handlingen" |
791 | 890 | |
891 | #. TRANSLATORS: command description | |
892 | msgid "Parse and show details about a firmware file" | |
893 | msgstr "Fortolk og vis deltaljer om en firmwarefil" | |
894 | ||
792 | 895 | #. TRANSLATORS: remote filename base |
793 | 896 | msgid "Password" |
794 | 897 | msgstr "Adgangskode" |
831 | 934 | msgstr "Forespørg om understøttelse af firmwareopdatering" |
832 | 935 | |
833 | 936 | #. TRANSLATORS: command description |
937 | msgid "Read a firmware blob from a device" | |
938 | msgstr "Læs en firmwareblob fra en enhed" | |
939 | ||
940 | #. TRANSLATORS: command description | |
834 | 941 | msgid "Read firmware from device into a file" |
835 | 942 | msgstr "Læs firmware fra enhed ind i en fil" |
836 | 943 | |
837 | 944 | #. TRANSLATORS: command description |
838 | 945 | msgid "Read firmware from one partition into a file" |
839 | 946 | msgstr "Læs firmware fra en partition ind i en fil" |
947 | ||
948 | #. TRANSLATORS: %1 is a device name | |
949 | #, c-format | |
950 | msgid "Reading from %s…" | |
951 | msgstr "Læser fra %s …" | |
840 | 952 | |
841 | 953 | #. TRANSLATORS: reading from the flash chips |
842 | 954 | msgid "Reading…" |
878 | 990 | msgid "Report URI" |
879 | 991 | msgstr "Rapport-URI" |
880 | 992 | |
993 | #. TRANSLATORS: Has been reported to a metadata server | |
994 | msgid "Reported to remote server" | |
995 | msgstr "Rapporteret til fjernserver" | |
996 | ||
997 | #. TRANSLATORS: Must be plugged in to an outlet | |
998 | msgid "Requires AC power" | |
999 | msgstr "Kræver strømforsygning" | |
1000 | ||
1001 | #. TRANSLATORS: Requires a bootloader mode to be manually enabled by the user | |
1002 | msgid "Requires a bootloader" | |
1003 | msgstr "Kræver en opstartsindlæser" | |
1004 | ||
881 | 1005 | #. TRANSLATORS: metadata is downloaded from the Internet |
882 | 1006 | msgid "Requires internet connection" |
883 | 1007 | msgstr "Kræver internetforbindelse" |
917 | 1041 | #. TRANSLATORS: scheduing an update to be done on the next boot |
918 | 1042 | msgid "Scheduling…" |
919 | 1043 | msgstr "Planlægger …" |
1044 | ||
1045 | #. TRANSLATORS: Device has been chosen by the daemon for the user | |
1046 | msgid "Selected device" | |
1047 | msgstr "Valgte enhed" | |
920 | 1048 | |
921 | 1049 | #. TRANSLATORS: serial number of hardware |
922 | 1050 | msgid "Serial Number" |
1052 | 1180 | msgid "Successfully modified configuration value" |
1053 | 1181 | msgstr "Det lykkedes at redigere konfigurationsværdi" |
1054 | 1182 | |
1183 | #. TRANSLATORS: success message for a per-remote setting change | |
1184 | msgid "Successfully modified remote" | |
1185 | msgstr "Det lykkedes at redigere fjern" | |
1186 | ||
1055 | 1187 | #. TRANSLATORS: success message -- the user can do this by-hand too |
1056 | 1188 | msgid "Successfully refreshed metadata manually" |
1057 | 1189 | msgstr "Det lykkedes at opdatere metadata manuelt" |
1190 | ||
1191 | #. TRANSLATORS: success message when user refreshes device checksums | |
1192 | msgid "Successfully updated device checksums" | |
1193 | msgstr "Opdatering af enhedens tjeksumme lykkedes" | |
1058 | 1194 | |
1059 | 1195 | #. TRANSLATORS: success message -- where the user has uploaded |
1060 | 1196 | #. * success and/or failure reports to the remote server |
1064 | 1200 | msgstr[0] "Det lykkedes at uploade %u rapport" |
1065 | 1201 | msgstr[1] "Det lykkedes at uploade %u rapporter" |
1066 | 1202 | |
1203 | #. TRANSLATORS: success message when user verified device checksums | |
1204 | msgid "Successfully verified device checksums" | |
1205 | msgstr "Bekræftelse af enhedens tjeksumme lykkedes" | |
1206 | ||
1067 | 1207 | #. TRANSLATORS: one line summary of device |
1068 | 1208 | msgid "Summary" |
1069 | 1209 | msgstr "Opsummering" |
1210 | ||
1211 | #. TRANSLATORS: Is found in current metadata | |
1212 | msgid "Supported on remote server" | |
1213 | msgstr "Understøttes på fjernserver" | |
1070 | 1214 | |
1071 | 1215 | msgid "Target" |
1072 | 1216 | msgstr "Mål" |
1124 | 1268 | msgid "Unsupported daemon version %s, client version is %s" |
1125 | 1269 | msgstr "Uunderstøttet dæmonversion %s, klientversionen er %s" |
1126 | 1270 | |
1271 | #. TRANSLATORS: Device is updatable in this or any other mode | |
1272 | msgid "Updatable" | |
1273 | msgstr "Kan opdateres" | |
1274 | ||
1127 | 1275 | #. TRANSLATORS: error message from last update attempt |
1128 | 1276 | msgid "Update Error" |
1129 | 1277 | msgstr "Fejl ved opdatering" |
1149 | 1297 | msgid "Update now?" |
1150 | 1298 | msgstr "Opdater nu?" |
1151 | 1299 | |
1300 | #. TRANSLATORS: Update can only be done from offline mode | |
1301 | msgid "Update requires a reboot" | |
1302 | msgstr "Opdatering kræver en genstart" | |
1303 | ||
1304 | #. TRANSLATORS: command description | |
1305 | msgid "Update the stored cryptographic hash with current ROM contents" | |
1306 | msgstr "Opdater den gemte kryptografiske hash med indholdet fra den nuværende ROM" | |
1307 | ||
1152 | 1308 | msgid "Update the stored device verification information" |
1153 | 1309 | msgstr "Opdaterer de gemte informationer om enhedsverifikation" |
1154 | 1310 | |
1171 | 1327 | #, c-format |
1172 | 1328 | msgid "Updating %s…" |
1173 | 1329 | msgstr "Opdaterer %s …" |
1330 | ||
1331 | #. TRANSLATORS: message letting the user know an upgrade is available | |
1332 | #. * %1 is the device name and %2 and %3 are version strings | |
1333 | #, c-format | |
1334 | msgid "Upgrade available for %s from %s to %s" | |
1335 | msgstr "Opgradering tilgængelig for %s fra %s til %s" | |
1174 | 1336 | |
1175 | 1337 | #. TRANSLATORS: the server sent the user a small message |
1176 | 1338 | msgid "Upload message:" |
1198 | 1360 | msgid "Use quirk flags when installing firmware" |
1199 | 1361 | msgstr "Brug quirk-flag ved installation af firmware" |
1200 | 1362 | |
1363 | #. TRANSLATORS: User has been notified | |
1364 | msgid "User has been notified" | |
1365 | msgstr "Brugeren er blevet underrettet" | |
1366 | ||
1201 | 1367 | #. TRANSLATORS: remote filename base |
1202 | 1368 | msgid "Username" |
1203 | 1369 | msgstr "Brugernavn" |
80 | 80 | msgid "%s Update" |
81 | 81 | msgstr "%sPäivitys" |
82 | 82 | |
83 | #. TRANSLATORS: warn the user before updating, %1 is a device name | |
84 | #, c-format | |
85 | msgid "%s and all connected devices may not be usable while updating." | |
86 | msgstr "%s ja kaikki kytketyt laitteet eivät välttämättä ole käyttökelpoisia päivityksen aikana." | |
87 | ||
88 | #. TRANSLATORS: warn the user before updating, %1 is a device name | |
89 | #, c-format | |
90 | msgid "%s must remain connected for the duration of the update to avoid damage." | |
91 | msgstr "%s on oltava kytkettynä päivityksen ajaksi vaurioiden välttämiseksi." | |
92 | ||
93 | #. TRANSLATORS: warn the user before updating, %1 is a machine name | |
94 | #, c-format | |
95 | msgid "%s must remain plugged into a power source for the duration of the update to avoid damage." | |
96 | msgstr "%s on oltava kytkettynä virtalähteeseen päivityksen ajaksi vaurioiden välttämiseksi." | |
97 | ||
83 | 98 | #. TRANSLATORS: duration in days! |
84 | 99 | #, c-format |
85 | 100 | msgid "%u day" |
259 | 274 | msgid "Changed" |
260 | 275 | msgstr "Muutettu" |
261 | 276 | |
277 | #. TRANSLATORS: command description | |
278 | msgid "Checks cryptographic hash matches firmware" | |
279 | msgstr "Tarkistaa salaustekniikan, joka vastaa laiteohjelmistoa" | |
280 | ||
262 | 281 | #. TRANSLATORS: remote checksum |
263 | 282 | msgid "Checksum" |
264 | 283 | msgstr "Tarkistussumma" |
268 | 287 | msgstr "Valitse laite:" |
269 | 288 | |
270 | 289 | #. TRANSLATORS: get interactive prompt |
290 | msgid "Choose a firmware type:" | |
291 | msgstr "Valitse laiteohjelman tyyppi:" | |
292 | ||
293 | #. TRANSLATORS: get interactive prompt | |
271 | 294 | msgid "Choose a release:" |
272 | 295 | msgstr "Valitse julkaisu:" |
273 | 296 | |
283 | 306 | msgid "Command not found" |
284 | 307 | msgstr "Komentoa ei löytynyt" |
285 | 308 | |
309 | #. TRANSLATORS: prompt to apply the update | |
310 | msgid "Continue with update?" | |
311 | msgstr "Jatketaanko päivitystä?" | |
312 | ||
286 | 313 | #. TRANSLATORS: command description |
287 | 314 | msgid "Convert firmware to DFU format" |
288 | 315 | msgstr "Muunna firmware DFU-muotoon" |
316 | ||
317 | #. TRANSLATORS: Device supports some form of checksum verification | |
318 | msgid "Cryptographic hash verification is available" | |
319 | msgstr "Salauksen algoritmin tarkistus on käytettävissä" | |
289 | 320 | |
290 | 321 | #. TRANSLATORS: version number of current firmware |
291 | 322 | msgid "Current version" |
315 | 346 | msgid "Details" |
316 | 347 | msgstr "Tiedot" |
317 | 348 | |
349 | #. TRANSLATORS: description of device ability | |
350 | msgid "Device Flags" | |
351 | msgstr "Laitteen liput" | |
352 | ||
318 | 353 | #. TRANSLATORS: ID for hardware, typically a SHA1 sum |
319 | 354 | msgid "Device ID" |
320 | 355 | msgstr "Laitteen tunnus" |
323 | 358 | msgid "Device added:" |
324 | 359 | msgstr "Laite lisätty:" |
325 | 360 | |
361 | #. TRANSLATORS: Device supports a safety mechanism for flashing | |
362 | msgid "Device can recover flash failures" | |
363 | msgstr "Laite voi palauttaa virheitä" | |
364 | ||
326 | 365 | #. TRANSLATORS: this is when a device has been updated |
327 | 366 | msgid "Device changed:" |
328 | 367 | msgstr "Laite muutettu:" |
329 | 368 | |
369 | #. TRANSLATORS: Is locked and can be unlocked | |
370 | msgid "Device is locked" | |
371 | msgstr "Laite on lukittu" | |
372 | ||
373 | #. TRANSLATORS: Device remains usable during update | |
374 | msgid "Device is usable for the duration of the update" | |
375 | msgstr "Laitetta voidaan käyttää päivityksen aikana" | |
376 | ||
330 | 377 | #. TRANSLATORS: this is when a device is hotplugged |
331 | 378 | msgid "Device removed:" |
332 | 379 | msgstr "Laite poistettu:" |
333 | 380 | |
381 | #. TRANSLATORS: Device supports a safety mechanism for flashing | |
382 | msgid "Device stages updates" | |
383 | msgstr "Laitevyöhykkeen päivitykset" | |
384 | ||
385 | #. TRANSLATORS: Device update needs to be separately activated | |
386 | msgid "Device update needs activation" | |
387 | msgstr "Laitteen päivitys tarvitsee aktivointia" | |
388 | ||
389 | #. TRANSLATORS: Device will not return after update completes | |
390 | msgid "Device will not re-appear after update completes" | |
391 | msgstr "Laitetta ei näytetä uudelleen päivityksen päätyttyä" | |
392 | ||
334 | 393 | #. TRANSLATORS: a list of successful updates |
335 | 394 | msgid "Devices that have been updated successfully:" |
336 | 395 | msgstr "Laitteet, jotka on päivitetty onnistuneesti:" |
369 | 428 | #. TRANSLATORS: turn on all debugging |
370 | 429 | msgid "Do not include timestamp prefix" |
371 | 430 | msgstr "Älä sisällytä aikaleiman etuliitettä" |
431 | ||
432 | #. TRANSLATORS: command line option | |
433 | msgid "Do not perform device safety checks" | |
434 | msgstr "Älä suorita laitteen turvallisuustarkastuksia" | |
372 | 435 | |
373 | 436 | msgid "Do not upload report at this time, but prompt again for future updates" |
374 | 437 | msgid_plural "Do not upload reports at this time, but prompt again for future updates" |
616 | 679 | msgid "Gets the results from the last update" |
617 | 680 | msgstr "Saat viimeisimmän päivityksen tulokset" |
618 | 681 | |
682 | #. TRANSLATORS: The hardware is waiting to be replugged | |
683 | msgid "Hardware is waiting to be replugged" | |
684 | msgstr "Laitteisto odottaa uudelleen kytkemistä" | |
685 | ||
619 | 686 | #. TRANSLATORS: daemon is inactive |
620 | 687 | msgid "Idle…" |
621 | 688 | msgstr "Jouten…" |
624 | 691 | msgid "Ignore SSL strict checks when downloading files" |
625 | 692 | msgstr "Ohita tiukat SSL tarkistukset tiedostoja ladattaessa" |
626 | 693 | |
694 | #. TRANSLATORS: Ignore validation safety checks when flashing this device | |
695 | msgid "Ignore validation safety checks" | |
696 | msgstr "Ohita turvatarkastukset" | |
697 | ||
627 | 698 | #. TRANSLATORS: length of time the update takes to apply |
628 | 699 | msgid "Install Duration" |
629 | 700 | msgstr "Asennuksen kesto" |
645 | 716 | msgid "Install signed system firmware" |
646 | 717 | msgstr "Asenna allekirjoitettu järjestelmän firmware" |
647 | 718 | |
719 | #. TRANSLATORS: Install composite firmware on the parent before the child | |
720 | msgid "Install to parent device first" | |
721 | msgstr "Asenna ensin päälaitteelle" | |
722 | ||
648 | 723 | msgid "Install unsigned device firmware" |
649 | 724 | msgstr "Asenna allekirjoittamattoman laitteen firmware" |
650 | 725 | |
663 | 738 | #, c-format |
664 | 739 | msgid "Installing on %s…" |
665 | 740 | msgstr "Asentaa %s…" |
741 | ||
742 | #. TRANSLATORS: Device cannot be removed easily | |
743 | msgid "Internal device" | |
744 | msgstr "Sisäinen laite" | |
745 | ||
746 | #. TRANSLATORS: Is currently in bootloader mode | |
747 | msgid "Is in bootloader mode" | |
748 | msgstr "On käynnistyslatain moodissa" | |
666 | 749 | |
667 | 750 | #. TRANSLATORS: issue fixed with the release, e.g. CVE |
668 | 751 | msgid "Issue" |
695 | 778 | msgid "List supported firmware updates" |
696 | 779 | msgstr "Lista tuetuista firmware-päivityksistä" |
697 | 780 | |
781 | #. TRANSLATORS: command description | |
782 | msgid "List the available firmware types" | |
783 | msgstr "Luettelo käytettävissä olevista laiteohjelmistotyypeistä" | |
784 | ||
698 | 785 | #. TRANSLATORS: parsing the firmware information |
699 | 786 | msgid "Loading…" |
700 | 787 | msgstr "Ladataan…" |
746 | 833 | msgid "Monitor the daemon for events" |
747 | 834 | msgstr "Seuraa tapahtumien taustaprosessia" |
748 | 835 | |
836 | #. TRANSLATORS: Requires a reboot to apply firmware or to reload hardware | |
837 | msgid "Needs a reboot after installation" | |
838 | msgstr "Tarvitsee käynnistyksen asennuksen jälkeen" | |
839 | ||
840 | #. TRANSLATORS: Requires system shutdown to apply firmware | |
841 | msgid "Needs shutdown after installation" | |
842 | msgstr "Tarvitsee sammutuksen asennuksen jälkeen" | |
843 | ||
749 | 844 | #. TRANSLATORS: version number of new firmware |
750 | 845 | msgid "New version" |
751 | 846 | msgstr "Uusi versio" |
753 | 848 | msgid "No action specified!" |
754 | 849 | msgstr "Toimintoa ei ole määritetty!" |
755 | 850 | |
851 | #. TRANSLATORS: nothing found | |
852 | msgid "No firmware IDs found" | |
853 | msgstr "Laiteohjelmiston tunnuksia ei löytynyt" | |
854 | ||
756 | 855 | #. TRANSLATORS: nothing attached that can be upgraded |
757 | 856 | msgid "No hardware detected with firmware update capability" |
758 | 857 | msgstr "Ei havaittu sopivaa laitteistoa firmware päivitykselle" |
789 | 888 | msgid "Override warnings and force the action" |
790 | 889 | msgstr "Ohita varoitukset ja pakota toiminto" |
791 | 890 | |
891 | #. TRANSLATORS: command description | |
892 | msgid "Parse and show details about a firmware file" | |
893 | msgstr "Analysoi ja näytä tiedot laiteohjelmiston tiedostosta" | |
894 | ||
792 | 895 | #. TRANSLATORS: remote filename base |
793 | 896 | msgid "Password" |
794 | 897 | msgstr "Salasana" |
831 | 934 | msgstr "Kysely firmware-päivityksen tuesta" |
832 | 935 | |
833 | 936 | #. TRANSLATORS: command description |
937 | msgid "Read a firmware blob from a device" | |
938 | msgstr "Lue laiteohjelmiston BLOB laitteesta" | |
939 | ||
940 | #. TRANSLATORS: command description | |
834 | 941 | msgid "Read firmware from device into a file" |
835 | 942 | msgstr "Lue firmware laitteesta tiedostoon" |
836 | 943 | |
837 | 944 | #. TRANSLATORS: command description |
838 | 945 | msgid "Read firmware from one partition into a file" |
839 | 946 | msgstr "Lue firmware osiolta tiedostoon" |
947 | ||
948 | #. TRANSLATORS: %1 is a device name | |
949 | #, c-format | |
950 | msgid "Reading from %s…" | |
951 | msgstr "Lukeminen %s…" | |
840 | 952 | |
841 | 953 | #. TRANSLATORS: reading from the flash chips |
842 | 954 | msgid "Reading…" |
878 | 990 | msgid "Report URI" |
879 | 991 | msgstr "Ilmoita URI" |
880 | 992 | |
993 | #. TRANSLATORS: Has been reported to a metadata server | |
994 | msgid "Reported to remote server" | |
995 | msgstr "Raportoitu etäpalvelimelle" | |
996 | ||
997 | #. TRANSLATORS: Must be plugged in to an outlet | |
998 | msgid "Requires AC power" | |
999 | msgstr "Vaatii verkkovirtaa" | |
1000 | ||
1001 | #. TRANSLATORS: Requires a bootloader mode to be manually enabled by the user | |
1002 | msgid "Requires a bootloader" | |
1003 | msgstr "Vaatii käynnistyslataimen" | |
1004 | ||
881 | 1005 | #. TRANSLATORS: metadata is downloaded from the Internet |
882 | 1006 | msgid "Requires internet connection" |
883 | 1007 | msgstr "Vaatii Internet-yhteyden" |
917 | 1041 | #. TRANSLATORS: scheduing an update to be done on the next boot |
918 | 1042 | msgid "Scheduling…" |
919 | 1043 | msgstr "Ajoitetaan…" |
1044 | ||
1045 | #. TRANSLATORS: Device has been chosen by the daemon for the user | |
1046 | msgid "Selected device" | |
1047 | msgstr "Valittu laite" | |
920 | 1048 | |
921 | 1049 | #. TRANSLATORS: serial number of hardware |
922 | 1050 | msgid "Serial Number" |
1052 | 1180 | msgid "Successfully modified configuration value" |
1053 | 1181 | msgstr "Konfigurointiarvo on muokattu onnistuneesti" |
1054 | 1182 | |
1183 | #. TRANSLATORS: success message for a per-remote setting change | |
1184 | msgid "Successfully modified remote" | |
1185 | msgstr "Etäkäytön muokkaus onnistui" | |
1186 | ||
1055 | 1187 | #. TRANSLATORS: success message -- the user can do this by-hand too |
1056 | 1188 | msgid "Successfully refreshed metadata manually" |
1057 | 1189 | msgstr "Päivitetty metatiedot manuaalisesti" |
1190 | ||
1191 | #. TRANSLATORS: success message when user refreshes device checksums | |
1192 | msgid "Successfully updated device checksums" | |
1193 | msgstr "Laitteiden tarkistussummat päivitettiin onnistuneesti" | |
1058 | 1194 | |
1059 | 1195 | #. TRANSLATORS: success message -- where the user has uploaded |
1060 | 1196 | #. * success and/or failure reports to the remote server |
1064 | 1200 | msgstr[0] "Raporttien lataus %u onnistui" |
1065 | 1201 | msgstr[1] "Raporttien lataus %u onnistui" |
1066 | 1202 | |
1203 | #. TRANSLATORS: success message when user verified device checksums | |
1204 | msgid "Successfully verified device checksums" | |
1205 | msgstr "Laitteiden tarkistussummat vahvistettu onnistuneesti" | |
1206 | ||
1067 | 1207 | #. TRANSLATORS: one line summary of device |
1068 | 1208 | msgid "Summary" |
1069 | 1209 | msgstr "Yhteenveto" |
1210 | ||
1211 | #. TRANSLATORS: Is found in current metadata | |
1212 | msgid "Supported on remote server" | |
1213 | msgstr "Tuettu etäpalvelimella" | |
1070 | 1214 | |
1071 | 1215 | msgid "Target" |
1072 | 1216 | msgstr "Kohde" |
1124 | 1268 | msgid "Unsupported daemon version %s, client version is %s" |
1125 | 1269 | msgstr "Ei tuettu taustaprosessin versio %s, asiakasversio on %s" |
1126 | 1270 | |
1271 | #. TRANSLATORS: Device is updatable in this or any other mode | |
1272 | msgid "Updatable" | |
1273 | msgstr "Päivitettävissä" | |
1274 | ||
1127 | 1275 | #. TRANSLATORS: error message from last update attempt |
1128 | 1276 | msgid "Update Error" |
1129 | 1277 | msgstr "Päivitys virhe" |
1149 | 1297 | msgid "Update now?" |
1150 | 1298 | msgstr "Päivitä nyt?" |
1151 | 1299 | |
1300 | #. TRANSLATORS: Update can only be done from offline mode | |
1301 | msgid "Update requires a reboot" | |
1302 | msgstr "Päivitys edellyttää käynnistyksen" | |
1303 | ||
1304 | #. TRANSLATORS: command description | |
1305 | msgid "Update the stored cryptographic hash with current ROM contents" | |
1306 | msgstr "Päivitä tallennettu salaus nykyisellä ROM-sisällöllä" | |
1307 | ||
1152 | 1308 | msgid "Update the stored device verification information" |
1153 | 1309 | msgstr "Päivitä laitteen tallennetut vahvistustiedot" |
1154 | 1310 | |
1171 | 1327 | #, c-format |
1172 | 1328 | msgid "Updating %s…" |
1173 | 1329 | msgstr "Päivittää %s…" |
1330 | ||
1331 | #. TRANSLATORS: message letting the user know an upgrade is available | |
1332 | #. * %1 is the device name and %2 and %3 are version strings | |
1333 | #, c-format | |
1334 | msgid "Upgrade available for %s from %s to %s" | |
1335 | msgstr "Päivitys saatavilla %s alkaen %s kohde %s" | |
1174 | 1336 | |
1175 | 1337 | #. TRANSLATORS: the server sent the user a small message |
1176 | 1338 | msgid "Upload message:" |
1198 | 1360 | msgid "Use quirk flags when installing firmware" |
1199 | 1361 | msgstr "Käytä Quirk-lippuja asennettaessa laiteohjelmistoa" |
1200 | 1362 | |
1363 | #. TRANSLATORS: User has been notified | |
1364 | msgid "User has been notified" | |
1365 | msgstr "Käyttäjälle on ilmoitettu" | |
1366 | ||
1201 | 1367 | #. TRANSLATORS: remote filename base |
1202 | 1368 | msgid "Username" |
1203 | 1369 | msgstr "Käyttäjätunnus" |
24 | 24 | msgstr[0] "%.0f perc van hátra" |
25 | 25 | msgstr[1] "%.0f perc van hátra" |
26 | 26 | |
27 | #. TRANSLATORS: ME stands for Management Engine, where | |
28 | #. * the first %s is the device name, e.g. 'ThinkPad P50` | |
29 | #, c-format | |
30 | msgid "%s Consumer ME Update" | |
31 | msgstr "%s fogyasztói ME frissítés" | |
32 | ||
27 | 33 | #. TRANSLATORS: the controller is a device that has other devices |
28 | 34 | #. * plugged into it, for example ThunderBolt, FireWire or USB, |
29 | 35 | #. * the first %s is the device name, e.g. 'Intel ThunderBolt` |
30 | 36 | #, c-format |
31 | 37 | msgid "%s Controller Update" |
32 | 38 | msgstr "%s vezérlőfrissítés" |
39 | ||
40 | #. TRANSLATORS: ME stands for Management Engine (with Intel AMT), | |
41 | #. * where the first %s is the device name, e.g. 'ThinkPad P50` | |
42 | #, c-format | |
43 | msgid "%s Corporate ME Update" | |
44 | msgstr "%s vállalati ME frissítés" | |
33 | 45 | |
34 | 46 | #. TRANSLATORS: a specific part of hardware, |
35 | 47 | #. * the first %s is the device name, e.g. 'Unifying Receiver` |
70 | 82 | msgid "%s Update" |
71 | 83 | msgstr "%s frissítés" |
72 | 84 | |
85 | #. TRANSLATORS: warn the user before updating, %1 is a device name | |
86 | #, c-format | |
87 | msgid "%s and all connected devices may not be usable while updating." | |
88 | msgstr "Lehet, hogy a(z) %s és az összes kapcsolódó eszköz használhatatlan lesz a frissítés alatt." | |
89 | ||
90 | #. TRANSLATORS: warn the user before updating, %1 is a device name | |
91 | #, c-format | |
92 | msgid "%s must remain connected for the duration of the update to avoid damage." | |
93 | 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." | |
94 | ||
95 | #. TRANSLATORS: warn the user before updating, %1 is a machine name | |
96 | #, c-format | |
97 | msgid "%s must remain plugged into a power source for the duration of the update to avoid damage." | |
98 | 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." | |
99 | ||
73 | 100 | #. TRANSLATORS: duration in days! |
74 | 101 | #, c-format |
75 | 102 | msgid "%u day" |
83 | 110 | msgid_plural "%u hours" |
84 | 111 | msgstr[0] "%u óra" |
85 | 112 | msgstr[1] "%u óra" |
113 | ||
114 | #. TRANSLATORS: how many local devices can expect updates now | |
115 | #, c-format | |
116 | msgid "%u local device supported" | |
117 | msgid_plural "%u local devices supported" | |
118 | msgstr[0] "%u helyi eszköz támogatott" | |
119 | msgstr[1] "%u helyi eszköz támogatott" | |
86 | 120 | |
87 | 121 | #. TRANSLATORS: duration in minutes |
88 | 122 | #, c-format |
242 | 276 | msgid "Changed" |
243 | 277 | msgstr "Módosítva" |
244 | 278 | |
279 | #. TRANSLATORS: command description | |
280 | msgid "Checks cryptographic hash matches firmware" | |
281 | msgstr "Ellenőrzi, hogy a kriptográfiai hash egyezik-e a firmware-rel" | |
282 | ||
245 | 283 | #. TRANSLATORS: remote checksum |
246 | 284 | msgid "Checksum" |
247 | 285 | msgstr "Ellenőrzőösszeg" |
251 | 289 | msgstr "Válasszon eszközt:" |
252 | 290 | |
253 | 291 | #. TRANSLATORS: get interactive prompt |
292 | msgid "Choose a firmware type:" | |
293 | msgstr "Válasszon firmware típust:" | |
294 | ||
295 | #. TRANSLATORS: get interactive prompt | |
254 | 296 | msgid "Choose a release:" |
255 | 297 | msgstr "Válasszon kiadást:" |
256 | 298 | |
266 | 308 | msgid "Command not found" |
267 | 309 | msgstr "A parancs nem található" |
268 | 310 | |
311 | #. TRANSLATORS: prompt to apply the update | |
312 | msgid "Continue with update?" | |
313 | msgstr "Folytatja a frissítést?" | |
314 | ||
269 | 315 | #. TRANSLATORS: command description |
270 | 316 | msgid "Convert firmware to DFU format" |
271 | 317 | msgstr "Firmware átalakítása DFU formátumra" |
318 | ||
319 | #. TRANSLATORS: Device supports some form of checksum verification | |
320 | msgid "Cryptographic hash verification is available" | |
321 | msgstr "Kriptográfiai hash ellenőrzés érhető el" | |
272 | 322 | |
273 | 323 | #. TRANSLATORS: version number of current firmware |
274 | 324 | msgid "Current version" |
292 | 342 | |
293 | 343 | #. TRANSLATORS: command description |
294 | 344 | msgid "Detach to bootloader mode" |
295 | msgstr "Leválasztás a indítóbetöltő módhoz" | |
345 | msgstr "Leválasztás a rendszerbetöltő módhoz" | |
296 | 346 | |
297 | 347 | #. TRANSLATORS: more details about the update link |
298 | 348 | msgid "Details" |
299 | 349 | msgstr "Részletek" |
300 | 350 | |
351 | #. TRANSLATORS: description of device ability | |
352 | msgid "Device Flags" | |
353 | msgstr "Eszközjelzők" | |
354 | ||
301 | 355 | #. TRANSLATORS: ID for hardware, typically a SHA1 sum |
302 | 356 | msgid "Device ID" |
303 | 357 | msgstr "Eszközazonosító" |
306 | 360 | msgid "Device added:" |
307 | 361 | msgstr "Eszköz hozzáadva:" |
308 | 362 | |
363 | #. TRANSLATORS: Device supports a safety mechanism for flashing | |
364 | msgid "Device can recover flash failures" | |
365 | msgstr "Az eszköz képes helyreállni a felülírási hibák után" | |
366 | ||
309 | 367 | #. TRANSLATORS: this is when a device has been updated |
310 | 368 | msgid "Device changed:" |
311 | 369 | msgstr "Eszköz módosítva:" |
312 | 370 | |
371 | #. TRANSLATORS: Is locked and can be unlocked | |
372 | msgid "Device is locked" | |
373 | msgstr "Az eszköz zárolt" | |
374 | ||
375 | #. TRANSLATORS: Device remains usable during update | |
376 | msgid "Device is usable for the duration of the update" | |
377 | msgstr "Az eszköz használható marad a frissítés ideje alatt" | |
378 | ||
313 | 379 | #. TRANSLATORS: this is when a device is hotplugged |
314 | 380 | msgid "Device removed:" |
315 | 381 | msgstr "Eszköz eltávolítva:" |
316 | 382 | |
383 | #. TRANSLATORS: Device supports a safety mechanism for flashing | |
384 | msgid "Device stages updates" | |
385 | msgstr "Az eszköz szakaszosan frissít" | |
386 | ||
387 | #. TRANSLATORS: Device update needs to be separately activated | |
388 | msgid "Device update needs activation" | |
389 | msgstr "Az eszközfrissítés aktiválást igényel" | |
390 | ||
391 | #. TRANSLATORS: Device will not return after update completes | |
392 | msgid "Device will not re-appear after update completes" | |
393 | msgstr "Az eszköz a frissítés végeztével újra meg fog jelenni" | |
394 | ||
317 | 395 | #. TRANSLATORS: a list of successful updates |
318 | 396 | msgid "Devices that have been updated successfully:" |
319 | 397 | msgstr "Eszközök, melyek sikeresen frissítve lettek:" |
354 | 432 | msgstr "Ne tartalmazza az időbélyeg előtagot" |
355 | 433 | |
356 | 434 | #. TRANSLATORS: command line option |
435 | msgid "Do not perform device safety checks" | |
436 | msgstr "Ne végezzen eszköz biztonsági ellenőrzéseket" | |
437 | ||
438 | msgid "Do not upload report at this time, but prompt again for future updates" | |
439 | msgid_plural "Do not upload reports at this time, but prompt again for future updates" | |
440 | msgstr[0] "Most ne töltsön fel jelentést, de kérdezzen rá a jövőben" | |
441 | msgstr[1] "Most ne töltsön fel jelentéseket, de kérdezzen rá a jövőben" | |
442 | ||
443 | msgid "Do not upload report, and never ask to upload reports for future updates" | |
444 | msgid_plural "Do not upload reports, and never ask to upload reports for future updates" | |
445 | msgstr[0] "Ne töltsön fel jelentést, és sose kérdezzen rá a jövőben" | |
446 | msgstr[1] "Ne töltsön fel jelentéseket, és sose kérdezzen rá a jövőben" | |
447 | ||
448 | #. TRANSLATORS: command line option | |
357 | 449 | msgid "Do not write to the history database" |
358 | 450 | msgstr "Ne írjon az előzmények adatbázisába" |
359 | 451 | |
462 | 554 | #. TRANSLATORS: the user didn't read the man page |
463 | 555 | msgid "Failed to parse arguments" |
464 | 556 | msgstr "Nem sikerült feldolgozni az argumentumokat" |
557 | ||
558 | #. TRANSLATORS: the user didn't read the man page | |
559 | msgid "Failed to parse flags for --filter" | |
560 | msgstr "A --filter jelzőinek feldolgozása sikertelen" | |
465 | 561 | |
466 | 562 | #. TRANSLATORS: we could not reboot for some reason |
467 | 563 | msgid "Failed to reboot" |
585 | 681 | msgid "Gets the results from the last update" |
586 | 682 | msgstr "A legutóbbi frissítésből származó eredményeket kéri le" |
587 | 683 | |
684 | #. TRANSLATORS: The hardware is waiting to be replugged | |
685 | msgid "Hardware is waiting to be replugged" | |
686 | msgstr "A hardver újracsatlakoztatásra vár" | |
687 | ||
588 | 688 | #. TRANSLATORS: daemon is inactive |
589 | 689 | msgid "Idle…" |
590 | 690 | msgstr "Üresjárat…" |
593 | 693 | msgid "Ignore SSL strict checks when downloading files" |
594 | 694 | msgstr "A szogorú SSL ellenőrzések mellőzése a fájlok letöltésekor" |
595 | 695 | |
696 | #. TRANSLATORS: Ignore validation safety checks when flashing this device | |
697 | msgid "Ignore validation safety checks" | |
698 | msgstr "Biztonsági ellenőrzések mellőzése" | |
699 | ||
596 | 700 | #. TRANSLATORS: length of time the update takes to apply |
597 | 701 | msgid "Install Duration" |
598 | 702 | msgstr "Telepítés hossza" |
614 | 718 | msgid "Install signed system firmware" |
615 | 719 | msgstr "Aláírt rendszer firmware telepítése" |
616 | 720 | |
721 | #. TRANSLATORS: Install composite firmware on the parent before the child | |
722 | msgid "Install to parent device first" | |
723 | msgstr "Elsőként telepítés a szülő eszközre" | |
724 | ||
617 | 725 | msgid "Install unsigned device firmware" |
618 | 726 | msgstr "Nem aláírt eszköz firmware telepítése" |
619 | 727 | |
632 | 740 | #, c-format |
633 | 741 | msgid "Installing on %s…" |
634 | 742 | msgstr "%s telepítése…" |
743 | ||
744 | #. TRANSLATORS: Device cannot be removed easily | |
745 | msgid "Internal device" | |
746 | msgstr "Belső eszköz" | |
747 | ||
748 | #. TRANSLATORS: Is currently in bootloader mode | |
749 | msgid "Is in bootloader mode" | |
750 | msgstr "Rendszerbetöltő módban van" | |
635 | 751 | |
636 | 752 | #. TRANSLATORS: issue fixed with the release, e.g. CVE |
637 | 753 | msgid "Issue" |
664 | 780 | msgid "List supported firmware updates" |
665 | 781 | msgstr "A támogatott firmware-frissítések listázása" |
666 | 782 | |
783 | #. TRANSLATORS: command description | |
784 | msgid "List the available firmware types" | |
785 | msgstr "Az elérhető firmware típusok listázása" | |
786 | ||
667 | 787 | #. TRANSLATORS: parsing the firmware information |
668 | 788 | msgid "Loading…" |
669 | 789 | msgstr "Betöltés…" |
670 | 790 | |
671 | 791 | #. TRANSLATORS: command line option |
792 | msgid "Log output to FILE (typically for scripting use)" | |
793 | msgstr "Kimenet naplózása FÁJLba (jellemzően parancsfájlokhoz)" | |
794 | ||
795 | #. TRANSLATORS: command line option | |
672 | 796 | msgid "Manually whitelist specific plugins" |
673 | 797 | msgstr "Egyes bővítmények kézi fehérlistára tétele" |
674 | 798 | |
688 | 812 | msgid "Minimum Version" |
689 | 813 | msgstr "Legkisebb verzió" |
690 | 814 | |
815 | #. TRANSLATORS: error message | |
816 | #, c-format | |
817 | msgid "Mismatched daemon and client, use %s instead" | |
818 | msgstr "Eltérő démon és kliens, inkább ezt használja: %s" | |
819 | ||
820 | #. TRANSLATORS: sets something in daemon.conf | |
821 | msgid "Modifies a daemon configuration value." | |
822 | msgstr "Módosítja a démon egy konfigurációs értékét." | |
823 | ||
691 | 824 | #. TRANSLATORS: command description |
692 | 825 | msgid "Modifies a given remote" |
693 | 826 | msgstr "A megadott távoli tároló módosítása" |
701 | 834 | #. TRANSLATORS: command description |
702 | 835 | msgid "Monitor the daemon for events" |
703 | 836 | msgstr "A démon eseményeinek figyelése" |
837 | ||
838 | #. TRANSLATORS: Requires a reboot to apply firmware or to reload hardware | |
839 | msgid "Needs a reboot after installation" | |
840 | msgstr "A telepítés után újraindítást igényel" | |
841 | ||
842 | #. TRANSLATORS: Requires system shutdown to apply firmware | |
843 | msgid "Needs shutdown after installation" | |
844 | msgstr "A telepítés után kikapcsolást igényel" | |
704 | 845 | |
705 | 846 | #. TRANSLATORS: version number of new firmware |
706 | 847 | msgid "New version" |
709 | 850 | msgid "No action specified!" |
710 | 851 | msgstr "Nincs művelet megadva!" |
711 | 852 | |
853 | #. TRANSLATORS: nothing found | |
854 | msgid "No firmware IDs found" | |
855 | msgstr "Nem találhatók firmware azonosítók" | |
856 | ||
712 | 857 | #. TRANSLATORS: nothing attached that can be upgraded |
713 | 858 | msgid "No hardware detected with firmware update capability" |
714 | 859 | msgstr "Nem észlelhető firmware frissítési képességgel rendelkező hardver" |
717 | 862 | msgid "No plugins found" |
718 | 863 | msgstr "Nem található bővítmény" |
719 | 864 | |
865 | #. TRANSLATORS: no repositories to download from | |
866 | msgid "No releases available" | |
867 | msgstr "Nincsenek elérhető kiadások" | |
868 | ||
720 | 869 | #. TRANSLATORS: explain why no metadata available |
721 | 870 | msgid "No remotes are currently enabled so no metadata is available." |
722 | 871 | msgstr "Jelenleg nincsenek engedélyezett távoli tárolók, így nem érhetőek el metaadatok." |
723 | 872 | |
873 | #. TRANSLATORS: no repositories to download from | |
874 | msgid "No remotes available" | |
875 | msgstr "Nincsenek elérhető távoli tárolók" | |
876 | ||
724 | 877 | #. TRANSLATORS: nothing was updated offline |
725 | 878 | msgid "No updates were applied" |
726 | 879 | msgstr "Nem lett frissítés alkalmazva" |
733 | 886 | msgid "Override the default ESP path" |
734 | 887 | msgstr "Az alapértelmezett ESP útvonal felülbírálása" |
735 | 888 | |
889 | #. TRANSLATORS: command line option | |
890 | msgid "Override warnings and force the action" | |
891 | msgstr "Figyelmeztetések felülbírálása, és a művelet kényszerítése" | |
892 | ||
893 | #. TRANSLATORS: command description | |
894 | msgid "Parse and show details about a firmware file" | |
895 | msgstr "A firmware fájl részleteinek értelmezése és megjelenítése" | |
896 | ||
736 | 897 | #. TRANSLATORS: remote filename base |
737 | 898 | msgid "Password" |
738 | 899 | msgstr "Jelszó" |
775 | 936 | msgstr "Firmware frissítési támogatás lekérdezése" |
776 | 937 | |
777 | 938 | #. TRANSLATORS: command description |
939 | msgid "Read a firmware blob from a device" | |
940 | msgstr "Egy firmware blob beolvasása egy eszközről" | |
941 | ||
942 | #. TRANSLATORS: command description | |
778 | 943 | msgid "Read firmware from device into a file" |
779 | 944 | msgstr "Firmware beolvasása eszközről egy fájlba" |
780 | 945 | |
781 | 946 | #. TRANSLATORS: command description |
782 | 947 | msgid "Read firmware from one partition into a file" |
783 | 948 | msgstr "Firmware beolvasása egy partícióról fájlba" |
949 | ||
950 | #. TRANSLATORS: %1 is a device name | |
951 | #, c-format | |
952 | msgid "Reading from %s…" | |
953 | msgstr "Olvasás a(z) %s eszközről…" | |
784 | 954 | |
785 | 955 | #. TRANSLATORS: reading from the flash chips |
786 | 956 | msgid "Reading…" |
793 | 963 | #. TRANSLATORS: command description |
794 | 964 | msgid "Refresh metadata from remote server" |
795 | 965 | msgstr "Metaadatok frissítése a távoli kiszolgálóról" |
966 | ||
967 | #. TRANSLATORS: command description | |
968 | msgid "Reinstall current firmware on the device." | |
969 | msgstr "Újratelepíti a jelenlegi firmware-t az eszközön." | |
796 | 970 | |
797 | 971 | #. TRANSLATORS: the first replacement is a display name |
798 | 972 | #. * e.g. "ColorHugALS" and the second is a version number |
818 | 992 | msgid "Report URI" |
819 | 993 | msgstr "Jelentési URI" |
820 | 994 | |
995 | #. TRANSLATORS: Has been reported to a metadata server | |
996 | msgid "Reported to remote server" | |
997 | msgstr "Jelentve a távoli kiszolgálónak" | |
998 | ||
999 | #. TRANSLATORS: Must be plugged in to an outlet | |
1000 | msgid "Requires AC power" | |
1001 | msgstr "Hálózati áramforrás szükséges" | |
1002 | ||
1003 | #. TRANSLATORS: Requires a bootloader mode to be manually enabled by the user | |
1004 | msgid "Requires a bootloader" | |
1005 | msgstr "Rendszerbetöltő szükséges" | |
1006 | ||
821 | 1007 | #. TRANSLATORS: metadata is downloaded from the Internet |
822 | 1008 | msgid "Requires internet connection" |
823 | 1009 | msgstr "Internetkapcsolat szükséges" |
826 | 1012 | msgid "Restart now?" |
827 | 1013 | msgstr "Újraindítja most?" |
828 | 1014 | |
1015 | #. TRANSLATORS: configuration changes only take effect on restart | |
1016 | msgid "Restart the daemon to make the change effective?" | |
1017 | msgstr "Újraindítja a démont a változás életbe léptetéséhez?" | |
1018 | ||
829 | 1019 | #. TRANSLATORS: restarting the device to pick up new F/W |
830 | 1020 | msgid "Restarting device…" |
831 | 1021 | msgstr "Eszköz újraindítása…" |
835 | 1025 | msgstr "A géphez tartozó összes hardverazonosító visszaadása" |
836 | 1026 | |
837 | 1027 | #. TRANSLATORS: command line option |
1028 | msgid "Run the plugin composite cleanup routine when using install-blob" | |
1029 | msgstr "A bővítmény összetett tisztítási rutinjának futtatása az install-blob használatakor" | |
1030 | ||
1031 | #. TRANSLATORS: command line option | |
1032 | msgid "Run the plugin composite prepare routine when using install-blob" | |
1033 | msgstr "A bővítmény összetett előkészítési rutinjának futtatása az install-blob használatakor" | |
1034 | ||
1035 | #. TRANSLATORS: command line option | |
838 | 1036 | msgid "Save device state into a JSON file between executions" |
839 | 1037 | msgstr "Eszköz állapotának mentése JSON fájlba a végrehajtások között" |
840 | 1038 | |
846 | 1044 | msgid "Scheduling…" |
847 | 1045 | msgstr "Ütemezés…" |
848 | 1046 | |
1047 | #. TRANSLATORS: Device has been chosen by the daemon for the user | |
1048 | msgid "Selected device" | |
1049 | msgstr "Kiválasztott eszköz" | |
1050 | ||
849 | 1051 | #. TRANSLATORS: serial number of hardware |
850 | 1052 | msgid "Serial Number" |
851 | 1053 | msgstr "Sorozatszám" |
949 | 1151 | msgid "Specify the number of bytes per USB transfer" |
950 | 1152 | msgstr "Adja meg az USB átvitelek bájtjainak számát" |
951 | 1153 | |
1154 | #. TRANSLATORS: success message -- where activation is making the new | |
1155 | #. * firmware take effect, usually after updating offline | |
1156 | msgid "Successfully activated all devices" | |
1157 | msgstr "Az összes eszköz sikeresen aktiválva" | |
1158 | ||
1159 | #. TRANSLATORS: success message | |
1160 | msgid "Successfully disabled remote" | |
1161 | msgstr "Távoli tároló sikeresen letiltva" | |
1162 | ||
1163 | #. TRANSLATORS: success message where we made the firmware on the | |
1164 | #. * device older than it was before | |
1165 | msgid "Successfully downgraded device" | |
1166 | msgstr "Eszköz sikeresen visszaállítva" | |
1167 | ||
1168 | #. TRANSLATORS: success message -- where 'metadata' is information | |
1169 | #. * about available firmware on the remote server | |
1170 | msgid "Successfully downloaded new metadata: " | |
1171 | msgstr "Új metaadatok sikeresen letöltve:" | |
1172 | ||
1173 | #. TRANSLATORS: success message | |
1174 | msgid "Successfully enabled remote" | |
1175 | msgstr "Távoli tároló sikeresen engedélyezve" | |
1176 | ||
1177 | #. TRANSLATORS: success message | |
1178 | msgid "Successfully installed firmware" | |
1179 | msgstr "Firmware sikeresen telepítve" | |
1180 | ||
1181 | #. TRANSLATORS: success message -- a per-system setting value | |
1182 | msgid "Successfully modified configuration value" | |
1183 | msgstr "Konfigurációs érték sikeresen módosítva" | |
1184 | ||
1185 | #. TRANSLATORS: success message for a per-remote setting change | |
1186 | msgid "Successfully modified remote" | |
1187 | msgstr "Távoli tároló sikeresen módosítva" | |
1188 | ||
1189 | #. TRANSLATORS: success message -- the user can do this by-hand too | |
1190 | msgid "Successfully refreshed metadata manually" | |
1191 | msgstr "Metaadatok kézi frissítése sikeres" | |
1192 | ||
1193 | #. TRANSLATORS: success message when user refreshes device checksums | |
1194 | msgid "Successfully updated device checksums" | |
1195 | msgstr "Eszköz ellenőrzőösszegek sikeresen frissítve" | |
1196 | ||
1197 | #. TRANSLATORS: success message -- where the user has uploaded | |
1198 | #. * success and/or failure reports to the remote server | |
1199 | #, c-format | |
1200 | msgid "Successfully uploaded %u report" | |
1201 | msgid_plural "Successfully uploaded %u reports" | |
1202 | msgstr[0] "%u jelentés sikeresen feltöltve" | |
1203 | msgstr[1] "%u jelentés sikeresen feltöltve" | |
1204 | ||
1205 | #. TRANSLATORS: success message when user verified device checksums | |
1206 | msgid "Successfully verified device checksums" | |
1207 | msgstr "Eszköz ellenőrzőösszegek sikeresen ellenőrizve" | |
1208 | ||
952 | 1209 | #. TRANSLATORS: one line summary of device |
953 | 1210 | msgid "Summary" |
954 | 1211 | msgstr "Összegzés" |
1212 | ||
1213 | #. TRANSLATORS: Is found in current metadata | |
1214 | msgid "Supported on remote server" | |
1215 | msgstr "Távoli kiszolgálón nem támogatott " | |
955 | 1216 | |
956 | 1217 | msgid "Target" |
957 | 1218 | msgstr "Cél" |
1004 | 1265 | msgid "Unset the debugging flag during update" |
1005 | 1266 | msgstr "A hibakeresési jelző kikapcsolása frissítéskor" |
1006 | 1267 | |
1268 | #. TRANSLATORS: error message | |
1269 | #, c-format | |
1270 | msgid "Unsupported daemon version %s, client version is %s" | |
1271 | msgstr "Nem támogatott démonverzió: %s, a kliensverzió %s" | |
1272 | ||
1273 | #. TRANSLATORS: Device is updatable in this or any other mode | |
1274 | msgid "Updatable" | |
1275 | msgstr "Frissíthet" | |
1276 | ||
1007 | 1277 | #. TRANSLATORS: error message from last update attempt |
1008 | 1278 | msgid "Update Error" |
1009 | 1279 | msgstr "Frissítési hiba" |
1029 | 1299 | msgid "Update now?" |
1030 | 1300 | msgstr "Frissíti most?" |
1031 | 1301 | |
1302 | #. TRANSLATORS: Update can only be done from offline mode | |
1303 | msgid "Update requires a reboot" | |
1304 | msgstr "A frissítés újraindítást igényel" | |
1305 | ||
1306 | #. TRANSLATORS: command description | |
1307 | msgid "Update the stored cryptographic hash with current ROM contents" | |
1308 | msgstr "A tárolt kriptográfiai hash frissítése a jelenlegi ROM tartalmával" | |
1309 | ||
1032 | 1310 | msgid "Update the stored device verification information" |
1033 | 1311 | msgstr "A tárolt eszközellenőrzési információk frissítése" |
1034 | 1312 | |
1052 | 1330 | msgid "Updating %s…" |
1053 | 1331 | msgstr "%s frissítése…" |
1054 | 1332 | |
1333 | #. TRANSLATORS: message letting the user know an upgrade is available | |
1334 | #. * %1 is the device name and %2 and %3 are version strings | |
1335 | #, c-format | |
1336 | msgid "Upgrade available for %s from %s to %s" | |
1337 | msgstr "%s frissítés érhető el, erről: %s, erre: %s" | |
1338 | ||
1055 | 1339 | #. TRANSLATORS: the server sent the user a small message |
1056 | 1340 | msgid "Upload message:" |
1057 | 1341 | msgstr "Feltöltési üzenet:" |
1058 | 1342 | |
1343 | msgid "Upload report just this one time, but prompt again for future updates" | |
1344 | msgid_plural "Upload reports just this one time, but prompt again for future updates" | |
1345 | msgstr[0] "Most töltse fel a jelentést, de kérdezzen rá a jövőben" | |
1346 | msgstr[1] "Most töltse fel a jelentéseket, de kérdezzen rá a jövőben" | |
1347 | ||
1059 | 1348 | #. TRANSLATORS: ask the user to upload |
1060 | 1349 | msgid "Upload report now?" |
1061 | 1350 | msgstr "Feltölti most a jelentést?" |
1062 | 1351 | |
1352 | msgid "Upload report this time and automatically upload reports after completing future updates" | |
1353 | msgid_plural "Upload reports this time and automatically upload reports after completing future updates" | |
1354 | msgstr[0] "Most töltse fel a jelentést, és automatikusan tegye meg ezt a jövőben" | |
1355 | msgstr[1] "Most töltse fel a jelentéseket, és automatikusan tegye meg ezt a jövőben" | |
1356 | ||
1063 | 1357 | #. TRANSLATORS: explain why we want to upload |
1064 | 1358 | msgid "Uploading firmware reports helps hardware vendors to quickly identify failing and successful updates on real devices." |
1065 | 1359 | 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." |
1066 | 1360 | |
1361 | #. TRANSLATORS: command line option | |
1362 | msgid "Use quirk flags when installing firmware" | |
1363 | msgstr "Kerülőmegoldás jelzők használata a firmware telepítésekor" | |
1364 | ||
1365 | #. TRANSLATORS: User has been notified | |
1366 | msgid "User has been notified" | |
1367 | msgstr "A felhasználó értesítve" | |
1368 | ||
1067 | 1369 | #. TRANSLATORS: remote filename base |
1068 | 1370 | msgid "Username" |
1069 | 1371 | msgstr "Felhasználónév" |
1080 | 1382 | msgid "Verifying…" |
1081 | 1383 | msgstr "Ellenőrzés…" |
1082 | 1384 | |
1385 | #. TRANSLATORS: try to help | |
1386 | msgid "WARNING: Ignoring SSL strict checks, to do this automatically in the future export DISABLE_SSL_STRICT in your environment" | |
1387 | msgstr "FIGYELMEZTETÉS: A szigorú SSL ellenőrzések mellőzése, ahhoz hogy ezt automatikusan megtegye a jövőben, exportálja a DISABLE_SSL_STRICT változót a környezetében" | |
1388 | ||
1083 | 1389 | #. TRANSLATORS: waiting for device to do something |
1084 | 1390 | msgid "Waiting…" |
1085 | 1391 | msgstr "Várakozás…" |
80 | 80 | msgid "%s Update" |
81 | 81 | msgstr "Aggiornamento di %s" |
82 | 82 | |
83 | #. TRANSLATORS: warn the user before updating, %1 is a device name | |
84 | #, c-format | |
85 | msgid "%s and all connected devices may not be usable while updating." | |
86 | msgstr "%s e tutti i dispositivi collegati potrebbero non essere utilizzabili durante l'aggiornamento." | |
87 | ||
88 | #. TRANSLATORS: warn the user before updating, %1 is a device name | |
89 | #, c-format | |
90 | msgid "%s must remain connected for the duration of the update to avoid damage." | |
91 | msgstr "%s deve rimanere collegato durante l'aggiornamento per evitare possibili danni." | |
92 | ||
93 | #. TRANSLATORS: warn the user before updating, %1 is a machine name | |
94 | #, c-format | |
95 | msgid "%s must remain plugged into a power source for the duration of the update to avoid damage." | |
96 | msgstr "%s deve rimanere collegato alla rete elettrica durante l'aggiornamento per evitare possibili danni." | |
97 | ||
83 | 98 | #. TRANSLATORS: duration in days! |
84 | 99 | #, c-format |
85 | 100 | msgid "%u day" |
259 | 274 | msgid "Changed" |
260 | 275 | msgstr "Modificato" |
261 | 276 | |
277 | #. TRANSLATORS: command description | |
278 | msgid "Checks cryptographic hash matches firmware" | |
279 | msgstr "Verifica che l'hash crittografico corrisponda col firmware" | |
280 | ||
262 | 281 | #. TRANSLATORS: remote checksum |
263 | 282 | msgid "Checksum" |
264 | 283 | msgstr "Codice di controllo" |
268 | 287 | msgstr "Scegliere un dispositivo:" |
269 | 288 | |
270 | 289 | #. TRANSLATORS: get interactive prompt |
290 | msgid "Choose a firmware type:" | |
291 | msgstr "Scegliere un tipo di firmware:" | |
292 | ||
293 | #. TRANSLATORS: get interactive prompt | |
271 | 294 | msgid "Choose a release:" |
272 | 295 | msgstr "Scegliere un rilascio:" |
273 | 296 | |
283 | 306 | msgid "Command not found" |
284 | 307 | msgstr "Comando non trovato" |
285 | 308 | |
309 | #. TRANSLATORS: prompt to apply the update | |
310 | msgid "Continue with update?" | |
311 | msgstr "Continuare con l'aggiornamento?" | |
312 | ||
286 | 313 | #. TRANSLATORS: command description |
287 | 314 | msgid "Convert firmware to DFU format" |
288 | 315 | msgstr "Converte il firmware nel formato DFU" |
316 | ||
317 | #. TRANSLATORS: Device supports some form of checksum verification | |
318 | msgid "Cryptographic hash verification is available" | |
319 | msgstr "È disponibile la verifica dell'hash crittografico" | |
289 | 320 | |
290 | 321 | #. TRANSLATORS: version number of current firmware |
291 | 322 | msgid "Current version" |
315 | 346 | msgid "Details" |
316 | 347 | msgstr "Dettagli" |
317 | 348 | |
349 | #. TRANSLATORS: description of device ability | |
350 | msgid "Device Flags" | |
351 | msgstr "Flag dispositivo" | |
352 | ||
318 | 353 | #. TRANSLATORS: ID for hardware, typically a SHA1 sum |
319 | 354 | msgid "Device ID" |
320 | 355 | msgstr "ID dispositivo" |
323 | 358 | msgid "Device added:" |
324 | 359 | msgstr "Dispositivo aggiunto:" |
325 | 360 | |
361 | #. TRANSLATORS: Device supports a safety mechanism for flashing | |
362 | msgid "Device can recover flash failures" | |
363 | msgstr "Il dispositivo è in grado di correggere problemi di flash" | |
364 | ||
326 | 365 | #. TRANSLATORS: this is when a device has been updated |
327 | 366 | msgid "Device changed:" |
328 | 367 | msgstr "Dispositivo modificato:" |
329 | 368 | |
369 | #. TRANSLATORS: Is locked and can be unlocked | |
370 | msgid "Device is locked" | |
371 | msgstr "Il dispositivo è bloccato" | |
372 | ||
373 | #. TRANSLATORS: Device remains usable during update | |
374 | msgid "Device is usable for the duration of the update" | |
375 | msgstr "Il dispositivo è utilizzabile per la durata dell'aggiornamento" | |
376 | ||
330 | 377 | #. TRANSLATORS: this is when a device is hotplugged |
331 | 378 | msgid "Device removed:" |
332 | 379 | msgstr "Dispositivo rimosso:" |
333 | 380 | |
381 | #. TRANSLATORS: Device supports a safety mechanism for flashing | |
382 | msgid "Device stages updates" | |
383 | msgstr "Il dispositivo applica gli aggiornamenti a fasi" | |
384 | ||
385 | #. TRANSLATORS: Device update needs to be separately activated | |
386 | msgid "Device update needs activation" | |
387 | msgstr "L'aggiornamento del dispositivo richiede l'attivazione" | |
388 | ||
389 | #. TRANSLATORS: Device will not return after update completes | |
390 | msgid "Device will not re-appear after update completes" | |
391 | msgstr "Il dispositivo non comparirà nuovamente dopo l'aggiornamento" | |
392 | ||
334 | 393 | #. TRANSLATORS: a list of successful updates |
335 | 394 | msgid "Devices that have been updated successfully:" |
336 | 395 | msgstr "Dispositivi aggiornati con successo:" |
369 | 428 | #. TRANSLATORS: turn on all debugging |
370 | 429 | msgid "Do not include timestamp prefix" |
371 | 430 | msgstr "Non include il prefisso della marcatura temporale" |
431 | ||
432 | #. TRANSLATORS: command line option | |
433 | msgid "Do not perform device safety checks" | |
434 | msgstr "Non esegue i controlli di sicurezza del dispositivo" | |
372 | 435 | |
373 | 436 | msgid "Do not upload report at this time, but prompt again for future updates" |
374 | 437 | msgid_plural "Do not upload reports at this time, but prompt again for future updates" |
616 | 679 | msgid "Gets the results from the last update" |
617 | 680 | msgstr "Ottiene i risultati dell'ultimo aggiornamento" |
618 | 681 | |
682 | #. TRANSLATORS: The hardware is waiting to be replugged | |
683 | msgid "Hardware is waiting to be replugged" | |
684 | msgstr "L'hardware è in attesa di essere ricollegato" | |
685 | ||
619 | 686 | #. TRANSLATORS: daemon is inactive |
620 | 687 | msgid "Idle…" |
621 | 688 | msgstr "Inattivo…" |
624 | 691 | msgid "Ignore SSL strict checks when downloading files" |
625 | 692 | msgstr "Ignora controlli SSL nello scaricare i file" |
626 | 693 | |
694 | #. TRANSLATORS: Ignore validation safety checks when flashing this device | |
695 | msgid "Ignore validation safety checks" | |
696 | msgstr "Ignora i controlli di sicurezza di validazione" | |
697 | ||
627 | 698 | #. TRANSLATORS: length of time the update takes to apply |
628 | 699 | msgid "Install Duration" |
629 | 700 | msgstr "Tempo di installazione" |
645 | 716 | msgid "Install signed system firmware" |
646 | 717 | msgstr "Installa firmware firmato di sistema" |
647 | 718 | |
719 | #. TRANSLATORS: Install composite firmware on the parent before the child | |
720 | msgid "Install to parent device first" | |
721 | msgstr "Installare sul dispositivo genitore prima" | |
722 | ||
648 | 723 | msgid "Install unsigned device firmware" |
649 | 724 | msgstr "Installa firmware non firmato del dispositivo" |
650 | 725 | |
663 | 738 | #, c-format |
664 | 739 | msgid "Installing on %s…" |
665 | 740 | msgstr "Installazione su %s…" |
741 | ||
742 | #. TRANSLATORS: Device cannot be removed easily | |
743 | msgid "Internal device" | |
744 | msgstr "Dispositivo interno" | |
745 | ||
746 | #. TRANSLATORS: Is currently in bootloader mode | |
747 | msgid "Is in bootloader mode" | |
748 | msgstr "È in modalità bootloader" | |
666 | 749 | |
667 | 750 | #. TRANSLATORS: issue fixed with the release, e.g. CVE |
668 | 751 | msgid "Issue" |
695 | 778 | msgid "List supported firmware updates" |
696 | 779 | msgstr "Elenca gli aggiornamenti firmware supportati" |
697 | 780 | |
781 | #. TRANSLATORS: command description | |
782 | msgid "List the available firmware types" | |
783 | msgstr "Elenca tutti i tipi di firmware disponibili" | |
784 | ||
698 | 785 | #. TRANSLATORS: parsing the firmware information |
699 | 786 | msgid "Loading…" |
700 | 787 | msgstr "Caricamento…" |
746 | 833 | msgid "Monitor the daemon for events" |
747 | 834 | msgstr "Controlla il demone per gli eventi" |
748 | 835 | |
836 | #. TRANSLATORS: Requires a reboot to apply firmware or to reload hardware | |
837 | msgid "Needs a reboot after installation" | |
838 | msgstr "Necessita un riavvio dopo l'installazione" | |
839 | ||
840 | #. TRANSLATORS: Requires system shutdown to apply firmware | |
841 | msgid "Needs shutdown after installation" | |
842 | msgstr "Necessita l'arresto dopo l'installazione" | |
843 | ||
749 | 844 | #. TRANSLATORS: version number of new firmware |
750 | 845 | msgid "New version" |
751 | 846 | msgstr "Nuova versione" |
753 | 848 | msgid "No action specified!" |
754 | 849 | msgstr "Nessuna azione specificata." |
755 | 850 | |
851 | #. TRANSLATORS: nothing found | |
852 | msgid "No firmware IDs found" | |
853 | msgstr "Nessun ID firmware trovato" | |
854 | ||
756 | 855 | #. TRANSLATORS: nothing attached that can be upgraded |
757 | 856 | msgid "No hardware detected with firmware update capability" |
758 | 857 | msgstr "Non è stato rilevato nessun hardware con capacità di aggiornamento del firmware" |
789 | 888 | msgid "Override warnings and force the action" |
790 | 889 | msgstr "Scavalca gli avvisi e forza l'azione" |
791 | 890 | |
891 | #. TRANSLATORS: command description | |
892 | msgid "Parse and show details about a firmware file" | |
893 | msgstr "Legge e mostra i dettagli riguardo a un file firmware" | |
894 | ||
792 | 895 | #. TRANSLATORS: remote filename base |
793 | 896 | msgid "Password" |
794 | 897 | msgstr "Password" |
831 | 934 | msgstr "Interroga il supporto per gli aggiornamenti firmware" |
832 | 935 | |
833 | 936 | #. TRANSLATORS: command description |
937 | msgid "Read a firmware blob from a device" | |
938 | msgstr "Legge un blob firmware da un dispositivo" | |
939 | ||
940 | #. TRANSLATORS: command description | |
834 | 941 | msgid "Read firmware from device into a file" |
835 | 942 | msgstr "Legge il firmware dal dispositivo in un file" |
836 | 943 | |
837 | 944 | #. TRANSLATORS: command description |
838 | 945 | msgid "Read firmware from one partition into a file" |
839 | 946 | msgstr "Legge il firmware da una partizione in un file" |
947 | ||
948 | #. TRANSLATORS: %1 is a device name | |
949 | #, c-format | |
950 | msgid "Reading from %s…" | |
951 | msgstr "Lettura da %s…" | |
840 | 952 | |
841 | 953 | #. TRANSLATORS: reading from the flash chips |
842 | 954 | msgid "Reading…" |
878 | 990 | msgid "Report URI" |
879 | 991 | msgstr "URI del rapporto" |
880 | 992 | |
993 | #. TRANSLATORS: Has been reported to a metadata server | |
994 | msgid "Reported to remote server" | |
995 | msgstr "Segnalato al server remoto" | |
996 | ||
997 | #. TRANSLATORS: Must be plugged in to an outlet | |
998 | msgid "Requires AC power" | |
999 | msgstr "Richiede corrente elettrica" | |
1000 | ||
1001 | #. TRANSLATORS: Requires a bootloader mode to be manually enabled by the user | |
1002 | msgid "Requires a bootloader" | |
1003 | msgstr "Richiede un bootloader" | |
1004 | ||
881 | 1005 | #. TRANSLATORS: metadata is downloaded from the Internet |
882 | 1006 | msgid "Requires internet connection" |
883 | 1007 | msgstr "Richiede una connessione a Internet" |
917 | 1041 | #. TRANSLATORS: scheduing an update to be done on the next boot |
918 | 1042 | msgid "Scheduling…" |
919 | 1043 | msgstr "Pianificazione…" |
1044 | ||
1045 | #. TRANSLATORS: Device has been chosen by the daemon for the user | |
1046 | msgid "Selected device" | |
1047 | msgstr "Dispositivo selezionato" | |
920 | 1048 | |
921 | 1049 | #. TRANSLATORS: serial number of hardware |
922 | 1050 | msgid "Serial Number" |
1052 | 1180 | msgid "Successfully modified configuration value" |
1053 | 1181 | msgstr "Valore della configurazione modificato con successo" |
1054 | 1182 | |
1183 | #. TRANSLATORS: success message for a per-remote setting change | |
1184 | msgid "Successfully modified remote" | |
1185 | msgstr "Remoto modificato con successo" | |
1186 | ||
1055 | 1187 | #. TRANSLATORS: success message -- the user can do this by-hand too |
1056 | 1188 | msgid "Successfully refreshed metadata manually" |
1057 | 1189 | msgstr "Metadati aggiornati manualmente con successo" |
1190 | ||
1191 | #. TRANSLATORS: success message when user refreshes device checksums | |
1192 | msgid "Successfully updated device checksums" | |
1193 | msgstr "Codici di controllo del dispositivo aggiornati con successo" | |
1058 | 1194 | |
1059 | 1195 | #. TRANSLATORS: success message -- where the user has uploaded |
1060 | 1196 | #. * success and/or failure reports to the remote server |
1064 | 1200 | msgstr[0] "%u rapporto caricato con successo" |
1065 | 1201 | msgstr[1] "%u rapporti caricati con successo" |
1066 | 1202 | |
1203 | #. TRANSLATORS: success message when user verified device checksums | |
1204 | msgid "Successfully verified device checksums" | |
1205 | msgstr "Codici di controllo del dispositivo verificati con successo" | |
1206 | ||
1067 | 1207 | #. TRANSLATORS: one line summary of device |
1068 | 1208 | msgid "Summary" |
1069 | 1209 | msgstr "Riepilogo" |
1210 | ||
1211 | #. TRANSLATORS: Is found in current metadata | |
1212 | msgid "Supported on remote server" | |
1213 | msgstr "Supportato sul server remoto" | |
1070 | 1214 | |
1071 | 1215 | msgid "Target" |
1072 | 1216 | msgstr "Obiettivo" |
1124 | 1268 | msgid "Unsupported daemon version %s, client version is %s" |
1125 | 1269 | msgstr "Demone versione %s non supportato, la versione del client è %s" |
1126 | 1270 | |
1271 | #. TRANSLATORS: Device is updatable in this or any other mode | |
1272 | msgid "Updatable" | |
1273 | msgstr "Aggiornabile" | |
1274 | ||
1127 | 1275 | #. TRANSLATORS: error message from last update attempt |
1128 | 1276 | msgid "Update Error" |
1129 | 1277 | msgstr "Errore aggiornamento" |
1149 | 1297 | msgid "Update now?" |
1150 | 1298 | msgstr "Aggiornare ora?" |
1151 | 1299 | |
1300 | #. TRANSLATORS: Update can only be done from offline mode | |
1301 | msgid "Update requires a reboot" | |
1302 | msgstr "L'aggiornamento richiede il riavvio" | |
1303 | ||
1304 | #. TRANSLATORS: command description | |
1305 | msgid "Update the stored cryptographic hash with current ROM contents" | |
1306 | msgstr "Aggiorna l'hash crittografico archiviato con il contenuto della ROM" | |
1307 | ||
1152 | 1308 | msgid "Update the stored device verification information" |
1153 | 1309 | msgstr "Aggiornamento delle informazioni di verifica del dispositivo salvate" |
1154 | 1310 | |
1171 | 1327 | #, c-format |
1172 | 1328 | msgid "Updating %s…" |
1173 | 1329 | msgstr "Aggiornamento di %s…" |
1330 | ||
1331 | #. TRANSLATORS: message letting the user know an upgrade is available | |
1332 | #. * %1 is the device name and %2 and %3 are version strings | |
1333 | #, c-format | |
1334 | msgid "Upgrade available for %s from %s to %s" | |
1335 | msgstr "Aggiornamento disponibile per %s da %s a %s" | |
1174 | 1336 | |
1175 | 1337 | #. TRANSLATORS: the server sent the user a small message |
1176 | 1338 | msgid "Upload message:" |
1198 | 1360 | msgid "Use quirk flags when installing firmware" |
1199 | 1361 | msgstr "Usa flag quirk nell'installare il firmware" |
1200 | 1362 | |
1363 | #. TRANSLATORS: User has been notified | |
1364 | msgid "User has been notified" | |
1365 | msgstr "Notifica inviata all'utente" | |
1366 | ||
1201 | 1367 | #. TRANSLATORS: remote filename base |
1202 | 1368 | msgid "Username" |
1203 | 1369 | msgstr "Nome utente" |
81 | 81 | msgid "%s Update" |
82 | 82 | msgstr "Aktualizacja urządzenia %s" |
83 | 83 | |
84 | #. TRANSLATORS: warn the user before updating, %1 is a device name | |
85 | #, c-format | |
86 | msgid "%s and all connected devices may not be usable while updating." | |
87 | msgstr "%s i wszystkie podłączone urządzenia nie będą mogły być używane podczas aktualizacji." | |
88 | ||
89 | #. TRANSLATORS: warn the user before updating, %1 is a device name | |
90 | #, c-format | |
91 | msgid "%s must remain connected for the duration of the update to avoid damage." | |
92 | msgstr "Urządzenie %s musi być podłączone podczas trwania aktualizacji, aby uniknąć uszkodzenia." | |
93 | ||
94 | #. TRANSLATORS: warn the user before updating, %1 is a machine name | |
95 | #, c-format | |
96 | msgid "%s must remain plugged into a power source for the duration of the update to avoid damage." | |
97 | msgstr "Urządzenie %s musi być podłączone do prądu podczas trwania aktualizacji, aby uniknąć uszkodzenia." | |
98 | ||
84 | 99 | #. TRANSLATORS: duration in days! |
85 | 100 | #, c-format |
86 | 101 | msgid "%u day" |
272 | 287 | msgid "Changed" |
273 | 288 | msgstr "Zmieniono" |
274 | 289 | |
290 | #. TRANSLATORS: command description | |
291 | msgid "Checks cryptographic hash matches firmware" | |
292 | msgstr "Sprawdza, czy kryptograficzna suma kontrolna pasuje do oprogramowania sprzętowego" | |
293 | ||
275 | 294 | #. TRANSLATORS: remote checksum |
276 | 295 | msgid "Checksum" |
277 | 296 | msgstr "Suma kontrolna" |
281 | 300 | msgstr "Wybór urządzenia:" |
282 | 301 | |
283 | 302 | #. TRANSLATORS: get interactive prompt |
303 | msgid "Choose a firmware type:" | |
304 | msgstr "Wybór typu oprogramowania sprzętowego:" | |
305 | ||
306 | #. TRANSLATORS: get interactive prompt | |
284 | 307 | msgid "Choose a release:" |
285 | 308 | msgstr "Wybór wydania:" |
286 | 309 | |
296 | 319 | msgid "Command not found" |
297 | 320 | msgstr "Nie odnaleziono polecenia" |
298 | 321 | |
322 | #. TRANSLATORS: prompt to apply the update | |
323 | msgid "Continue with update?" | |
324 | msgstr "Kontynuować aktualizację?" | |
325 | ||
299 | 326 | #. TRANSLATORS: command description |
300 | 327 | msgid "Convert firmware to DFU format" |
301 | 328 | msgstr "Konwertuje oprogramowanie sprzętowe do formatu DFU" |
329 | ||
330 | #. TRANSLATORS: Device supports some form of checksum verification | |
331 | msgid "Cryptographic hash verification is available" | |
332 | msgstr "Dostępne jest sprawdzenie poprawności kryptograficznej sumy kontrolnej" | |
302 | 333 | |
303 | 334 | #. TRANSLATORS: version number of current firmware |
304 | 335 | msgid "Current version" |
328 | 359 | msgid "Details" |
329 | 360 | msgstr "Informacje" |
330 | 361 | |
362 | #. TRANSLATORS: description of device ability | |
363 | msgid "Device Flags" | |
364 | msgstr "Flagi urządzenia" | |
365 | ||
331 | 366 | #. TRANSLATORS: ID for hardware, typically a SHA1 sum |
332 | 367 | msgid "Device ID" |
333 | 368 | msgstr "Identyfikator urządzenia" |
336 | 371 | msgid "Device added:" |
337 | 372 | msgstr "Dodano urządzenie:" |
338 | 373 | |
374 | #. TRANSLATORS: Device supports a safety mechanism for flashing | |
375 | msgid "Device can recover flash failures" | |
376 | msgstr "Urządzenie może przywrócić się po niepowodzeniu wgrywania" | |
377 | ||
339 | 378 | #. TRANSLATORS: this is when a device has been updated |
340 | 379 | msgid "Device changed:" |
341 | 380 | msgstr "Zmieniono urządzenie:" |
342 | 381 | |
382 | #. TRANSLATORS: Is locked and can be unlocked | |
383 | msgid "Device is locked" | |
384 | msgstr "Urządzenie jest zablokowane" | |
385 | ||
386 | #. TRANSLATORS: Device remains usable during update | |
387 | msgid "Device is usable for the duration of the update" | |
388 | msgstr "Urządzenie może być używane podczas trwania aktualizacji" | |
389 | ||
343 | 390 | #. TRANSLATORS: this is when a device is hotplugged |
344 | 391 | msgid "Device removed:" |
345 | 392 | msgstr "Usunięto urządzenie:" |
346 | 393 | |
394 | #. TRANSLATORS: Device supports a safety mechanism for flashing | |
395 | msgid "Device stages updates" | |
396 | msgstr "Urządzenie przygotowuje aktualizacje" | |
397 | ||
398 | #. TRANSLATORS: Device update needs to be separately activated | |
399 | msgid "Device update needs activation" | |
400 | msgstr "Aktualizacja urządzenia wymaga aktywacji" | |
401 | ||
402 | #. TRANSLATORS: Device will not return after update completes | |
403 | msgid "Device will not re-appear after update completes" | |
404 | msgstr "Urządzenie nie pojawi się z powrotem po ukończeniu aktualizacji" | |
405 | ||
347 | 406 | #. TRANSLATORS: a list of successful updates |
348 | 407 | msgid "Devices that have been updated successfully:" |
349 | 408 | msgstr "Pomyślnie zaktualizowane urządzenia:" |
382 | 441 | #. TRANSLATORS: turn on all debugging |
383 | 442 | msgid "Do not include timestamp prefix" |
384 | 443 | msgstr "Bez dołączania przedrostka czasu" |
444 | ||
445 | #. TRANSLATORS: command line option | |
446 | msgid "Do not perform device safety checks" | |
447 | msgstr "Bez wykonywania testów bezpieczeństwa urządzenia" | |
385 | 448 | |
386 | 449 | msgid "Do not upload report at this time, but prompt again for future updates" |
387 | 450 | msgid_plural "Do not upload reports at this time, but prompt again for future updates" |
637 | 700 | msgid "Gets the results from the last update" |
638 | 701 | msgstr "Uzyskuje wyniki z ostatniej aktualizacji" |
639 | 702 | |
703 | #. TRANSLATORS: The hardware is waiting to be replugged | |
704 | msgid "Hardware is waiting to be replugged" | |
705 | msgstr "Sprzęt czeka na ponowne podłączenie" | |
706 | ||
640 | 707 | #. TRANSLATORS: daemon is inactive |
641 | 708 | msgid "Idle…" |
642 | 709 | msgstr "Bezczynne…" |
645 | 712 | msgid "Ignore SSL strict checks when downloading files" |
646 | 713 | msgstr "Ignoruje ścisłe testy SSL podczas pobierania plików" |
647 | 714 | |
715 | #. TRANSLATORS: Ignore validation safety checks when flashing this device | |
716 | msgid "Ignore validation safety checks" | |
717 | msgstr "Ignoruje testy bezpieczeństwa i sprawdzanie poprawności" | |
718 | ||
648 | 719 | #. TRANSLATORS: length of time the update takes to apply |
649 | 720 | msgid "Install Duration" |
650 | 721 | msgstr "Czas trwania instalacji" |
666 | 737 | msgid "Install signed system firmware" |
667 | 738 | msgstr "Instalacja podpisanego oprogramowania sprzętowego komputera" |
668 | 739 | |
740 | #. TRANSLATORS: Install composite firmware on the parent before the child | |
741 | msgid "Install to parent device first" | |
742 | msgstr "Instaluje najpierw na urządzeniu nadrzędnym" | |
743 | ||
669 | 744 | msgid "Install unsigned device firmware" |
670 | 745 | msgstr "Instalacja niepodpisanego oprogramowania sprzętowego urządzenia" |
671 | 746 | |
684 | 759 | #, c-format |
685 | 760 | msgid "Installing on %s…" |
686 | 761 | msgstr "Instalowanie na urządzeniu %s…" |
762 | ||
763 | #. TRANSLATORS: Device cannot be removed easily | |
764 | msgid "Internal device" | |
765 | msgstr "Wewnętrzne urządzenie" | |
766 | ||
767 | #. TRANSLATORS: Is currently in bootloader mode | |
768 | msgid "Is in bootloader mode" | |
769 | msgstr "Jest w trybie programu startowego" | |
687 | 770 | |
688 | 771 | #. TRANSLATORS: issue fixed with the release, e.g. CVE |
689 | 772 | msgid "Issue" |
718 | 801 | msgid "List supported firmware updates" |
719 | 802 | msgstr "Wyświetla listę obsługiwanych aktualizacji oprogramowania sprzętowego" |
720 | 803 | |
804 | #. TRANSLATORS: command description | |
805 | msgid "List the available firmware types" | |
806 | msgstr "Wyświetla listę dostępnych typów oprogramowania sprzętowego" | |
807 | ||
721 | 808 | #. TRANSLATORS: parsing the firmware information |
722 | 809 | msgid "Loading…" |
723 | 810 | msgstr "Wczytywanie…" |
769 | 856 | msgid "Monitor the daemon for events" |
770 | 857 | msgstr "Monitoruje zdarzenia usługi" |
771 | 858 | |
859 | #. TRANSLATORS: Requires a reboot to apply firmware or to reload hardware | |
860 | msgid "Needs a reboot after installation" | |
861 | msgstr "Wymaga ponownego uruchomienia po instalacji" | |
862 | ||
863 | #. TRANSLATORS: Requires system shutdown to apply firmware | |
864 | msgid "Needs shutdown after installation" | |
865 | msgstr "Wymaga wyłączenia komputera po instalacji" | |
866 | ||
772 | 867 | #. TRANSLATORS: version number of new firmware |
773 | 868 | msgid "New version" |
774 | 869 | msgstr "Nowa wersja" |
776 | 871 | msgid "No action specified!" |
777 | 872 | msgstr "Nie podano działania." |
778 | 873 | |
874 | #. TRANSLATORS: nothing found | |
875 | msgid "No firmware IDs found" | |
876 | msgstr "Nie odnaleziono identyfikatorów oprogramowania sprzętowego" | |
877 | ||
779 | 878 | #. TRANSLATORS: nothing attached that can be upgraded |
780 | 879 | msgid "No hardware detected with firmware update capability" |
781 | 880 | msgstr "Nie wykryto sprzętu z możliwością aktualizacji jego oprogramowania" |
812 | 911 | msgid "Override warnings and force the action" |
813 | 912 | msgstr "Zastępuje ostrzeżenia i wymusza działanie" |
814 | 913 | |
914 | #. TRANSLATORS: command description | |
915 | msgid "Parse and show details about a firmware file" | |
916 | msgstr "Przetwarza i wyświetla informacje o pliku oprogramowania sprzętowego" | |
917 | ||
815 | 918 | #. TRANSLATORS: remote filename base |
816 | 919 | msgid "Password" |
817 | 920 | msgstr "Hasło" |
854 | 957 | msgstr "Odpytuje obsługę aktualizacji oprogramowania sprzętowego" |
855 | 958 | |
856 | 959 | #. TRANSLATORS: command description |
960 | msgid "Read a firmware blob from a device" | |
961 | msgstr "Odczytuje zamknięte oprogramowanie sprzętowe z urządzenia" | |
962 | ||
963 | #. TRANSLATORS: command description | |
857 | 964 | msgid "Read firmware from device into a file" |
858 | 965 | msgstr "Odczytuje oprogramowanie sprzętowe z urządzenia do pliku" |
859 | 966 | |
860 | 967 | #. TRANSLATORS: command description |
861 | 968 | msgid "Read firmware from one partition into a file" |
862 | 969 | msgstr "Odczytuje oprogramowanie sprzętowe z jednej partycji do pliku" |
970 | ||
971 | #. TRANSLATORS: %1 is a device name | |
972 | #, c-format | |
973 | msgid "Reading from %s…" | |
974 | msgstr "Odczytywanie z urządzenia %s…" | |
863 | 975 | |
864 | 976 | #. TRANSLATORS: reading from the flash chips |
865 | 977 | msgid "Reading…" |
901 | 1013 | msgid "Report URI" |
902 | 1014 | msgstr "Adres URI zgłoszenia" |
903 | 1015 | |
1016 | #. TRANSLATORS: Has been reported to a metadata server | |
1017 | msgid "Reported to remote server" | |
1018 | msgstr "Zgłoszone do zdalnego serwera" | |
1019 | ||
1020 | #. TRANSLATORS: Must be plugged in to an outlet | |
1021 | msgid "Requires AC power" | |
1022 | msgstr "Wymaga zasilania z gniazdka" | |
1023 | ||
1024 | #. TRANSLATORS: Requires a bootloader mode to be manually enabled by the user | |
1025 | msgid "Requires a bootloader" | |
1026 | msgstr "Wymaga programu startowego" | |
1027 | ||
904 | 1028 | #. TRANSLATORS: metadata is downloaded from the Internet |
905 | 1029 | msgid "Requires internet connection" |
906 | 1030 | msgstr "Wymaga połączenia z Internetem" |
940 | 1064 | #. TRANSLATORS: scheduing an update to be done on the next boot |
941 | 1065 | msgid "Scheduling…" |
942 | 1066 | msgstr "Planowanie…" |
1067 | ||
1068 | #. TRANSLATORS: Device has been chosen by the daemon for the user | |
1069 | msgid "Selected device" | |
1070 | msgstr "Wybrane urządzenie" | |
943 | 1071 | |
944 | 1072 | #. TRANSLATORS: serial number of hardware |
945 | 1073 | msgid "Serial Number" |
1075 | 1203 | msgid "Successfully modified configuration value" |
1076 | 1204 | msgstr "Pomyślnie zmodyfikowano wartość konfiguracji" |
1077 | 1205 | |
1206 | #. TRANSLATORS: success message for a per-remote setting change | |
1207 | msgid "Successfully modified remote" | |
1208 | msgstr "Pomyślnie zmodyfikowano repozytorium" | |
1209 | ||
1078 | 1210 | #. TRANSLATORS: success message -- the user can do this by-hand too |
1079 | 1211 | msgid "Successfully refreshed metadata manually" |
1080 | 1212 | msgstr "Pomyślnie ręcznie odświeżono metadane" |
1213 | ||
1214 | #. TRANSLATORS: success message when user refreshes device checksums | |
1215 | msgid "Successfully updated device checksums" | |
1216 | msgstr "Pomyślnie zaktualizowano sumy kontrolne urządzenia" | |
1081 | 1217 | |
1082 | 1218 | #. TRANSLATORS: success message -- where the user has uploaded |
1083 | 1219 | #. * success and/or failure reports to the remote server |
1089 | 1225 | msgstr[2] "Pomyślnie wysłano %u zgłoszeń" |
1090 | 1226 | msgstr[3] "Pomyślnie wysłano %u zgłoszeń" |
1091 | 1227 | |
1228 | #. TRANSLATORS: success message when user verified device checksums | |
1229 | msgid "Successfully verified device checksums" | |
1230 | msgstr "Pomyślnie sprawdzono poprawność sum kontrolnych urządzenia" | |
1231 | ||
1092 | 1232 | #. TRANSLATORS: one line summary of device |
1093 | 1233 | msgid "Summary" |
1094 | 1234 | msgstr "Podsumowanie" |
1235 | ||
1236 | #. TRANSLATORS: Is found in current metadata | |
1237 | msgid "Supported on remote server" | |
1238 | msgstr "Obsługiwane na zdalnym serwerze" | |
1095 | 1239 | |
1096 | 1240 | msgid "Target" |
1097 | 1241 | msgstr "Cel" |
1149 | 1293 | msgid "Unsupported daemon version %s, client version is %s" |
1150 | 1294 | msgstr "Nieobsługiwana wersja usługi %s, wersja klienta to %s" |
1151 | 1295 | |
1296 | #. TRANSLATORS: Device is updatable in this or any other mode | |
1297 | msgid "Updatable" | |
1298 | msgstr "Można aktualizować" | |
1299 | ||
1152 | 1300 | #. TRANSLATORS: error message from last update attempt |
1153 | 1301 | msgid "Update Error" |
1154 | 1302 | msgstr "Błąd aktualizacji" |
1174 | 1322 | msgid "Update now?" |
1175 | 1323 | msgstr "Zaktualizować teraz?" |
1176 | 1324 | |
1325 | #. TRANSLATORS: Update can only be done from offline mode | |
1326 | msgid "Update requires a reboot" | |
1327 | msgstr "Aktualizacja wymaga ponownego uruchomienia" | |
1328 | ||
1329 | #. TRANSLATORS: command description | |
1330 | msgid "Update the stored cryptographic hash with current ROM contents" | |
1331 | msgstr "Aktualizuje przechowywaną kryptograficzną sumę kontrolną bieżącą zawartością pamięci ROM" | |
1332 | ||
1177 | 1333 | msgid "Update the stored device verification information" |
1178 | 1334 | msgstr "Aktualizacja przechowywanych informacji o poprawności urządzenia" |
1179 | 1335 | |
1196 | 1352 | #, c-format |
1197 | 1353 | msgid "Updating %s…" |
1198 | 1354 | msgstr "Aktualizowanie urządzenia %s…" |
1355 | ||
1356 | #. TRANSLATORS: message letting the user know an upgrade is available | |
1357 | #. * %1 is the device name and %2 and %3 are version strings | |
1358 | #, c-format | |
1359 | msgid "Upgrade available for %s from %s to %s" | |
1360 | msgstr "Dostępna jest aktualizacja urządzenia %s z wersji %s do %s" | |
1199 | 1361 | |
1200 | 1362 | #. TRANSLATORS: the server sent the user a small message |
1201 | 1363 | msgid "Upload message:" |
1227 | 1389 | msgid "Use quirk flags when installing firmware" |
1228 | 1390 | msgstr "Używa flag poprawek podczas instalowania oprogramowania sprzętowego" |
1229 | 1391 | |
1392 | #. TRANSLATORS: User has been notified | |
1393 | msgid "User has been notified" | |
1394 | msgstr "Użytkownik został powiadomiony" | |
1395 | ||
1230 | 1396 | #. TRANSLATORS: remote filename base |
1231 | 1397 | msgid "Username" |
1232 | 1398 | msgstr "Nazwa użytkownika" |
83 | 83 | msgid "%s Update" |
84 | 84 | msgstr "Atualização de %s" |
85 | 85 | |
86 | #. TRANSLATORS: warn the user before updating, %1 is a device name | |
87 | #, c-format | |
88 | msgid "%s and all connected devices may not be usable while updating." | |
89 | msgstr "%s e todos os dispositivos conectados não podem ser usados durante a atualização." | |
90 | ||
91 | #. TRANSLATORS: warn the user before updating, %1 is a device name | |
92 | #, c-format | |
93 | msgid "%s must remain connected for the duration of the update to avoid damage." | |
94 | msgstr "%s deve permanecer conectado durante a atualização para evitar danos." | |
95 | ||
96 | #. TRANSLATORS: warn the user before updating, %1 is a machine name | |
97 | #, c-format | |
98 | msgid "%s must remain plugged into a power source for the duration of the update to avoid damage." | |
99 | msgstr "%s deve permanecer conectado a uma fonte de energia durante a atualização para evitar danos." | |
100 | ||
86 | 101 | #. TRANSLATORS: duration in days! |
87 | 102 | #, c-format |
88 | 103 | msgid "%u day" |
243 | 258 | |
244 | 259 | #. TRANSLATORS: firmware version of bootloader |
245 | 260 | msgid "Bootloader Version" |
246 | msgstr "Versão do gerenciador de inicialização" | |
261 | msgstr "Versão do gerenciador de boot" | |
247 | 262 | |
248 | 263 | #. TRANSLATORS: command description |
249 | 264 | msgid "Build firmware using a sandbox" |
262 | 277 | msgid "Changed" |
263 | 278 | msgstr "Alterado" |
264 | 279 | |
280 | #. TRANSLATORS: command description | |
281 | msgid "Checks cryptographic hash matches firmware" | |
282 | msgstr "Verifica se o hash criptográfico corresponde ao firmware" | |
283 | ||
265 | 284 | #. TRANSLATORS: remote checksum |
266 | 285 | msgid "Checksum" |
267 | 286 | msgstr "Soma de verificação" |
271 | 290 | msgstr "Escolha um dispositivo:" |
272 | 291 | |
273 | 292 | #. TRANSLATORS: get interactive prompt |
293 | msgid "Choose a firmware type:" | |
294 | msgstr "Escolha um tipo de firmware:" | |
295 | ||
296 | #. TRANSLATORS: get interactive prompt | |
274 | 297 | msgid "Choose a release:" |
275 | 298 | msgstr "Escolha um lançamento:" |
276 | 299 | |
286 | 309 | msgid "Command not found" |
287 | 310 | msgstr "Comando não encontrado" |
288 | 311 | |
312 | #. TRANSLATORS: prompt to apply the update | |
313 | msgid "Continue with update?" | |
314 | msgstr "Continuar com a atualização?" | |
315 | ||
289 | 316 | #. TRANSLATORS: command description |
290 | 317 | msgid "Convert firmware to DFU format" |
291 | 318 | msgstr "Converte um firmware para formato DFU" |
319 | ||
320 | #. TRANSLATORS: Device supports some form of checksum verification | |
321 | msgid "Cryptographic hash verification is available" | |
322 | msgstr "A verificação criptográfica de hash está disponível" | |
292 | 323 | |
293 | 324 | #. TRANSLATORS: version number of current firmware |
294 | 325 | msgid "Current version" |
318 | 349 | msgid "Details" |
319 | 350 | msgstr "Detalhes" |
320 | 351 | |
352 | #. TRANSLATORS: description of device ability | |
353 | msgid "Device Flags" | |
354 | msgstr "Opções do dispositivo" | |
355 | ||
321 | 356 | #. TRANSLATORS: ID for hardware, typically a SHA1 sum |
322 | 357 | msgid "Device ID" |
323 | 358 | msgstr "ID do dispositivo" |
326 | 361 | msgid "Device added:" |
327 | 362 | msgstr "Dispositivo adicionado:" |
328 | 363 | |
364 | #. TRANSLATORS: Device supports a safety mechanism for flashing | |
365 | msgid "Device can recover flash failures" | |
366 | msgstr "O dispositivo pode recuperar falhas de flashing" | |
367 | ||
329 | 368 | #. TRANSLATORS: this is when a device has been updated |
330 | 369 | msgid "Device changed:" |
331 | 370 | msgstr "Dispositivo modificado:" |
332 | 371 | |
372 | #. TRANSLATORS: Is locked and can be unlocked | |
373 | msgid "Device is locked" | |
374 | msgstr "O dispositivo está travado" | |
375 | ||
376 | #. TRANSLATORS: Device remains usable during update | |
377 | msgid "Device is usable for the duration of the update" | |
378 | msgstr "O dispositivo pode ser usado durante a atualização" | |
379 | ||
333 | 380 | #. TRANSLATORS: this is when a device is hotplugged |
334 | 381 | msgid "Device removed:" |
335 | 382 | msgstr "Dispositivo removido:" |
336 | 383 | |
384 | #. TRANSLATORS: Device supports a safety mechanism for flashing | |
385 | msgid "Device stages updates" | |
386 | msgstr "Atualizações de estágios do dispositivo" | |
387 | ||
388 | #. TRANSLATORS: Device update needs to be separately activated | |
389 | msgid "Device update needs activation" | |
390 | msgstr "A atualização do dispositivo precisa de ativação" | |
391 | ||
392 | #. TRANSLATORS: Device will not return after update completes | |
393 | msgid "Device will not re-appear after update completes" | |
394 | msgstr "O dispositivo não aparecerá novamente após a atualização ser concluída" | |
395 | ||
337 | 396 | #. TRANSLATORS: a list of successful updates |
338 | 397 | msgid "Devices that have been updated successfully:" |
339 | 398 | msgstr "Dispositivos que foram atualizados com sucesso:" |
372 | 431 | #. TRANSLATORS: turn on all debugging |
373 | 432 | msgid "Do not include timestamp prefix" |
374 | 433 | msgstr "Não inclui prefixo com marca de tempo" |
434 | ||
435 | #. TRANSLATORS: command line option | |
436 | msgid "Do not perform device safety checks" | |
437 | msgstr "Não realiza verificações de segurança de dispositivo" | |
375 | 438 | |
376 | 439 | msgid "Do not upload report at this time, but prompt again for future updates" |
377 | 440 | msgid_plural "Do not upload reports at this time, but prompt again for future updates" |
619 | 682 | msgid "Gets the results from the last update" |
620 | 683 | msgstr "Obtém os resultados da última atualização" |
621 | 684 | |
685 | #. TRANSLATORS: The hardware is waiting to be replugged | |
686 | msgid "Hardware is waiting to be replugged" | |
687 | msgstr "O hardware está aguardando para ser reconectado" | |
688 | ||
622 | 689 | #. TRANSLATORS: daemon is inactive |
623 | 690 | msgid "Idle…" |
624 | 691 | msgstr "Ocioso…" |
627 | 694 | msgid "Ignore SSL strict checks when downloading files" |
628 | 695 | msgstr "Ignora verificações estritas de SSL ao baixar arquivos" |
629 | 696 | |
697 | #. TRANSLATORS: Ignore validation safety checks when flashing this device | |
698 | msgid "Ignore validation safety checks" | |
699 | msgstr "Ignorar verificações de segurança de validação" | |
700 | ||
630 | 701 | #. TRANSLATORS: length of time the update takes to apply |
631 | 702 | msgid "Install Duration" |
632 | 703 | msgstr "Duração de instalação" |
648 | 719 | msgid "Install signed system firmware" |
649 | 720 | msgstr "Instalar firmware assinado no sistema" |
650 | 721 | |
722 | #. TRANSLATORS: Install composite firmware on the parent before the child | |
723 | msgid "Install to parent device first" | |
724 | msgstr "Instalar primeiro no dispositivo pai" | |
725 | ||
651 | 726 | msgid "Install unsigned device firmware" |
652 | 727 | msgstr "Instalar firmware não assinado no dispositivo" |
653 | 728 | |
666 | 741 | #, c-format |
667 | 742 | msgid "Installing on %s…" |
668 | 743 | msgstr "Instalando em %s…" |
744 | ||
745 | #. TRANSLATORS: Device cannot be removed easily | |
746 | msgid "Internal device" | |
747 | msgstr "Dispositivo interno" | |
748 | ||
749 | #. TRANSLATORS: Is currently in bootloader mode | |
750 | msgid "Is in bootloader mode" | |
751 | msgstr "Está no modo gerenciador de boot" | |
669 | 752 | |
670 | 753 | #. TRANSLATORS: issue fixed with the release, e.g. CVE |
671 | 754 | msgid "Issue" |
698 | 781 | msgid "List supported firmware updates" |
699 | 782 | msgstr "Lista atualizações de firmware suportadas" |
700 | 783 | |
784 | #. TRANSLATORS: command description | |
785 | msgid "List the available firmware types" | |
786 | msgstr "Lista os tipos de firmware disponível" | |
787 | ||
701 | 788 | #. TRANSLATORS: parsing the firmware information |
702 | 789 | msgid "Loading…" |
703 | 790 | msgstr "Carregando…" |
749 | 836 | msgid "Monitor the daemon for events" |
750 | 837 | msgstr "Monitora o daemon por eventos" |
751 | 838 | |
839 | #. TRANSLATORS: Requires a reboot to apply firmware or to reload hardware | |
840 | msgid "Needs a reboot after installation" | |
841 | msgstr "Precisa de uma reinicialização após a instalação" | |
842 | ||
843 | #. TRANSLATORS: Requires system shutdown to apply firmware | |
844 | msgid "Needs shutdown after installation" | |
845 | msgstr "Precisa de desligamento após a instalação" | |
846 | ||
752 | 847 | #. TRANSLATORS: version number of new firmware |
753 | 848 | msgid "New version" |
754 | 849 | msgstr "Nova versão" |
756 | 851 | msgid "No action specified!" |
757 | 852 | msgstr "Nenhuma ação especificada!" |
758 | 853 | |
854 | #. TRANSLATORS: nothing found | |
855 | msgid "No firmware IDs found" | |
856 | msgstr "Nenhum ID de firmware encontrado" | |
857 | ||
759 | 858 | #. TRANSLATORS: nothing attached that can be upgraded |
760 | 859 | msgid "No hardware detected with firmware update capability" |
761 | 860 | msgstr "Nenhum periférico com capacidade de atualização de firmware foi detectado" |
792 | 891 | msgid "Override warnings and force the action" |
793 | 892 | msgstr "Anula avisos e força a ação" |
794 | 893 | |
894 | #. TRANSLATORS: command description | |
895 | msgid "Parse and show details about a firmware file" | |
896 | msgstr "Analisa e mostra detalhes sobre um arquivo de firmware" | |
897 | ||
795 | 898 | #. TRANSLATORS: remote filename base |
796 | 899 | msgid "Password" |
797 | 900 | msgstr "Senha" |
834 | 937 | msgstr "Consulta por suporte a atualização de firmware" |
835 | 938 | |
836 | 939 | #. TRANSLATORS: command description |
940 | msgid "Read a firmware blob from a device" | |
941 | msgstr "Lê um blob de firmware de um dispositivo" | |
942 | ||
943 | #. TRANSLATORS: command description | |
837 | 944 | msgid "Read firmware from device into a file" |
838 | 945 | msgstr "Lê o firmware do dispositivo para um arquivo" |
839 | 946 | |
840 | 947 | #. TRANSLATORS: command description |
841 | 948 | msgid "Read firmware from one partition into a file" |
842 | 949 | msgstr "Lê o firmware de uma partição para um arquivo" |
950 | ||
951 | #. TRANSLATORS: %1 is a device name | |
952 | #, c-format | |
953 | msgid "Reading from %s…" | |
954 | msgstr "Lendo de %s…" | |
843 | 955 | |
844 | 956 | #. TRANSLATORS: reading from the flash chips |
845 | 957 | msgid "Reading…" |
881 | 993 | msgid "Report URI" |
882 | 994 | msgstr "URI do relatório" |
883 | 995 | |
996 | #. TRANSLATORS: Has been reported to a metadata server | |
997 | msgid "Reported to remote server" | |
998 | msgstr "Relatado ao servidor remoto" | |
999 | ||
1000 | #. TRANSLATORS: Must be plugged in to an outlet | |
1001 | msgid "Requires AC power" | |
1002 | msgstr "Requer alimentação CA" | |
1003 | ||
1004 | #. TRANSLATORS: Requires a bootloader mode to be manually enabled by the user | |
1005 | msgid "Requires a bootloader" | |
1006 | msgstr "Requer um gerenciador de boot" | |
1007 | ||
884 | 1008 | #. TRANSLATORS: metadata is downloaded from the Internet |
885 | 1009 | msgid "Requires internet connection" |
886 | 1010 | msgstr "Requer conexão com a Internet" |
920 | 1044 | #. TRANSLATORS: scheduing an update to be done on the next boot |
921 | 1045 | msgid "Scheduling…" |
922 | 1046 | msgstr "Agendando…" |
1047 | ||
1048 | #. TRANSLATORS: Device has been chosen by the daemon for the user | |
1049 | msgid "Selected device" | |
1050 | msgstr "Dispositivo selecionado" | |
923 | 1051 | |
924 | 1052 | #. TRANSLATORS: serial number of hardware |
925 | 1053 | msgid "Serial Number" |
1055 | 1183 | msgid "Successfully modified configuration value" |
1056 | 1184 | msgstr "Valor da configuração modificada com sucesso" |
1057 | 1185 | |
1186 | #. TRANSLATORS: success message for a per-remote setting change | |
1187 | msgid "Successfully modified remote" | |
1188 | msgstr "Remoto modificado com sucesso" | |
1189 | ||
1058 | 1190 | #. TRANSLATORS: success message -- the user can do this by-hand too |
1059 | 1191 | msgid "Successfully refreshed metadata manually" |
1060 | 1192 | msgstr "Metadados atualizados manualmente com sucesso" |
1193 | ||
1194 | #. TRANSLATORS: success message when user refreshes device checksums | |
1195 | msgid "Successfully updated device checksums" | |
1196 | msgstr "Somas de verificação de dispositivo atualizadas com sucesso" | |
1061 | 1197 | |
1062 | 1198 | #. TRANSLATORS: success message -- where the user has uploaded |
1063 | 1199 | #. * success and/or failure reports to the remote server |
1067 | 1203 | msgstr[0] "Enviado com sucesso %u relatório" |
1068 | 1204 | msgstr[1] "Enviados com sucesso %u relatórios" |
1069 | 1205 | |
1206 | #. TRANSLATORS: success message when user verified device checksums | |
1207 | msgid "Successfully verified device checksums" | |
1208 | msgstr "Somas de verificação de dispositivo verificadas com sucesso" | |
1209 | ||
1070 | 1210 | #. TRANSLATORS: one line summary of device |
1071 | 1211 | msgid "Summary" |
1072 | 1212 | msgstr "Resumo" |
1213 | ||
1214 | #. TRANSLATORS: Is found in current metadata | |
1215 | msgid "Supported on remote server" | |
1216 | msgstr "Suporte no servidor remoto" | |
1073 | 1217 | |
1074 | 1218 | msgid "Target" |
1075 | 1219 | msgstr "Alvo" |
1127 | 1271 | msgid "Unsupported daemon version %s, client version is %s" |
1128 | 1272 | msgstr "Sem suporte ao daemon na versão %s, a versão do cliente é %s" |
1129 | 1273 | |
1274 | #. TRANSLATORS: Device is updatable in this or any other mode | |
1275 | msgid "Updatable" | |
1276 | msgstr "Atualizável" | |
1277 | ||
1130 | 1278 | #. TRANSLATORS: error message from last update attempt |
1131 | 1279 | msgid "Update Error" |
1132 | 1280 | msgstr "Erro na atualização" |
1152 | 1300 | msgid "Update now?" |
1153 | 1301 | msgstr "Atualizar agora?" |
1154 | 1302 | |
1303 | #. TRANSLATORS: Update can only be done from offline mode | |
1304 | msgid "Update requires a reboot" | |
1305 | msgstr "A atualização requer uma reinicialização" | |
1306 | ||
1307 | #. TRANSLATORS: command description | |
1308 | msgid "Update the stored cryptographic hash with current ROM contents" | |
1309 | msgstr "Atualiza o hash criptográfico armazenado com o atual conteúdo da ROM" | |
1310 | ||
1155 | 1311 | msgid "Update the stored device verification information" |
1156 | 1312 | msgstr "Atualizar as informações de verificação do dispositivo armazenado" |
1157 | 1313 | |
1174 | 1330 | #, c-format |
1175 | 1331 | msgid "Updating %s…" |
1176 | 1332 | msgstr "Atualizando %s…" |
1333 | ||
1334 | #. TRANSLATORS: message letting the user know an upgrade is available | |
1335 | #. * %1 is the device name and %2 and %3 are version strings | |
1336 | #, c-format | |
1337 | msgid "Upgrade available for %s from %s to %s" | |
1338 | msgstr "Atualização disponível para %s de %s para %s" | |
1177 | 1339 | |
1178 | 1340 | #. TRANSLATORS: the server sent the user a small message |
1179 | 1341 | msgid "Upload message:" |
1201 | 1363 | msgid "Use quirk flags when installing firmware" |
1202 | 1364 | msgstr "Usa opções de peculiaridades ao instalar firmware" |
1203 | 1365 | |
1366 | #. TRANSLATORS: User has been notified | |
1367 | msgid "User has been notified" | |
1368 | msgstr "O usuário foi notificado" | |
1369 | ||
1204 | 1370 | #. TRANSLATORS: remote filename base |
1205 | 1371 | msgid "Username" |
1206 | 1372 | msgstr "Nome de usuário" |
81 | 81 | msgid "%s Update" |
82 | 82 | msgstr "Оновлення для %s" |
83 | 83 | |
84 | #. TRANSLATORS: warn the user before updating, %1 is a device name | |
85 | #, c-format | |
86 | msgid "%s and all connected devices may not be usable while updating." | |
87 | msgstr "%s і усі з'єднані пристрою можуть бути недоступними протягом оновлення." | |
88 | ||
89 | #. TRANSLATORS: warn the user before updating, %1 is a device name | |
90 | #, c-format | |
91 | msgid "%s must remain connected for the duration of the update to avoid damage." | |
92 | msgstr "Для уникнення пошкоджень %s має лишатися з'єднаним із комп'ютером протягом оновлення." | |
93 | ||
94 | #. TRANSLATORS: warn the user before updating, %1 is a machine name | |
95 | #, c-format | |
96 | msgid "%s must remain plugged into a power source for the duration of the update to avoid damage." | |
97 | msgstr "Для уникнення пошкоджень %s має лишатися з'єднаним із джерелом живлення протягом оновлення." | |
98 | ||
84 | 99 | #. TRANSLATORS: duration in days! |
85 | 100 | #, c-format |
86 | 101 | msgid "%u day" |
272 | 287 | msgid "Changed" |
273 | 288 | msgstr "Змінено" |
274 | 289 | |
290 | #. TRANSLATORS: command description | |
291 | msgid "Checks cryptographic hash matches firmware" | |
292 | msgstr "Перевірити відповідність криптографічних хешів мікропрограми" | |
293 | ||
275 | 294 | #. TRANSLATORS: remote checksum |
276 | 295 | msgid "Checksum" |
277 | 296 | msgstr "Контрольна сума" |
281 | 300 | msgstr "Виберіть пристрій:" |
282 | 301 | |
283 | 302 | #. TRANSLATORS: get interactive prompt |
303 | msgid "Choose a firmware type:" | |
304 | msgstr "Виберіть тип мікропрограми:" | |
305 | ||
306 | #. TRANSLATORS: get interactive prompt | |
284 | 307 | msgid "Choose a release:" |
285 | 308 | msgstr "Виберіть випуск:" |
286 | 309 | |
296 | 319 | msgid "Command not found" |
297 | 320 | msgstr "Такої команди не знайдено" |
298 | 321 | |
322 | #. TRANSLATORS: prompt to apply the update | |
323 | msgid "Continue with update?" | |
324 | msgstr "Продовжити оновлення?" | |
325 | ||
299 | 326 | #. TRANSLATORS: command description |
300 | 327 | msgid "Convert firmware to DFU format" |
301 | 328 | msgstr "Перетворити мікропрограму у формат DFU" |
329 | ||
330 | #. TRANSLATORS: Device supports some form of checksum verification | |
331 | msgid "Cryptographic hash verification is available" | |
332 | msgstr "Доступна перевірка криптографічної хеш-суми" | |
302 | 333 | |
303 | 334 | #. TRANSLATORS: version number of current firmware |
304 | 335 | msgid "Current version" |
328 | 359 | msgid "Details" |
329 | 360 | msgstr "Подробиці" |
330 | 361 | |
362 | #. TRANSLATORS: description of device ability | |
363 | msgid "Device Flags" | |
364 | msgstr "Прапорці пристрою" | |
365 | ||
331 | 366 | #. TRANSLATORS: ID for hardware, typically a SHA1 sum |
332 | 367 | msgid "Device ID" |
333 | 368 | msgstr "Ід. пристрою" |
336 | 371 | msgid "Device added:" |
337 | 372 | msgstr "Додано пристрій:" |
338 | 373 | |
374 | #. TRANSLATORS: Device supports a safety mechanism for flashing | |
375 | msgid "Device can recover flash failures" | |
376 | msgstr "Пристрій може відновлюватися при невдалих оновленнях" | |
377 | ||
339 | 378 | #. TRANSLATORS: this is when a device has been updated |
340 | 379 | msgid "Device changed:" |
341 | 380 | msgstr "Змінено пристрій:" |
342 | 381 | |
382 | #. TRANSLATORS: Is locked and can be unlocked | |
383 | msgid "Device is locked" | |
384 | msgstr "Пристрій заблоковано" | |
385 | ||
386 | #. TRANSLATORS: Device remains usable during update | |
387 | msgid "Device is usable for the duration of the update" | |
388 | msgstr "Пристроєм можна користуватися протягом оновлення" | |
389 | ||
343 | 390 | #. TRANSLATORS: this is when a device is hotplugged |
344 | 391 | msgid "Device removed:" |
345 | 392 | msgstr "Вилучено пристрій:" |
346 | 393 | |
394 | #. TRANSLATORS: Device supports a safety mechanism for flashing | |
395 | msgid "Device stages updates" | |
396 | msgstr "Пристрій із покроковим оновленням" | |
397 | ||
398 | #. TRANSLATORS: Device update needs to be separately activated | |
399 | msgid "Device update needs activation" | |
400 | msgstr "Оновлення пристрою потребує активації" | |
401 | ||
402 | #. TRANSLATORS: Device will not return after update completes | |
403 | msgid "Device will not re-appear after update completes" | |
404 | msgstr "Пристрій не з'явиться у списку пристроїв після завершення оновлення" | |
405 | ||
347 | 406 | #. TRANSLATORS: a list of successful updates |
348 | 407 | msgid "Devices that have been updated successfully:" |
349 | 408 | msgstr "Пристрої, для яких вдалося успішно оновити дані:" |
382 | 441 | #. TRANSLATORS: turn on all debugging |
383 | 442 | msgid "Do not include timestamp prefix" |
384 | 443 | msgstr "Не включати префікс часової позначки" |
444 | ||
445 | #. TRANSLATORS: command line option | |
446 | msgid "Do not perform device safety checks" | |
447 | msgstr "Не виконувати перевірок безпечності для пристрою" | |
385 | 448 | |
386 | 449 | msgid "Do not upload report at this time, but prompt again for future updates" |
387 | 450 | msgid_plural "Do not upload reports at this time, but prompt again for future updates" |
637 | 700 | msgid "Gets the results from the last update" |
638 | 701 | msgstr "Отримує результати з останнього оновлення" |
639 | 702 | |
703 | #. TRANSLATORS: The hardware is waiting to be replugged | |
704 | msgid "Hardware is waiting to be replugged" | |
705 | msgstr "Обладнання очікує на повторне з'єднання" | |
706 | ||
640 | 707 | #. TRANSLATORS: daemon is inactive |
641 | 708 | msgid "Idle…" |
642 | 709 | msgstr "Бездіяльність…" |
645 | 712 | msgid "Ignore SSL strict checks when downloading files" |
646 | 713 | msgstr "Ігнорувати результати строгої перевірки SSL під час отримання файлів" |
647 | 714 | |
715 | #. TRANSLATORS: Ignore validation safety checks when flashing this device | |
716 | msgid "Ignore validation safety checks" | |
717 | msgstr "Ігнорувати перевірки безпечності" | |
718 | ||
648 | 719 | #. TRANSLATORS: length of time the update takes to apply |
649 | 720 | msgid "Install Duration" |
650 | 721 | msgstr "Тривалість встановлення" |
666 | 737 | msgid "Install signed system firmware" |
667 | 738 | msgstr "Встановити підписану мікропрограму системи" |
668 | 739 | |
740 | #. TRANSLATORS: Install composite firmware on the parent before the child | |
741 | msgid "Install to parent device first" | |
742 | msgstr "Встановити спочатку на батьківському пристрої" | |
743 | ||
669 | 744 | msgid "Install unsigned device firmware" |
670 | 745 | msgstr "Встановити непідписану мікропрограму пристрою" |
671 | 746 | |
684 | 759 | #, c-format |
685 | 760 | msgid "Installing on %s…" |
686 | 761 | msgstr "Встановлюємо на %s…" |
762 | ||
763 | #. TRANSLATORS: Device cannot be removed easily | |
764 | msgid "Internal device" | |
765 | msgstr "Внутрішній пристрій" | |
766 | ||
767 | #. TRANSLATORS: Is currently in bootloader mode | |
768 | msgid "Is in bootloader mode" | |
769 | msgstr "Перебуває у режимі завантажувача" | |
687 | 770 | |
688 | 771 | #. TRANSLATORS: issue fixed with the release, e.g. CVE |
689 | 772 | msgid "Issue" |
718 | 801 | msgid "List supported firmware updates" |
719 | 802 | msgstr "Показати список підтримуваних оновлень мікропрограми" |
720 | 803 | |
804 | #. TRANSLATORS: command description | |
805 | msgid "List the available firmware types" | |
806 | msgstr "Вивести список доступних типів мікропрограм" | |
807 | ||
721 | 808 | #. TRANSLATORS: parsing the firmware information |
722 | 809 | msgid "Loading…" |
723 | 810 | msgstr "Завантаження…" |
769 | 856 | msgid "Monitor the daemon for events" |
770 | 857 | msgstr "Стежити за подіями у фоновій службі" |
771 | 858 | |
859 | #. TRANSLATORS: Requires a reboot to apply firmware or to reload hardware | |
860 | msgid "Needs a reboot after installation" | |
861 | msgstr "Потребує перезавантаження після встановлення" | |
862 | ||
863 | #. TRANSLATORS: Requires system shutdown to apply firmware | |
864 | msgid "Needs shutdown after installation" | |
865 | msgstr "Потребує вимикання після встановлення" | |
866 | ||
772 | 867 | #. TRANSLATORS: version number of new firmware |
773 | 868 | msgid "New version" |
774 | 869 | msgstr "Нова версія" |
776 | 871 | msgid "No action specified!" |
777 | 872 | msgstr "Не вказано дії!" |
778 | 873 | |
874 | #. TRANSLATORS: nothing found | |
875 | msgid "No firmware IDs found" | |
876 | msgstr "Не знайдено ідентифікаторів мікропрограм" | |
877 | ||
779 | 878 | #. TRANSLATORS: nothing attached that can be upgraded |
780 | 879 | msgid "No hardware detected with firmware update capability" |
781 | 880 | msgstr "Не виявлено обладнання із передбаченою можливістю оновлення мікропрограми" |
812 | 911 | msgid "Override warnings and force the action" |
813 | 912 | msgstr "Не зважати на попередження і примусово виконати дію" |
814 | 913 | |
914 | #. TRANSLATORS: command description | |
915 | msgid "Parse and show details about a firmware file" | |
916 | msgstr "Обробити і показати параметри файла мікропрограми" | |
917 | ||
815 | 918 | #. TRANSLATORS: remote filename base |
816 | 919 | msgid "Password" |
817 | 920 | msgstr "Пароль" |
854 | 957 | msgstr "Опитати щодо підтримки оновлення мікропрограми" |
855 | 958 | |
856 | 959 | #. TRANSLATORS: command description |
960 | msgid "Read a firmware blob from a device" | |
961 | msgstr "Прочитати бінарну мікропрограму з пристрою" | |
962 | ||
963 | #. TRANSLATORS: command description | |
857 | 964 | msgid "Read firmware from device into a file" |
858 | 965 | msgstr "Прочитати мікропрограму з пристрою до файла" |
859 | 966 | |
860 | 967 | #. TRANSLATORS: command description |
861 | 968 | msgid "Read firmware from one partition into a file" |
862 | 969 | msgstr "Прочитати мікропрограму з одного розділу до файла" |
970 | ||
971 | #. TRANSLATORS: %1 is a device name | |
972 | #, c-format | |
973 | msgid "Reading from %s…" | |
974 | msgstr "Читаємо з %s…" | |
863 | 975 | |
864 | 976 | #. TRANSLATORS: reading from the flash chips |
865 | 977 | msgid "Reading…" |
901 | 1013 | msgid "Report URI" |
902 | 1014 | msgstr "Адреса звіту" |
903 | 1015 | |
1016 | #. TRANSLATORS: Has been reported to a metadata server | |
1017 | msgid "Reported to remote server" | |
1018 | msgstr "Повідомлено на віддаленому сервері" | |
1019 | ||
1020 | #. TRANSLATORS: Must be plugged in to an outlet | |
1021 | msgid "Requires AC power" | |
1022 | msgstr "Потребує сталого живлення" | |
1023 | ||
1024 | #. TRANSLATORS: Requires a bootloader mode to be manually enabled by the user | |
1025 | msgid "Requires a bootloader" | |
1026 | msgstr "Потребує завантажувача" | |
1027 | ||
904 | 1028 | #. TRANSLATORS: metadata is downloaded from the Internet |
905 | 1029 | msgid "Requires internet connection" |
906 | 1030 | msgstr "Потребує з'єднання із інтернетом" |
940 | 1064 | #. TRANSLATORS: scheduing an update to be done on the next boot |
941 | 1065 | msgid "Scheduling…" |
942 | 1066 | msgstr "Плануємо…" |
1067 | ||
1068 | #. TRANSLATORS: Device has been chosen by the daemon for the user | |
1069 | msgid "Selected device" | |
1070 | msgstr "Вибрано пристрій" | |
943 | 1071 | |
944 | 1072 | #. TRANSLATORS: serial number of hardware |
945 | 1073 | msgid "Serial Number" |
1075 | 1203 | msgid "Successfully modified configuration value" |
1076 | 1204 | msgstr "Успішно змінено значення у налаштуваннях" |
1077 | 1205 | |
1206 | #. TRANSLATORS: success message for a per-remote setting change | |
1207 | msgid "Successfully modified remote" | |
1208 | msgstr "Успішно змінено віддалене сховище" | |
1209 | ||
1078 | 1210 | #. TRANSLATORS: success message -- the user can do this by-hand too |
1079 | 1211 | msgid "Successfully refreshed metadata manually" |
1080 | 1212 | msgstr "Метадані успішно оновлено вручну" |
1213 | ||
1214 | #. TRANSLATORS: success message when user refreshes device checksums | |
1215 | msgid "Successfully updated device checksums" | |
1216 | msgstr "Успішно оновлено контрольні суми пристрою" | |
1081 | 1217 | |
1082 | 1218 | #. TRANSLATORS: success message -- where the user has uploaded |
1083 | 1219 | #. * success and/or failure reports to the remote server |
1089 | 1225 | msgstr[2] "Успішно вивантажено %u звітів" |
1090 | 1226 | msgstr[3] "Успішно вивантажено %u звіт" |
1091 | 1227 | |
1228 | #. TRANSLATORS: success message when user verified device checksums | |
1229 | msgid "Successfully verified device checksums" | |
1230 | msgstr "Успішно перевірено контрольні суми пристрою" | |
1231 | ||
1092 | 1232 | #. TRANSLATORS: one line summary of device |
1093 | 1233 | msgid "Summary" |
1094 | 1234 | msgstr "Резюме" |
1235 | ||
1236 | #. TRANSLATORS: Is found in current metadata | |
1237 | msgid "Supported on remote server" | |
1238 | msgstr "Підтримуваний на віддаленому сервері" | |
1095 | 1239 | |
1096 | 1240 | msgid "Target" |
1097 | 1241 | msgstr "Ціль" |
1149 | 1293 | msgid "Unsupported daemon version %s, client version is %s" |
1150 | 1294 | msgstr "Непідтримувана версія фонової служби %s, версія клієнта — %s" |
1151 | 1295 | |
1296 | #. TRANSLATORS: Device is updatable in this or any other mode | |
1297 | msgid "Updatable" | |
1298 | msgstr "Придатний до оновлення" | |
1299 | ||
1152 | 1300 | #. TRANSLATORS: error message from last update attempt |
1153 | 1301 | msgid "Update Error" |
1154 | 1302 | msgstr "Помилка оновлення" |
1174 | 1322 | msgid "Update now?" |
1175 | 1323 | msgstr "Оновити зараз?" |
1176 | 1324 | |
1325 | #. TRANSLATORS: Update can only be done from offline mode | |
1326 | msgid "Update requires a reboot" | |
1327 | msgstr "Оновлення потребує перезавантаження" | |
1328 | ||
1329 | #. TRANSLATORS: command description | |
1330 | msgid "Update the stored cryptographic hash with current ROM contents" | |
1331 | msgstr "Оновити збережений криптографічний хеш на основі поточних даних ROM" | |
1332 | ||
1177 | 1333 | msgid "Update the stored device verification information" |
1178 | 1334 | msgstr "Оновлення збережених даних щодо верифікації пристрою" |
1179 | 1335 | |
1196 | 1352 | #, c-format |
1197 | 1353 | msgid "Updating %s…" |
1198 | 1354 | msgstr "Оновлюємо %s…" |
1355 | ||
1356 | #. TRANSLATORS: message letting the user know an upgrade is available | |
1357 | #. * %1 is the device name and %2 and %3 are version strings | |
1358 | #, c-format | |
1359 | msgid "Upgrade available for %s from %s to %s" | |
1360 | msgstr "Виявлено оновлення для %s з %s до %s" | |
1199 | 1361 | |
1200 | 1362 | #. TRANSLATORS: the server sent the user a small message |
1201 | 1363 | msgid "Upload message:" |
1227 | 1389 | msgid "Use quirk flags when installing firmware" |
1228 | 1390 | msgstr "Використовувати варіативні прапорці при встановленні мікропрограми" |
1229 | 1391 | |
1392 | #. TRANSLATORS: User has been notified | |
1393 | msgid "User has been notified" | |
1394 | msgstr "Сповіщено користувача" | |
1395 | ||
1230 | 1396 | #. TRANSLATORS: remote filename base |
1231 | 1397 | msgid "Username" |
1232 | 1398 | msgstr "Користувач" |
83 | 83 | * @self: A #FuArchive |
84 | 84 | * @callback: A #FuArchiveIterateFunc. |
85 | 85 | * @user_data: User data. |
86 | * @error: A #GError, or %NULL | |
86 | 87 | * |
87 | 88 | * Iterates over the archive contents, calling the given function for each |
88 | * of the files found. | |
89 | * of the files found. If any @callback returns %FALSE scanning is aborted. | |
90 | * | |
91 | * Returns: True if no @callback returned FALSE | |
89 | 92 | */ |
90 | void | |
91 | fu_archive_iterate (FuArchive *self, FuArchiveIterateFunc callback, gpointer user_data) | |
93 | gboolean | |
94 | fu_archive_iterate (FuArchive *self, | |
95 | FuArchiveIterateFunc callback, | |
96 | gpointer user_data, | |
97 | GError **error) | |
92 | 98 | { |
93 | 99 | GHashTableIter iter; |
94 | 100 | gpointer key, value; |
95 | 101 | |
96 | g_return_if_fail (FU_IS_ARCHIVE (self)); | |
97 | g_return_if_fail (callback != NULL); | |
102 | g_return_val_if_fail (FU_IS_ARCHIVE (self), FALSE); | |
103 | g_return_val_if_fail (callback != NULL, FALSE); | |
98 | 104 | |
99 | 105 | g_hash_table_iter_init (&iter, self->entries); |
100 | while (g_hash_table_iter_next (&iter, &key, &value)) | |
101 | callback (self, (const gchar *)key, (GBytes *)value, user_data); | |
106 | while (g_hash_table_iter_next (&iter, &key, &value)) { | |
107 | if (!callback (self, (const gchar *)key, (GBytes *)value, user_data, error)) | |
108 | return FALSE; | |
109 | } | |
110 | return TRUE; | |
102 | 111 | } |
103 | 112 | |
104 | 113 | /* workaround the struct types of libarchive */ |
34 | 34 | * |
35 | 35 | * Specifies the type of archive iteration function. |
36 | 36 | */ |
37 | typedef void (*FuArchiveIterateFunc) (FuArchive *self, | |
37 | typedef gboolean (*FuArchiveIterateFunc) (FuArchive *self, | |
38 | 38 | const gchar *filename, |
39 | 39 | GBytes *bytes, |
40 | gpointer user_data); | |
40 | gpointer user_data, | |
41 | GError **error); | |
41 | 42 | |
42 | 43 | FuArchive *fu_archive_new (GBytes *data, |
43 | 44 | FuArchiveFlags flags, |
45 | 46 | GBytes *fu_archive_lookup_by_fn (FuArchive *self, |
46 | 47 | const gchar *fn, |
47 | 48 | GError **error); |
48 | void fu_archive_iterate (FuArchive *self, | |
49 | gboolean fu_archive_iterate (FuArchive *self, | |
49 | 50 | FuArchiveIterateFunc callback, |
50 | gpointer user_data); | |
51 | gpointer user_data, | |
52 | GError **error); |
78 | 78 | (val >> 24) & 0x0f, |
79 | 79 | (val >> 16) & 0xff, |
80 | 80 | val & 0xffff); |
81 | } | |
82 | if (kind == FWUPD_VERSION_FORMAT_SURFACE_LEGACY) { | |
83 | /* 10b.12b.10b */ | |
84 | return g_strdup_printf ("%u.%u.%u", | |
85 | (val >> 22) & 0x3ff, | |
86 | (val >> 10) & 0xfff, | |
87 | val & 0x3ff); | |
88 | } | |
89 | if (kind == FWUPD_VERSION_FORMAT_SURFACE) { | |
90 | /* 8b.16b.8b */ | |
91 | return g_strdup_printf ("%u.%u.%u", | |
92 | (val >> 24) & 0xff, | |
93 | (val >> 8) & 0xffff, | |
94 | val & 0xff); | |
81 | 95 | } |
82 | 96 | g_critical ("failed to convert version format %s: %u", |
83 | 97 | fwupd_version_format_to_string (kind), val); |
52 | 52 | * @FU_PATH_KIND_SYSFSDIR_FW: The sysfs firmware location (IE /sys/firmware) |
53 | 53 | * @FU_PATH_KIND_SYSFSDIR_DRIVERS: The platform sysfs directory (IE /sys/bus/platform/drivers) |
54 | 54 | * @FU_PATH_KIND_SYSFSDIR_TPM: The TPM sysfs directory (IE /sys/class/tpm) |
55 | * @FU_PATH_KIND_POLKIT_ACTIONS The directory for policy kit actions (IE /usr/share/polkit-1/actions/) | |
55 | * @FU_PATH_KIND_POLKIT_ACTIONS: The directory for policy kit actions (IE /usr/share/polkit-1/actions/) | |
56 | 56 | * |
57 | 57 | * Path types to use when dynamically determining a path at runtime |
58 | 58 | **/ |
1078 | 1078 | fu_engine_check_requirement_firmware (FuEngine *self, XbNode *req, |
1079 | 1079 | FuDevice *device, GError **error) |
1080 | 1080 | { |
1081 | guint64 depth; | |
1082 | g_autoptr(FuDevice) device_actual = g_object_ref (device); | |
1081 | 1083 | g_autoptr(GError) error_local = NULL; |
1084 | ||
1085 | /* look at the parent device */ | |
1086 | depth = xb_node_get_attr_as_uint (req, "depth"); | |
1087 | if (depth != G_MAXUINT64) { | |
1088 | for (guint64 i = 0; i < depth; i++) { | |
1089 | FuDevice *device_tmp = fu_device_get_parent (device_actual); | |
1090 | if (device_actual == NULL) { | |
1091 | g_set_error (error, | |
1092 | FWUPD_ERROR, | |
1093 | FWUPD_ERROR_NOT_SUPPORTED, | |
1094 | "No parent device for %s " | |
1095 | "(%" G_GUINT64_FORMAT "/%" G_GUINT64_FORMAT ")", | |
1096 | fu_device_get_name (device_actual), i, depth); | |
1097 | return FALSE; | |
1098 | } | |
1099 | g_set_object (&device_actual, device_tmp); | |
1100 | } | |
1101 | } | |
1082 | 1102 | |
1083 | 1103 | /* old firmware version */ |
1084 | 1104 | if (xb_node_get_text (req) == NULL) { |
1085 | const gchar *version = fu_device_get_version (device); | |
1105 | const gchar *version = fu_device_get_version (device_actual); | |
1086 | 1106 | if (!fu_engine_require_vercmp (req, version, &error_local)) { |
1087 | 1107 | if (g_strcmp0 (xb_node_get_attr (req, "compare"), "ge") == 0) { |
1088 | 1108 | g_set_error (error, |
1104 | 1124 | |
1105 | 1125 | /* bootloader version */ |
1106 | 1126 | if (g_strcmp0 (xb_node_get_text (req), "bootloader") == 0) { |
1107 | const gchar *version = fu_device_get_version_bootloader (device); | |
1127 | const gchar *version = fu_device_get_version_bootloader (device_actual); | |
1108 | 1128 | if (!fu_engine_require_vercmp (req, version, &error_local)) { |
1109 | 1129 | if (g_strcmp0 (xb_node_get_attr (req, "compare"), "ge") == 0) { |
1110 | 1130 | g_set_error (error, |
1126 | 1146 | |
1127 | 1147 | /* vendor ID */ |
1128 | 1148 | if (g_strcmp0 (xb_node_get_text (req), "vendor-id") == 0) { |
1129 | const gchar *version = fu_device_get_vendor_id (device); | |
1149 | const gchar *version = fu_device_get_vendor_id (device_actual); | |
1130 | 1150 | if (!fu_engine_require_vercmp (req, version, &error_local)) { |
1131 | 1151 | if (g_strcmp0 (xb_node_get_attr (req, "compare"), "ge") == 0) { |
1132 | 1152 | g_set_error (error, |
1148 | 1168 | |
1149 | 1169 | /* child version */ |
1150 | 1170 | if (g_strcmp0 (xb_node_get_text (req), "not-child") == 0) |
1151 | return fu_engine_check_requirement_not_child (self, req, device, error); | |
1171 | return fu_engine_check_requirement_not_child (self, req, device_actual, error); | |
1152 | 1172 | |
1153 | 1173 | /* another device */ |
1154 | 1174 | if (fwupd_guid_is_valid (xb_node_get_text (req))) { |
1155 | 1175 | const gchar *guid = xb_node_get_text (req); |
1156 | 1176 | const gchar *version; |
1157 | g_autoptr(FuDevice) device2 = NULL; | |
1158 | 1177 | |
1159 | 1178 | /* find if the other device exists */ |
1160 | device2 = fu_device_list_get_by_guid (self->device_list, guid, error); | |
1161 | if (device2 == NULL) | |
1162 | return FALSE; | |
1179 | if (depth == G_MAXUINT64) { | |
1180 | g_autoptr(FuDevice) device_tmp = NULL; | |
1181 | device_tmp = fu_device_list_get_by_guid (self->device_list, guid, error); | |
1182 | if (device_tmp == NULL) | |
1183 | return FALSE; | |
1184 | g_set_object (&device_actual, device_tmp); | |
1185 | ||
1186 | /* verify the parent device has the GUID */ | |
1187 | } else { | |
1188 | if (!fu_device_has_guid (device_actual, guid)) { | |
1189 | g_set_error (error, | |
1190 | FWUPD_ERROR, | |
1191 | FWUPD_ERROR_NOT_SUPPORTED, | |
1192 | "No GUID of %s on parent device %s", | |
1193 | guid, fu_device_get_name (device_actual)); | |
1194 | return FALSE; | |
1195 | } | |
1196 | } | |
1163 | 1197 | |
1164 | 1198 | /* get the version of the other device */ |
1165 | version = fu_device_get_version (device2); | |
1199 | version = fu_device_get_version (device_actual); | |
1166 | 1200 | if (version != NULL && |
1201 | xb_node_get_attr (req, "compare") != NULL && | |
1167 | 1202 | !fu_engine_require_vercmp (req, version, &error_local)) { |
1168 | 1203 | if (g_strcmp0 (xb_node_get_attr (req, "compare"), "ge") == 0) { |
1169 | 1204 | g_set_error (error, |
1170 | 1205 | FWUPD_ERROR, |
1171 | 1206 | FWUPD_ERROR_INVALID_FILE, |
1172 | 1207 | "Not compatible with %s version %s, requires >= %s", |
1173 | fu_device_get_name (device2), | |
1208 | fu_device_get_name (device_actual), | |
1174 | 1209 | version, |
1175 | 1210 | xb_node_get_attr (req, "version")); |
1176 | 1211 | } else { |
1178 | 1213 | FWUPD_ERROR, |
1179 | 1214 | FWUPD_ERROR_INVALID_FILE, |
1180 | 1215 | "Not compatible with %s: %s", |
1181 | fu_device_get_name (device2), | |
1216 | fu_device_get_name (device_actual), | |
1182 | 1217 | error_local->message); |
1183 | 1218 | } |
1184 | 1219 | return FALSE; |
1871 | 1906 | FwupdInstallFlags flags, |
1872 | 1907 | GError **error) |
1873 | 1908 | { |
1874 | g_autoptr(FuDeviceLocker) locker = fu_device_locker_new (device, error); | |
1909 | g_autoptr(FuDeviceLocker) locker = NULL; | |
1910 | ||
1911 | if (fu_device_has_flag (device, FWUPD_DEVICE_FLAG_WILL_DISAPPEAR)) { | |
1912 | g_debug ("skipping device cleanup due to will-disappear flag"); | |
1913 | return TRUE; | |
1914 | } | |
1915 | ||
1916 | locker = fu_device_locker_new (device, error); | |
1875 | 1917 | if (locker == NULL) |
1876 | 1918 | return FALSE; |
1877 | 1919 | return fu_device_cleanup (device, flags, error); |
4368 | 4410 | /* if loaded from fu_engine_load() open the plugin */ |
4369 | 4411 | if (self->usb_ctx != NULL) { |
4370 | 4412 | if (!fu_plugin_open (plugin, filename, &error_local)) { |
4371 | g_warning ("failed to open plugin %s: %s", | |
4372 | filename, error_local->message); | |
4413 | g_warning ("%s", error_local->message); | |
4373 | 4414 | continue; |
4374 | 4415 | } |
4375 | 4416 | } |
22 | 22 | GBytes *bytes; |
23 | 23 | guint64 addr; |
24 | 24 | guint64 idx; |
25 | gchar *version; | |
25 | 26 | } FuFirmwareImagePrivate; |
26 | 27 | |
27 | 28 | G_DEFINE_TYPE_WITH_PRIVATE (FuFirmwareImage, fu_firmware_image, G_TYPE_OBJECT) |
28 | 29 | #define GET_PRIVATE(o) (fu_firmware_image_get_instance_private (o)) |
30 | ||
31 | /** | |
32 | * fu_firmware_image_get_version: | |
33 | * @self: A #FuFirmwareImage | |
34 | * | |
35 | * Gets an optional version that represents the firmware image. | |
36 | * | |
37 | * Returns: a string, or %NULL | |
38 | * | |
39 | * Since: 1.3.4 | |
40 | **/ | |
41 | const gchar * | |
42 | fu_firmware_image_get_version (FuFirmwareImage *self) | |
43 | { | |
44 | FuFirmwareImagePrivate *priv = GET_PRIVATE (self); | |
45 | g_return_val_if_fail (FU_IS_FIRMWARE_IMAGE (self), NULL); | |
46 | return priv->version; | |
47 | } | |
48 | ||
49 | /** | |
50 | * fu_firmware_image_set_version: | |
51 | * @self: A #FuFirmwareImage | |
52 | * @version: A string version, or %NULL | |
53 | * | |
54 | * Sets an optional version that represents the firmware image. | |
55 | * | |
56 | * Since: 1.3.4 | |
57 | **/ | |
58 | void | |
59 | fu_firmware_image_set_version (FuFirmwareImage *self, const gchar *version) | |
60 | { | |
61 | FuFirmwareImagePrivate *priv = GET_PRIVATE (self); | |
62 | g_return_if_fail (FU_IS_FIRMWARE_IMAGE (self)); | |
63 | g_free (priv->version); | |
64 | priv->version = g_strdup (version); | |
65 | } | |
29 | 66 | |
30 | 67 | /** |
31 | 68 | * fu_firmware_image_set_id: |
260 | 297 | fu_common_string_append_kx (str, idt, "Index", priv->idx); |
261 | 298 | if (priv->addr != 0x0) |
262 | 299 | fu_common_string_append_kx (str, idt, "Address", priv->addr); |
300 | if (priv->version != NULL) | |
301 | fu_common_string_append_kv (str, 0, "Version", priv->version); | |
263 | 302 | if (priv->bytes != NULL) { |
264 | 303 | fu_common_string_append_kx (str, idt, "Data", |
265 | 304 | g_bytes_get_size (priv->bytes)); |
299 | 338 | FuFirmwareImage *self = FU_FIRMWARE_IMAGE (object); |
300 | 339 | FuFirmwareImagePrivate *priv = GET_PRIVATE (self); |
301 | 340 | g_free (priv->id); |
341 | g_free (priv->version); | |
302 | 342 | if (priv->bytes != NULL) |
303 | 343 | g_bytes_unref (priv->bytes); |
304 | 344 | G_OBJECT_CLASS (fu_firmware_image_parent_class)->finalize (object); |
27 | 27 | gpointer padding[28]; |
28 | 28 | }; |
29 | 29 | |
30 | #define FU_FIRMWARE_IMAGE_ID_PAYLOAD "payload" | |
31 | #define FU_FIRMWARE_IMAGE_ID_SIGNATURE "signature" | |
32 | #define FU_FIRMWARE_IMAGE_ID_HEADER "header" | |
33 | ||
30 | 34 | FuFirmwareImage *fu_firmware_image_new (GBytes *bytes); |
31 | gchar *fu_firmware_image_to_string (FuFirmwareImage *selfz); | |
35 | gchar *fu_firmware_image_to_string (FuFirmwareImage *self); | |
32 | 36 | |
37 | const gchar *fu_firmware_image_get_version (FuFirmwareImage *self); | |
38 | void fu_firmware_image_set_version (FuFirmwareImage *self, | |
39 | const gchar *version); | |
33 | 40 | const gchar *fu_firmware_image_get_id (FuFirmwareImage *self); |
34 | 41 | void fu_firmware_image_set_id (FuFirmwareImage *self, |
35 | 42 | const gchar *id); |
15 | 15 | |
16 | 16 | struct _FuIhexFirmware { |
17 | 17 | FuFirmware parent_instance; |
18 | GPtrArray *records; | |
18 | 19 | }; |
19 | 20 | |
20 | 21 | G_DEFINE_TYPE (FuIhexFirmware, fu_ihex_firmware, FU_TYPE_FIRMWARE) |
26 | 27 | #define DFU_INHX32_RECORD_TYPE_EXTENDED_LINEAR 0x04 |
27 | 28 | #define DFU_INHX32_RECORD_TYPE_START_LINEAR 0x05 |
28 | 29 | #define DFU_INHX32_RECORD_TYPE_SIGNATURE 0xfd |
30 | ||
31 | /** | |
32 | * fu_ihex_firmware_get_records: | |
33 | * @self: A #FuIhexFirmware | |
34 | * | |
35 | * Returns the raw lines from tokenization. | |
36 | * | |
37 | * This might be useful if the plugin is expecting the hex file to be a list | |
38 | * of operations, rather than a simple linear image with filled holes. | |
39 | * | |
40 | * Returns: (transfer none) (element-type FuIhexFirmwareRecord): records | |
41 | * | |
42 | * Since: 1.3.4 | |
43 | **/ | |
44 | GPtrArray * | |
45 | fu_ihex_firmware_get_records (FuIhexFirmware *self) | |
46 | { | |
47 | g_return_val_if_fail (FU_IS_IHEX_FIRMWARE (self), NULL); | |
48 | return self->records; | |
49 | } | |
50 | ||
51 | static void | |
52 | fu_ihex_firmware_record_free (FuIhexFirmwareRecord *rcd) | |
53 | { | |
54 | g_string_free (rcd->buf, TRUE); | |
55 | g_free (rcd); | |
56 | } | |
57 | ||
58 | G_DEFINE_AUTOPTR_CLEANUP_FUNC(FuIhexFirmwareRecord, fu_ihex_firmware_record_free) | |
59 | ||
60 | static FuIhexFirmwareRecord * | |
61 | fu_ihex_firmware_record_new (guint ln, const gchar *buf) | |
62 | { | |
63 | FuIhexFirmwareRecord *rcd = g_new0 (FuIhexFirmwareRecord, 1); | |
64 | rcd->ln = ln; | |
65 | rcd->buf = g_string_new (buf); | |
66 | return rcd; | |
67 | } | |
29 | 68 | |
30 | 69 | static const gchar * |
31 | 70 | fu_ihex_firmware_record_type_to_string (guint8 record_type) |
48 | 87 | } |
49 | 88 | |
50 | 89 | static gboolean |
90 | fu_ihex_firmware_tokenize (FuFirmware *firmware, GBytes *fw, | |
91 | FwupdInstallFlags flags, GError **error) | |
92 | { | |
93 | FuIhexFirmware *self = FU_IHEX_FIRMWARE (firmware); | |
94 | gsize sz = 0; | |
95 | const gchar *data = g_bytes_get_data (fw, &sz); | |
96 | g_auto(GStrv) lines = fu_common_strnsplit (data, sz, "\n", -1); | |
97 | ||
98 | for (guint ln = 0; lines[ln] != NULL; ln++) { | |
99 | g_autoptr(FuIhexFirmwareRecord) rcd = NULL; | |
100 | g_strdelimit (lines[ln], "\r\x1a", '\0'); | |
101 | rcd = fu_ihex_firmware_record_new (ln + 1, lines[ln]); | |
102 | g_ptr_array_add (self->records, g_steal_pointer (&rcd)); | |
103 | } | |
104 | return TRUE; | |
105 | } | |
106 | ||
107 | static gboolean | |
51 | 108 | fu_ihex_firmware_parse (FuFirmware *firmware, |
52 | 109 | GBytes *fw, |
53 | 110 | guint64 addr_start, |
55 | 112 | FwupdInstallFlags flags, |
56 | 113 | GError **error) |
57 | 114 | { |
58 | const gchar *data; | |
115 | FuIhexFirmware *self = FU_IHEX_FIRMWARE (firmware); | |
59 | 116 | gboolean got_eof = FALSE; |
60 | gsize sz = 0; | |
61 | 117 | guint32 abs_addr = 0x0; |
62 | 118 | guint32 addr_last = 0x0; |
63 | 119 | guint32 img_addr = G_MAXUINT32; |
64 | 120 | guint32 seg_addr = 0x0; |
65 | g_auto(GStrv) lines = NULL; | |
66 | 121 | g_autoptr(FuFirmwareImage) img = fu_firmware_image_new (NULL); |
67 | 122 | g_autoptr(GBytes) img_bytes = NULL; |
68 | 123 | g_autoptr(GByteArray) buf = g_byte_array_new (); |
69 | 124 | g_autoptr(GByteArray) buf_signature = g_byte_array_new (); |
70 | 125 | |
71 | g_return_val_if_fail (fw != NULL, FALSE); | |
72 | ||
73 | 126 | /* parse records */ |
74 | data = g_bytes_get_data (fw, &sz); | |
75 | lines = fu_common_strnsplit (data, sz, "\n", -1); | |
76 | for (guint ln = 0; lines[ln] != NULL; ln++) { | |
77 | const gchar *line = lines[ln]; | |
78 | gsize linesz; | |
127 | for (guint k = 0; k < self->records->len; k++) { | |
128 | FuIhexFirmwareRecord *rcd = g_ptr_array_index (self->records, k); | |
129 | const gchar *line = rcd->buf->str; | |
79 | 130 | guint32 addr; |
80 | 131 | guint8 byte_cnt; |
81 | 132 | guint8 record_type; |
86 | 137 | continue; |
87 | 138 | |
88 | 139 | /* ignore blank lines */ |
89 | g_strdelimit (lines[ln], "\r\x1a", '\0'); | |
90 | linesz = strlen (line); | |
91 | if (linesz == 0) | |
140 | if (rcd->buf->len == 0) | |
92 | 141 | continue; |
93 | 142 | |
94 | 143 | /* check starting token */ |
97 | 146 | FWUPD_ERROR, |
98 | 147 | FWUPD_ERROR_INVALID_FILE, |
99 | 148 | "invalid starting token on line %u: %s", |
100 | ln + 1, line); | |
149 | rcd->ln, line); | |
101 | 150 | return FALSE; |
102 | 151 | } |
103 | 152 | |
104 | 153 | /* check there's enough data for the smallest possible record */ |
105 | if (linesz < 11) { | |
154 | if (rcd->buf->len < 11) { | |
106 | 155 | g_set_error (error, |
107 | 156 | FWUPD_ERROR, |
108 | 157 | FWUPD_ERROR_INVALID_FILE, |
109 | 158 | "line %u is incomplete, length %u", |
110 | ln + 1, (guint) linesz); | |
159 | rcd->ln, (guint) rcd->buf->len); | |
111 | 160 | return FALSE; |
112 | 161 | } |
113 | 162 | |
124 | 173 | |
125 | 174 | /* position of checksum */ |
126 | 175 | line_end = 9 + byte_cnt * 2; |
127 | if (line_end > (guint) linesz) { | |
176 | if (line_end > (guint) rcd->buf->len) { | |
128 | 177 | g_set_error (error, |
129 | 178 | FWUPD_ERROR, |
130 | 179 | FWUPD_ERROR_INVALID_FILE, |
131 | 180 | "line %u malformed, length: %u", |
132 | ln + 1, line_end); | |
181 | rcd->ln, line_end); | |
133 | 182 | return FALSE; |
134 | 183 | } |
135 | 184 | |
145 | 194 | FWUPD_ERROR, |
146 | 195 | FWUPD_ERROR_INVALID_FILE, |
147 | 196 | "line %u has invalid checksum (0x%02x)", |
148 | ln + 1, checksum); | |
197 | rcd->ln, checksum); | |
149 | 198 | return FALSE; |
150 | 199 | } |
151 | 200 | } |
165 | 214 | "invalid address 0x%x, last was 0x%x on line %u", |
166 | 215 | (guint) addr, |
167 | 216 | (guint) addr_last, |
168 | ln + 1); | |
217 | rcd->ln); | |
169 | 218 | return FALSE; |
170 | 219 | } |
171 | 220 | |
181 | 230 | FWUPD_ERROR_INVALID_FILE, |
182 | 231 | "hole of 0x%x bytes too large to fill on line %u", |
183 | 232 | (guint) len_hole, |
184 | ln + 1); | |
233 | rcd->ln); | |
185 | 234 | return FALSE; |
186 | 235 | } |
187 | 236 | if (addr_last > 0x0 && len_hole > 1) { |
188 | 237 | g_debug ("filling address 0x%08x to 0x%08x on line %u", |
189 | addr_last + 1, addr_last + len_hole - 1, ln + 1); | |
238 | addr_last + 1, addr_last + len_hole - 1, rcd->ln); | |
190 | 239 | for (guint j = 1; j < len_hole; j++) { |
191 | 240 | /* although 0xff might be clearer, |
192 | 241 | * we can't write 0xffff to pic14 */ |
212 | 261 | break; |
213 | 262 | case DFU_INHX32_RECORD_TYPE_EXTENDED_LINEAR: |
214 | 263 | abs_addr = fu_firmware_strparse_uint16 (line + 9) << 16; |
215 | g_debug (" abs_addr:\t0x%02x on line %u", abs_addr, ln + 1); | |
264 | g_debug (" abs_addr:\t0x%02x on line %u", abs_addr, rcd->ln); | |
216 | 265 | break; |
217 | 266 | case DFU_INHX32_RECORD_TYPE_START_LINEAR: |
218 | 267 | abs_addr = fu_firmware_strparse_uint32 (line + 9); |
219 | g_debug (" abs_addr:\t0x%08x on line %u", abs_addr, ln + 1); | |
268 | g_debug (" abs_addr:\t0x%08x on line %u", abs_addr, rcd->ln); | |
220 | 269 | break; |
221 | 270 | case DFU_INHX32_RECORD_TYPE_EXTENDED_SEGMENT: |
222 | 271 | /* segment base address, so ~1Mb addressable */ |
223 | 272 | seg_addr = fu_firmware_strparse_uint16 (line + 9) * 16; |
224 | g_debug (" seg_addr:\t0x%08x on line %u", seg_addr, ln + 1); | |
273 | g_debug (" seg_addr:\t0x%08x on line %u", seg_addr, rcd->ln); | |
225 | 274 | break; |
226 | 275 | case DFU_INHX32_RECORD_TYPE_START_SEGMENT: |
227 | 276 | /* initial content of the CS:IP registers */ |
228 | 277 | seg_addr = fu_firmware_strparse_uint32 (line + 9); |
229 | g_debug (" seg_addr:\t0x%02x on line %u", seg_addr, ln + 1); | |
278 | g_debug (" seg_addr:\t0x%02x on line %u", seg_addr, rcd->ln); | |
230 | 279 | break; |
231 | 280 | case DFU_INHX32_RECORD_TYPE_SIGNATURE: |
232 | 281 | for (guint i = 9; i < line_end; i += 2) { |
242 | 291 | FWUPD_ERROR, |
243 | 292 | FWUPD_ERROR_INVALID_FILE, |
244 | 293 | "invalid ihex record type %i on line %u", |
245 | record_type, ln + 1); | |
294 | record_type, rcd->ln); | |
246 | 295 | return FALSE; |
247 | 296 | } |
248 | 297 | } |
267 | 316 | if (buf_signature->len > 0) { |
268 | 317 | g_autoptr(GBytes) data_sig = g_bytes_new (buf_signature->data, buf_signature->len); |
269 | 318 | g_autoptr(FuFirmwareImage) img_sig = fu_firmware_image_new (data_sig); |
270 | fu_firmware_image_set_id (img_sig, "signature"); | |
319 | fu_firmware_image_set_id (img_sig, FU_FIRMWARE_IMAGE_ID_SIGNATURE); | |
271 | 320 | fu_firmware_add_image (firmware, img_sig); |
272 | 321 | } |
273 | 322 | return TRUE; |
312 | 361 | return FALSE; |
313 | 362 | |
314 | 363 | /* special case */ |
315 | if (g_strcmp0 (fu_firmware_image_get_id (img), "signature") == 0) | |
364 | if (g_strcmp0 (fu_firmware_image_get_id (img), | |
365 | FU_FIRMWARE_IMAGE_ID_SIGNATURE) == 0) | |
316 | 366 | record_type = DFU_INHX32_RECORD_TYPE_SIGNATURE; |
317 | 367 | |
318 | 368 | /* get number of chunks */ |
359 | 409 | } |
360 | 410 | |
361 | 411 | static void |
412 | fu_ihex_firmware_finalize (GObject *object) | |
413 | { | |
414 | FuIhexFirmware *self = FU_IHEX_FIRMWARE (object); | |
415 | g_ptr_array_unref (self->records); | |
416 | G_OBJECT_CLASS (fu_ihex_firmware_parent_class)->finalize (object); | |
417 | } | |
418 | ||
419 | static void | |
362 | 420 | fu_ihex_firmware_init (FuIhexFirmware *self) |
363 | 421 | { |
422 | self->records = g_ptr_array_new_with_free_func ((GFreeFunc) fu_ihex_firmware_record_free); | |
364 | 423 | } |
365 | 424 | |
366 | 425 | static void |
367 | 426 | fu_ihex_firmware_class_init (FuIhexFirmwareClass *klass) |
368 | 427 | { |
428 | GObjectClass *object_class = G_OBJECT_CLASS (klass); | |
369 | 429 | FuFirmwareClass *klass_firmware = FU_FIRMWARE_CLASS (klass); |
430 | object_class->finalize = fu_ihex_firmware_finalize; | |
370 | 431 | klass_firmware->parse = fu_ihex_firmware_parse; |
432 | klass_firmware->tokenize = fu_ihex_firmware_tokenize; | |
371 | 433 | klass_firmware->write = fu_ihex_firmware_write; |
372 | 434 | } |
373 | 435 |
10 | 10 | #define FU_TYPE_IHEX_FIRMWARE (fu_ihex_firmware_get_type ()) |
11 | 11 | G_DECLARE_FINAL_TYPE (FuIhexFirmware, fu_ihex_firmware, FU, IHEX_FIRMWARE, FuFirmware) |
12 | 12 | |
13 | typedef struct { | |
14 | guint ln; | |
15 | GString *buf; | |
16 | } FuIhexFirmwareRecord; | |
17 | ||
13 | 18 | FuFirmware *fu_ihex_firmware_new (void); |
19 | GPtrArray *fu_ihex_firmware_get_records (FuIhexFirmware *self); |
14 | 14 | #include <glib-unix.h> |
15 | 15 | #include <locale.h> |
16 | 16 | #include <polkit/polkit.h> |
17 | #include <stdio.h> | |
17 | 18 | #include <stdlib.h> |
18 | 19 | |
19 | 20 | #include "fwupd-device-private.h" |
1367 | 1368 | |
1368 | 1369 | if (g_strcmp0 (property_name, "HostMachineId") == 0) |
1369 | 1370 | return g_variant_new_string (fu_engine_get_host_machine_id (priv->engine)); |
1371 | ||
1372 | if (g_strcmp0 (property_name, "Interactive") == 0) | |
1373 | return g_variant_new_boolean (isatty (fileno (stdout)) != 0); | |
1370 | 1374 | |
1371 | 1375 | /* return an error */ |
1372 | 1376 | g_set_error (error, |
12 | 12 | #include <errno.h> |
13 | 13 | #include <string.h> |
14 | 14 | #include <unistd.h> |
15 | #include <gio/gunixinputstream.h> | |
16 | 15 | #ifdef HAVE_VALGRIND |
17 | 16 | #include <valgrind.h> |
18 | 17 | #endif /* HAVE_VALGRIND */ |
353 | 352 | g_set_error (error, |
354 | 353 | G_IO_ERROR, |
355 | 354 | G_IO_ERROR_FAILED, |
356 | "failed to open plugin: %s", | |
357 | g_module_error ()); | |
355 | "failed to open plugin %s: %s", | |
356 | filename, g_module_error ()); | |
358 | 357 | return FALSE; |
359 | 358 | } |
360 | 359 |
9 | 9 | #include <fwupd.h> |
10 | 10 | #include <glib-object.h> |
11 | 11 | #include <glib/gstdio.h> |
12 | #include <gio/gfiledescriptorbased.h> | |
13 | 12 | #include <libgcab.h> |
14 | 13 | #include <stdlib.h> |
15 | 14 | #include <string.h> |
558 | 557 | |
559 | 558 | /* check this passes */ |
560 | 559 | task = fu_install_task_new (device1, component); |
560 | ret = fu_engine_check_requirements (engine, task, | |
561 | FWUPD_INSTALL_FLAG_NONE, | |
562 | &error); | |
563 | g_assert_no_error (error); | |
564 | g_assert (ret); | |
565 | } | |
566 | ||
567 | static void | |
568 | fu_engine_requirements_parent_device_func (void) | |
569 | { | |
570 | gboolean ret; | |
571 | g_autoptr(FuDevice) device1 = fu_device_new (); | |
572 | g_autoptr(FuDevice) device2 = fu_device_new (); | |
573 | g_autoptr(FuEngine) engine = fu_engine_new (FU_APP_FLAGS_NONE); | |
574 | g_autoptr(FuInstallTask) task = NULL; | |
575 | g_autoptr(GError) error = NULL; | |
576 | g_autoptr(XbNode) component = NULL; | |
577 | g_autoptr(XbSilo) silo = NULL; | |
578 | g_autoptr(XbSilo) silo_empty = xb_silo_new (); | |
579 | const gchar *xml = | |
580 | "<component>" | |
581 | " <requires>" | |
582 | " <firmware depth=\"1\" compare=\"eq\" version=\"1.2.3\"/>" | |
583 | " <firmware depth=\"1\">12345678-1234-1234-1234-123456789012</firmware>" | |
584 | " </requires>" | |
585 | " <provides>" | |
586 | " <firmware type=\"flashed\">1ff60ab2-3905-06a1-b476-0371f00c9e9b</firmware>" | |
587 | " </provides>" | |
588 | " <releases>" | |
589 | " <release version=\"4.5.7\">" | |
590 | " <checksum type=\"sha1\" filename=\"bios.bin\" target=\"content\"/>" | |
591 | " </release>" | |
592 | " </releases>" | |
593 | "</component>"; | |
594 | ||
595 | /* no metadata in daemon */ | |
596 | fu_engine_set_silo (engine, silo_empty); | |
597 | ||
598 | /* set up child device */ | |
599 | fu_device_set_id (device2, "child"); | |
600 | fu_device_set_name (device2, "child"); | |
601 | fu_device_set_version (device2, "4.5.6", FWUPD_VERSION_FORMAT_TRIPLET); | |
602 | fu_device_add_flag (device2, FWUPD_DEVICE_FLAG_UPDATABLE); | |
603 | fu_device_add_guid (device2, "1ff60ab2-3905-06a1-b476-0371f00c9e9b"); | |
604 | ||
605 | /* set up a parent device */ | |
606 | fu_device_set_id (device1, "parent"); | |
607 | fu_device_set_name (device1, "parent"); | |
608 | fu_device_set_version (device1, "1.2.3", FWUPD_VERSION_FORMAT_TRIPLET); | |
609 | fu_device_add_guid (device1, "12345678-1234-1234-1234-123456789012"); | |
610 | fu_device_add_child (device1, device2); | |
611 | fu_engine_add_device (engine, device1); | |
612 | ||
613 | /* import firmware metainfo */ | |
614 | silo = xb_silo_new_from_xml (xml, &error); | |
615 | g_assert_no_error (error); | |
616 | g_assert_nonnull (silo); | |
617 | component = xb_silo_query_first (silo, "component", &error); | |
618 | g_assert_no_error (error); | |
619 | g_assert_nonnull (component); | |
620 | ||
621 | /* check this passes */ | |
622 | task = fu_install_task_new (device2, component); | |
561 | 623 | ret = fu_engine_check_requirements (engine, task, |
562 | 624 | FWUPD_INSTALL_FLAG_NONE, |
563 | 625 | &error); |
3612 | 3674 | { 0xffffffff, "18.31.255.65535", FWUPD_VERSION_FORMAT_INTEL_ME }, |
3613 | 3675 | { 0x0b32057a, "11.11.50.1402", FWUPD_VERSION_FORMAT_INTEL_ME }, |
3614 | 3676 | { 0xb8320d84, "11.8.50.3460", FWUPD_VERSION_FORMAT_INTEL_ME2 }, |
3677 | { 0x226a4b00, "137.2706.768", FWUPD_VERSION_FORMAT_SURFACE_LEGACY }, | |
3678 | { 0x6001988, "6.25.136", FWUPD_VERSION_FORMAT_SURFACE }, | |
3615 | 3679 | { 0, NULL } |
3616 | 3680 | }; |
3617 | 3681 | struct { |
3813 | 3877 | g_assert_cmpint (g_bytes_get_size (data_fw), ==, 136); |
3814 | 3878 | |
3815 | 3879 | /* get the signed image */ |
3816 | data_sig = fu_firmware_get_image_by_id_bytes (firmware, "signature", &error); | |
3880 | data_sig = fu_firmware_get_image_by_id_bytes (firmware, | |
3881 | FU_FIRMWARE_IMAGE_ID_SIGNATURE, | |
3882 | &error); | |
3817 | 3883 | g_assert_no_error (error); |
3818 | 3884 | g_assert_nonnull (data_sig); |
3819 | 3885 | data = g_bytes_get_data (data_sig, &len); |
4163 | 4229 | g_test_add_func ("/fwupd/engine{downgrade}", fu_engine_downgrade_func); |
4164 | 4230 | g_test_add_func ("/fwupd/engine{requirements-success}", fu_engine_requirements_func); |
4165 | 4231 | g_test_add_func ("/fwupd/engine{requirements-missing}", fu_engine_requirements_missing_func); |
4232 | g_test_add_func ("/fwupd/engine{requirements-parent-device}", fu_engine_requirements_parent_device_func); | |
4166 | 4233 | g_test_add_func ("/fwupd/engine{requirements-not-child}", fu_engine_requirements_child_func); |
4167 | 4234 | g_test_add_func ("/fwupd/engine{requirements-not-child-fail}", fu_engine_requirements_child_fail_func); |
4168 | 4235 | g_test_add_func ("/fwupd/engine{requirements-unsupported}", fu_engine_requirements_unsupported_func); |
1013 | 1013 | static gboolean |
1014 | 1014 | fu_util_update (FuUtilPrivate *priv, gchar **values, GError **error) |
1015 | 1015 | { |
1016 | if (priv->flags & FWUPD_INSTALL_FLAG_ALLOW_OLDER) { | |
1017 | g_set_error_literal (error, | |
1018 | FWUPD_ERROR, | |
1019 | FWUPD_ERROR_INVALID_ARGS, | |
1020 | "--allow-older is not supported for this command"); | |
1021 | return FALSE; | |
1022 | } | |
1023 | ||
1024 | if (priv->flags & FWUPD_INSTALL_FLAG_ALLOW_REINSTALL) { | |
1025 | g_set_error_literal (error, | |
1026 | FWUPD_ERROR, | |
1027 | FWUPD_ERROR_INVALID_ARGS, | |
1028 | "--allow-reinstall is not supported for this command"); | |
1029 | return FALSE; | |
1030 | } | |
1031 | ||
1016 | 1032 | if (g_strv_length (values) > 1) { |
1017 | 1033 | g_set_error_literal (error, |
1018 | 1034 | FWUPD_ERROR, |
32 | 32 | typedef struct |
33 | 33 | { |
34 | 34 | GUdevDevice *udev_device; |
35 | guint16 vendor; | |
36 | guint16 model; | |
35 | guint32 vendor; | |
36 | guint32 model; | |
37 | 37 | guint8 revision; |
38 | 38 | gchar *subsystem; |
39 | 39 | gchar *device_file; |
42 | 42 | } FuUdevDevicePrivate; |
43 | 43 | |
44 | 44 | G_DEFINE_TYPE_WITH_PRIVATE (FuUdevDevice, fu_udev_device, FU_TYPE_DEVICE) |
45 | ||
46 | #ifndef HAVE_GUDEV_232 | |
47 | #pragma clang diagnostic push | |
48 | #pragma clang diagnostic ignored "-Wunused-function" | |
49 | G_DEFINE_AUTOPTR_CLEANUP_FUNC(GUdevDevice, g_object_unref) | |
50 | #pragma clang diagnostic pop | |
51 | #endif | |
52 | 45 | |
53 | 46 | enum { |
54 | 47 | PROP_0, |
83 | 76 | g_signal_emit (self, signals[SIGNAL_CHANGED], 0); |
84 | 77 | } |
85 | 78 | |
86 | static guint64 | |
87 | fu_udev_device_get_sysfs_attr_as_uint64 (GUdevDevice *udev_device, const gchar *name) | |
88 | { | |
89 | return fu_common_strtoull (g_udev_device_get_sysfs_attr (udev_device, name)); | |
90 | } | |
91 | ||
92 | static guint16 | |
93 | fu_udev_device_read_uint16 (const gchar *str) | |
94 | { | |
95 | gchar buf[5] = { 0x0, 0x0, 0x0, 0x0, 0x0 }; | |
96 | memcpy (buf, str, 4); | |
97 | return (guint16) g_ascii_strtoull (buf, NULL, 16); | |
79 | static guint32 | |
80 | fu_udev_device_get_sysfs_attr_as_uint32 (GUdevDevice *udev_device, const gchar *name) | |
81 | { | |
82 | guint64 tmp = fu_common_strtoull (g_udev_device_get_sysfs_attr (udev_device, name)); | |
83 | if (tmp > G_MAXUINT32) { | |
84 | g_warning ("reading %s for %s overflowed", | |
85 | name, | |
86 | g_udev_device_get_sysfs_path (udev_device)); | |
87 | return G_MAXUINT32; | |
88 | } | |
89 | return tmp; | |
90 | } | |
91 | ||
92 | static guint8 | |
93 | fu_udev_device_get_sysfs_attr_as_uint8 (GUdevDevice *udev_device, const gchar *name) | |
94 | { | |
95 | guint64 tmp = fu_common_strtoull (g_udev_device_get_sysfs_attr (udev_device, name)); | |
96 | if (tmp > G_MAXUINT8) { | |
97 | g_warning ("reading %s for %s overflowed", | |
98 | name, | |
99 | g_udev_device_get_sysfs_path (udev_device)); | |
100 | return G_MAXUINT8; | |
101 | } | |
102 | return tmp; | |
98 | 103 | } |
99 | 104 | |
100 | 105 | static void |
101 | 106 | fu_udev_device_dump_internal (GUdevDevice *udev_device) |
102 | 107 | { |
103 | #ifdef HAVE_GUDEV_232 | |
104 | 108 | const gchar * const *keys; |
105 | 109 | |
106 | 110 | keys = g_udev_device_get_property_keys (udev_device); |
113 | 117 | g_debug ("%s=[%s]", keys[i], |
114 | 118 | g_udev_device_get_sysfs_attr (udev_device, keys[i])); |
115 | 119 | } |
116 | #endif | |
117 | 120 | } |
118 | 121 | |
119 | 122 | void |
141 | 144 | return TRUE; |
142 | 145 | |
143 | 146 | /* set ven:dev:rev */ |
144 | priv->vendor = fu_udev_device_get_sysfs_attr_as_uint64 (priv->udev_device, "vendor"); | |
145 | priv->model = fu_udev_device_get_sysfs_attr_as_uint64 (priv->udev_device, "device"); | |
146 | priv->revision = fu_udev_device_get_sysfs_attr_as_uint64 (priv->udev_device, "revision"); | |
147 | priv->vendor = fu_udev_device_get_sysfs_attr_as_uint32 (priv->udev_device, "vendor"); | |
148 | priv->model = fu_udev_device_get_sysfs_attr_as_uint32 (priv->udev_device, "device"); | |
149 | priv->revision = fu_udev_device_get_sysfs_attr_as_uint8 (priv->udev_device, "revision"); | |
147 | 150 | |
148 | 151 | /* fallback to the parent */ |
149 | 152 | udev_parent = g_udev_device_get_parent (priv->udev_device); |
150 | 153 | if (udev_parent != NULL && |
151 | 154 | priv->vendor == 0x0 && priv->model == 0x0 && priv->revision == 0x0) { |
152 | priv->vendor = fu_udev_device_get_sysfs_attr_as_uint64 (udev_parent, "vendor"); | |
153 | priv->model = fu_udev_device_get_sysfs_attr_as_uint64 (udev_parent, "device"); | |
154 | priv->revision = fu_udev_device_get_sysfs_attr_as_uint64 (udev_parent, "revision"); | |
155 | priv->vendor = fu_udev_device_get_sysfs_attr_as_uint32 (udev_parent, "vendor"); | |
156 | priv->model = fu_udev_device_get_sysfs_attr_as_uint32 (udev_parent, "device"); | |
157 | priv->revision = fu_udev_device_get_sysfs_attr_as_uint8 (udev_parent, "revision"); | |
155 | 158 | } |
156 | 159 | |
157 | 160 | /* hidraw helpfully encodes the information in a different place */ |
159 | 162 | priv->vendor == 0x0 && priv->model == 0x0 && priv->revision == 0x0 && |
160 | 163 | g_strcmp0 (priv->subsystem, "hidraw") == 0) { |
161 | 164 | tmp = g_udev_device_get_property (udev_parent, "HID_ID"); |
162 | if (tmp != NULL && strlen (tmp) == 22) { | |
163 | priv->vendor = fu_udev_device_read_uint16 (tmp + 10); | |
164 | priv->model = fu_udev_device_read_uint16 (tmp + 18); | |
165 | if (tmp != NULL) { | |
166 | g_auto(GStrv) split = g_strsplit (tmp, ":", -1); | |
167 | if (g_strv_length (split) == 3) { | |
168 | guint64 val = g_ascii_strtoull (split[1], NULL, 16); | |
169 | if (val > G_MAXUINT32) { | |
170 | g_warning ("reading %s for %s overflowed", | |
171 | split[1], | |
172 | g_udev_device_get_sysfs_path (priv->udev_device)); | |
173 | } else { | |
174 | priv->vendor = val; | |
175 | } | |
176 | val = g_ascii_strtoull (split[2], NULL, 16); | |
177 | if (val > G_MAXUINT32) { | |
178 | g_warning ("reading %s for %s overflowed", | |
179 | split[2], | |
180 | g_udev_device_get_sysfs_path (priv->udev_device)); | |
181 | } else { | |
182 | priv->model = val; | |
183 | } | |
184 | } | |
165 | 185 | } |
166 | 186 | tmp = g_udev_device_get_property (udev_parent, "HID_NAME"); |
167 | 187 | if (tmp != NULL) { |
422 | 442 | * |
423 | 443 | * Since: 1.1.2 |
424 | 444 | **/ |
425 | guint16 | |
445 | guint32 | |
426 | 446 | fu_udev_device_get_vendor (FuUdevDevice *self) |
427 | 447 | { |
428 | 448 | FuUdevDevicePrivate *priv = GET_PRIVATE (self); |
440 | 460 | * |
441 | 461 | * Since: 1.1.2 |
442 | 462 | **/ |
443 | guint16 | |
463 | guint32 | |
444 | 464 | fu_udev_device_get_model (FuUdevDevice *self) |
445 | 465 | { |
446 | 466 | FuUdevDevicePrivate *priv = GET_PRIVATE (self); |
615 | 635 | FuUdevDevicePrivate *priv = GET_PRIVATE (self); |
616 | 636 | |
617 | 637 | g_return_if_fail (FU_IS_UDEV_DEVICE (self)); |
618 | g_return_if_fail (fd > 0); | |
619 | ||
620 | 638 | if (priv->fd > 0) |
621 | 639 | close (priv->fd); |
622 | 640 | priv->fd = fd; |
648 | 666 | FuUdevDeviceClass *klass = FU_UDEV_DEVICE_GET_CLASS (device); |
649 | 667 | |
650 | 668 | /* open device */ |
651 | priv->fd = g_open (priv->device_file, priv->readonly ? O_RDONLY : O_RDWR); | |
652 | if (priv->fd < 0) { | |
653 | g_set_error (error, | |
654 | G_IO_ERROR, | |
655 | G_IO_ERROR_FAILED, | |
656 | "failed to open %s: %s", | |
657 | priv->device_file, | |
658 | strerror (errno)); | |
659 | return FALSE; | |
669 | if (priv->device_file != NULL) { | |
670 | priv->fd = g_open (priv->device_file, priv->readonly ? O_RDONLY : O_RDWR); | |
671 | if (priv->fd < 0) { | |
672 | g_set_error (error, | |
673 | G_IO_ERROR, | |
674 | G_IO_ERROR_FAILED, | |
675 | "failed to open %s: %s", | |
676 | priv->device_file, | |
677 | strerror (errno)); | |
678 | return FALSE; | |
679 | } | |
660 | 680 | } |
661 | 681 | |
662 | 682 | /* subclassed */ |
720 | 740 | g_return_val_if_fail (FU_IS_UDEV_DEVICE (self), FALSE); |
721 | 741 | g_return_val_if_fail (request != 0x0, FALSE); |
722 | 742 | g_return_val_if_fail (buf != NULL, FALSE); |
743 | g_return_val_if_fail (priv->fd > 0, FALSE); | |
723 | 744 | |
724 | 745 | rc_tmp = ioctl (priv->fd, request, buf); |
725 | 746 | if (rc != NULL) |
762 | 783 | |
763 | 784 | g_return_val_if_fail (FU_IS_UDEV_DEVICE (self), FALSE); |
764 | 785 | g_return_val_if_fail (port != 0x0, FALSE); |
786 | g_return_val_if_fail (priv->fd > 0, FALSE); | |
765 | 787 | |
766 | 788 | if (pwrite (priv->fd, &data, 1, port) != 1) { |
767 | 789 | g_set_error (error, |
796 | 818 | g_return_val_if_fail (FU_IS_UDEV_DEVICE (self), FALSE); |
797 | 819 | g_return_val_if_fail (port != 0x0, FALSE); |
798 | 820 | g_return_val_if_fail (data != NULL, FALSE); |
821 | g_return_val_if_fail (priv->fd > 0, FALSE); | |
799 | 822 | |
800 | 823 | if (pread (priv->fd, data, 1, port) != 1) { |
801 | 824 | g_set_error (error, |
30 | 30 | const gchar *fu_udev_device_get_device_file (FuUdevDevice *self); |
31 | 31 | const gchar *fu_udev_device_get_sysfs_path (FuUdevDevice *self); |
32 | 32 | const gchar *fu_udev_device_get_subsystem (FuUdevDevice *self); |
33 | guint16 fu_udev_device_get_vendor (FuUdevDevice *self); | |
34 | guint16 fu_udev_device_get_model (FuUdevDevice *self); | |
33 | guint32 fu_udev_device_get_vendor (FuUdevDevice *self); | |
34 | guint32 fu_udev_device_get_model (FuUdevDevice *self); | |
35 | 35 | guint8 fu_udev_device_get_revision (FuUdevDevice *self); |
36 | 36 | guint fu_udev_device_get_slot_depth (FuUdevDevice *self, |
37 | 37 | const gchar *subsystem); |
11 | 11 | #include "fu-usb-device-private.h" |
12 | 12 | |
13 | 13 | /** |
14 | * SECTION:fu-device | |
14 | * SECTION:fu-usb-device | |
15 | 15 | * @short_description: a USB device |
16 | 16 | * |
17 | 17 | * An object that represents a USB device. |
128 | 128 | idx = g_usb_device_get_manufacturer_index (priv->usb_device); |
129 | 129 | if (idx != 0x00) { |
130 | 130 | g_autofree gchar *tmp = NULL; |
131 | g_autoptr(GError) error_local = NULL; | |
131 | 132 | tmp = g_usb_device_get_string_descriptor (priv->usb_device, |
132 | idx, error); | |
133 | if (tmp == NULL) | |
134 | return FALSE; | |
135 | fu_device_set_vendor (device, g_strchomp (tmp)); | |
133 | idx, &error_local); | |
134 | if (tmp != NULL) | |
135 | fu_device_set_vendor (device, g_strchomp (tmp)); | |
136 | else | |
137 | g_debug ("failed to load manufacturer string for usb device %u:%u: %s", | |
138 | g_usb_device_get_bus (priv->usb_device), | |
139 | g_usb_device_get_address (priv->usb_device), | |
140 | error_local->message); | |
136 | 141 | } |
137 | 142 | } |
138 | 143 | |
141 | 146 | idx = g_usb_device_get_product_index (priv->usb_device); |
142 | 147 | if (idx != 0x00) { |
143 | 148 | g_autofree gchar *tmp = NULL; |
149 | g_autoptr(GError) error_local = NULL; | |
144 | 150 | tmp = g_usb_device_get_string_descriptor (priv->usb_device, |
145 | idx, error); | |
146 | if (tmp == NULL) | |
147 | return FALSE; | |
148 | fu_device_set_name (device, g_strchomp (tmp)); | |
151 | idx, &error_local); | |
152 | if (tmp != NULL) | |
153 | fu_device_set_name (device, g_strchomp (tmp)); | |
154 | else | |
155 | g_debug ("failed to load product string for usb device %u:%u: %s", | |
156 | g_usb_device_get_bus (priv->usb_device), | |
157 | g_usb_device_get_address (priv->usb_device), | |
158 | error_local->message); | |
149 | 159 | } |
150 | 160 | } |
151 | 161 | |
154 | 164 | idx = g_usb_device_get_serial_number_index (priv->usb_device); |
155 | 165 | if (idx != 0x00) { |
156 | 166 | g_autofree gchar *tmp = NULL; |
167 | g_autoptr(GError) error_local = NULL; | |
157 | 168 | tmp = g_usb_device_get_string_descriptor (priv->usb_device, |
158 | idx, error); | |
159 | if (tmp == NULL) | |
160 | return FALSE; | |
161 | fu_device_set_serial (device, g_strchomp (tmp)); | |
169 | idx, &error_local); | |
170 | if (tmp != NULL) | |
171 | fu_device_set_serial (device, g_strchomp (tmp)); | |
172 | else | |
173 | g_debug ("failed to load serial number string for usb device %u:%u: %s", | |
174 | g_usb_device_get_bus (priv->usb_device), | |
175 | g_usb_device_get_address (priv->usb_device), | |
176 | error_local->message); | |
162 | 177 | } |
163 | 178 | } |
164 | 179 | |
357 | 372 | } |
358 | 373 | |
359 | 374 | /** |
375 | * fu_usb_device_get_spec: | |
376 | * @self: A #FuUsbDevice | |
377 | * | |
378 | * Gets the string USB revision for the device. | |
379 | * | |
380 | * Return value: a specification revision in BCD format, or 0x0 if not supported | |
381 | * | |
382 | * Since: 1.3.4 | |
383 | **/ | |
384 | guint16 | |
385 | fu_usb_device_get_spec (FuUsbDevice *self) | |
386 | { | |
387 | #if G_USB_CHECK_VERSION(0,3,1) | |
388 | FuUsbDevicePrivate *priv = GET_PRIVATE (self); | |
389 | g_return_val_if_fail (FU_IS_USB_DEVICE (self), 0x0); | |
390 | if (priv->usb_device == NULL) | |
391 | return 0x0; | |
392 | return g_usb_device_get_spec (priv->usb_device); | |
393 | #else | |
394 | return 0x0; | |
395 | #endif | |
396 | } | |
397 | ||
398 | /** | |
360 | 399 | * fu_usb_device_set_dev: |
361 | 400 | * @device: A #FuUsbDevice |
362 | 401 | * @usb_device: A #GUsbDevice, or %NULL |
39 | 39 | FuUsbDevice *fu_usb_device_new (GUsbDevice *usb_device); |
40 | 40 | guint16 fu_usb_device_get_vid (FuUsbDevice *self); |
41 | 41 | guint16 fu_usb_device_get_pid (FuUsbDevice *self); |
42 | guint16 fu_usb_device_get_spec (FuUsbDevice *self); | |
42 | 43 | GUsbDevice *fu_usb_device_get_dev (FuUsbDevice *device); |
43 | 44 | void fu_usb_device_set_dev (FuUsbDevice *device, |
44 | 45 | GUsbDevice *usb_device); |
251 | 251 | } |
252 | 252 | |
253 | 253 | gchar * |
254 | fu_util_get_versions (void) | |
254 | fu_util_get_client_version (void) | |
255 | 255 | { |
256 | 256 | GString *string = g_string_new (""); |
257 | 257 | |
258 | 258 | g_string_append_printf (string, |
259 | "client version:\t%i.%i.%i\n", | |
259 | "%i.%i.%i", | |
260 | 260 | FWUPD_MAJOR_VERSION, |
261 | 261 | FWUPD_MINOR_VERSION, |
262 | 262 | FWUPD_MICRO_VERSION); |
263 | #ifdef FWUPD_GIT_DESCRIBE | |
263 | #ifdef FWUPD_DIRTY_VERSION | |
264 | 264 | g_string_append_printf (string, |
265 | "checkout info:\t%s\n", FWUPD_GIT_DESCRIBE); | |
265 | "-%i-%s", | |
266 | FWUPD_DIRTY_VERSION, | |
267 | FWUPD_COMMIT_VERSION); | |
266 | 268 | #endif |
269 | return g_string_free (string, FALSE); | |
270 | } | |
271 | ||
272 | gchar * | |
273 | fu_util_get_versions (void) | |
274 | { | |
275 | GString *string = g_string_new (""); | |
276 | g_autofree gchar *client_version = fu_util_get_client_version (); | |
277 | ||
278 | g_string_append_printf (string, "client version:\t%s\n", client_version); | |
267 | 279 | g_string_append_printf (string, |
268 | 280 | "compile-time dependency versions\n"); |
269 | 281 | g_string_append_printf (string, |
33 | 33 | gchar *fu_util_get_user_cache_path (const gchar *fn); |
34 | 34 | SoupSession *fu_util_setup_networking (GError **error); |
35 | 35 | |
36 | gchar *fu_util_get_client_version (void); | |
36 | 37 | gchar *fu_util_get_versions (void); |
37 | 38 | |
38 | 39 | void fu_util_warning_box (const gchar *str, |
1913 | 1913 | static gboolean |
1914 | 1914 | fu_util_update (FuUtilPrivate *priv, gchar **values, GError **error) |
1915 | 1915 | { |
1916 | if (priv->flags & FWUPD_INSTALL_FLAG_ALLOW_OLDER) { | |
1917 | g_set_error_literal (error, | |
1918 | FWUPD_ERROR, | |
1919 | FWUPD_ERROR_INVALID_ARGS, | |
1920 | "--allow-older is not supported for this command"); | |
1921 | return FALSE; | |
1922 | } | |
1923 | ||
1924 | if (priv->flags & FWUPD_INSTALL_FLAG_ALLOW_REINSTALL) { | |
1925 | g_set_error_literal (error, | |
1926 | FWUPD_ERROR, | |
1927 | FWUPD_ERROR_INVALID_ARGS, | |
1928 | "--allow-reinstall is not supported for this command"); | |
1929 | return FALSE; | |
1930 | } | |
1931 | ||
1916 | 1932 | if (g_strv_length (values) == 0) |
1917 | 1933 | return fu_util_update_all (priv, error); |
1918 | 1934 | if (g_strv_length (values) == 1) |
1985 | 2001 | g_autoptr(FwupdDevice) dev = NULL; |
1986 | 2002 | g_autoptr(FwupdRelease) rel = NULL; |
1987 | 2003 | g_autoptr(GPtrArray) rels = NULL; |
2004 | ||
2005 | if (priv->flags & FWUPD_INSTALL_FLAG_ALLOW_REINSTALL) { | |
2006 | g_set_error_literal (error, | |
2007 | FWUPD_ERROR, | |
2008 | FWUPD_ERROR_INVALID_ARGS, | |
2009 | "--allow-reinstall is not supported for this command"); | |
2010 | return FALSE; | |
2011 | } | |
1988 | 2012 | |
1989 | 2013 | priv->filter_include |= FWUPD_DEVICE_FLAG_SUPPORTED; |
1990 | 2014 | dev = fu_util_get_device_or_prompt (priv, values, error); |
2275 | 2299 | static gboolean |
2276 | 2300 | fu_util_check_daemon_version (FuUtilPrivate *priv, GError **error) |
2277 | 2301 | { |
2278 | g_autofree gchar *client = g_strdup_printf ("%i.%i.%i", | |
2279 | FWUPD_MAJOR_VERSION, | |
2280 | FWUPD_MINOR_VERSION, | |
2281 | FWUPD_MICRO_VERSION); | |
2302 | g_autofree gchar *client = fu_util_get_client_version (); | |
2282 | 2303 | const gchar *daemon = fwupd_client_get_daemon_version (priv->client); |
2283 | 2304 | |
2284 | 2305 | if (g_strcmp0 (daemon, client) != 0) { |
2721 | 2742 | #ifdef HAVE_SYSTEMD |
2722 | 2743 | /* make sure the correct daemon is in use */ |
2723 | 2744 | if ((priv->flags & FWUPD_INSTALL_FLAG_FORCE) == 0 && |
2745 | !fwupd_client_get_daemon_interactive (priv->client) && | |
2724 | 2746 | !fu_util_using_correct_daemon (&error)) { |
2725 | 2747 | g_printerr ("%s\n", error->message); |
2726 | 2748 | return EXIT_FAILURE; |
55 | 55 | </property> |
56 | 56 | |
57 | 57 | <!--***********************************************************--> |
58 | <property name='Interactive' type='b' access='read'> | |
59 | <doc:doc> | |
60 | <doc:description> | |
61 | <doc:para> | |
62 | If the daemon is running on an interactive terminal. | |
63 | </doc:para> | |
64 | </doc:description> | |
65 | </doc:doc> | |
66 | </property> | |
67 | ||
68 | <!--***********************************************************--> | |
58 | 69 | <property name='Status' type='u' access='read'> |
59 | 70 | <doc:doc> |
60 | 71 | <doc:description> |