New upstream release (v0.6.0)
Mario Limonciello
8 years ago
0 | Version 0.5.4 | |
0 | Version 0.6.0 | |
1 | 1 | ~~~~~~~~~~~~~ |
2 | Released: 2015-11-18 | |
2 | Released: 2015-12-07 | |
3 | ||
4 | Notes: | |
5 | - This release adds a new GObject library called libdfu and a command line | |
6 | client called dfu-tool. This is a low-level tool used to upgrade USB device | |
7 | firmware and can either be shipped in the same package as fwupd or split off | |
8 | as separate subpackages. | |
3 | 9 | |
4 | 10 | New Features: |
5 | - Use API available in fwupdate 0.5 to avoid writing temp files (Richard Hughes) | |
11 | - Add support for automatically updating USB DFU-capable devices (Richard Hughes) | |
6 | 12 | |
7 | 13 | Bugfixes: |
8 | - Fix compile error against fwupdate 0.5 due to API bump (Mario Limonciello) | |
14 | - Emit the changed signal after doing an update (Richard Hughes) | |
15 | - Export the AppStream ID when returning device results (Richard Hughes) | |
16 | - Fix compile with --disable-shared (Richard Hughes) | |
17 | - Use new API available in fwup 0.5 (Richard Hughes, Mario Limonciello) | |
18 | - Use the same device identification string format as Microsoft (Richard Hughes) | |
9 | 19 | |
10 | 20 | Version 0.5.3 |
11 | 21 | ~~~~~~~~~~~~~ |
3 | 3 | |
4 | 4 | git shortlog 0.5.3.. | grep -i -v trivial | grep -v Merge > NEWS.new |
5 | 5 | |
6 | Version 0.5.4 | |
6 | Version 0.6.0 | |
7 | 7 | ~~~~~~~~~~~~~ |
8 | 8 | Released: 2015-xx-xx |
9 | 9 | |
22 | 22 | 2. Commit changes to git: |
23 | 23 | |
24 | 24 | # MAKE SURE THESE ARE CORRECT |
25 | export release_ver="0.5.4" | |
25 | export release_ver="0.6.0" | |
26 | 26 | |
27 | 27 | git commit -a -m "Release fwupd ${release_ver}" |
28 | 28 | git tag -s -f -m "Release fwupd ${release_ver}" "${release_ver}" |
21 | 21 | fi |
22 | 22 | |
23 | 23 | autopoint --force |
24 | gtkdocize || exit 1 | |
24 | 25 | ACLOCAL="${ACLOCAL-aclocal} $ACLOCAL_FLAGS" AUTOPOINT='intltoolize --automake --copy' autoreconf --force --install --verbose |
25 | 26 | |
26 | 27 | cd "$olddir" |
1 | 1 | AC_PREREQ(2.63) |
2 | 2 | |
3 | 3 | m4_define([fwupd_major_version], [0]) |
4 | m4_define([fwupd_minor_version], [5]) | |
5 | m4_define([fwupd_micro_version], [4]) | |
4 | m4_define([fwupd_minor_version], [6]) | |
5 | m4_define([fwupd_micro_version], [0]) | |
6 | 6 | m4_define([fwupd_version], |
7 | 7 | [fwupd_major_version.fwupd_minor_version.fwupd_micro_version]) |
8 | 8 | |
43 | 43 | |
44 | 44 | # enable nice build output on automake1.11 |
45 | 45 | m4_ifdef([AM_SILENT_RULES],[AM_SILENT_RULES([yes])]) |
46 | ||
47 | # check for gtk-doc | |
48 | GTK_DOC_CHECK([1.14],[--flavour no-tmpl]) | |
46 | 49 | |
47 | 50 | AS_ALL_LINGUAS |
48 | 51 | AC_PROG_CC |
111 | 114 | [RELRO_LDFLAGS="-Wl,-z,relro,-z,now"]) |
112 | 115 | AC_SUBST([RELRO_LDFLAGS]) |
113 | 116 | |
117 | # use -lm | |
118 | LT_LIB_M | |
119 | AC_SUBST(LIBM) | |
120 | ||
114 | 121 | dnl --------------------------------------------------------------------------- |
115 | 122 | dnl - Check library dependencies |
116 | 123 | dnl --------------------------------------------------------------------------- |
118 | 125 | PKG_CHECK_MODULES(GUDEV, gudev-1.0) |
119 | 126 | PKG_CHECK_MODULES(POLKIT, polkit-gobject-1 >= 0.103) |
120 | 127 | PKG_CHECK_MODULES(GCAB, libgcab-1.0) |
121 | PKG_CHECK_MODULES(APPSTREAM_GLIB, appstream-glib >= 0.5.2) | |
122 | PKG_CHECK_MODULES(GUSB, gusb >= 0.2.2) | |
128 | PKG_CHECK_MODULES(APPSTREAM_GLIB, appstream-glib >= 0.5.4) | |
129 | PKG_CHECK_MODULES(GUSB, gusb >= 0.2.8) | |
123 | 130 | PKG_CHECK_MODULES(SQLITE, sqlite3) |
124 | 131 | PKG_CHECK_MODULES(ARCHIVE, libarchive) |
125 | 132 | PKG_CHECK_MODULES(SOUP, libsoup-2.4 >= 2.51.92) |
137 | 144 | AC_ARG_ENABLE(colorhug, AS_HELP_STRING([--enable-colorhug],[Enable ColorHug support]), |
138 | 145 | enable_colorhug=$enableval, enable_colorhug=yes) |
139 | 146 | if test x$enable_colorhug != xno; then |
140 | PKG_CHECK_MODULES(COLORHUG, colorhug >= 1.2.9) | |
147 | PKG_CHECK_MODULES(COLORHUG, colorhug >= 1.2.12) | |
141 | 148 | AC_DEFINE(HAVE_COLORHUG,1,[Use ColorHug support]) |
142 | 149 | fi |
143 | 150 | AM_CONDITIONAL(HAVE_COLORHUG, test x$enable_colorhug = xyes) |
184 | 191 | dnl --------------------------------------------------------------------------- |
185 | 192 | AC_CONFIG_FILES([ |
186 | 193 | Makefile |
194 | libdfu/Makefile | |
195 | libdfu/dfu.pc | |
187 | 196 | libfwupd/fwupd-version.h |
188 | 197 | libfwupd/fwupd.pc |
189 | 198 | libfwupd/Makefile |
192 | 201 | data/tests/Makefile |
193 | 202 | data/tests/rpiupdate/Makefile |
194 | 203 | data/tests/colorhug/Makefile |
204 | data/tests/dfu/Makefile | |
195 | 205 | docs/Makefile |
206 | docs/libdfu/Makefile | |
196 | 207 | docs/man/Makefile |
197 | 208 | policy/Makefile |
198 | 209 | po/Makefile.in |
83 | 83 | %dir %{_libexecdir}/fwupd |
84 | 84 | %{_libexecdir}/fwupd/fwupd |
85 | 85 | %{_bindir}/fwupdmgr |
86 | %{_bindir}/dfu-tool | |
86 | 87 | %{_sysconfdir}/pki/fwupd |
87 | 88 | %{_sysconfdir}/pki/fwupd-metadata |
88 | 89 | %{_sysconfdir}/dbus-1/system.d/org.freedesktop.fwupd.conf |
91 | 92 | %{_datadir}/polkit-1/rules.d/org.freedesktop.fwupd.rules |
92 | 93 | %{_datadir}/dbus-1/system-services/org.freedesktop.fwupd.service |
93 | 94 | %{_datadir}/man/man1/fwupdmgr.1.gz |
95 | %{_datadir}/man/man1/dfu-tool.1.gz | |
96 | %{_datadir}/gtk-doc/html/libdfu | |
94 | 97 | %{_unitdir}/fwupd-offline-update.service |
95 | 98 | %{_unitdir}/fwupd.service |
96 | 99 | %{_unitdir}/system-update.target.wants/ |
0 | EXTRA_DIST = \ | |
1 | example.bin \ | |
2 | example.dfu \ | |
3 | example.xdfu \ | |
4 | firmware.bin \ | |
5 | firmware.hex \ | |
6 | metadata.dfu \ | |
7 | kiibohd.dfu.bin \ | |
8 | dev_VRBRAIN.dfu | |
9 | ||
10 | -include $(top_srcdir)/git.mk |
Binary diff not shown
0 | hello world |
Binary diff not shown
Binary diff not shown
Binary diff not shown
0 | :044000003DEF20F080 | |
1 | :10400800FACF01F0FBCF02F0E9CF03F0EACF04F0DA | |
2 | :10401800E1CF05F0E2CF06F0D9CF07F0DACF08F00C | |
3 | :10402800F3CF09F0F4CF0AF0F6CF0BF0F7CF0CF08E | |
4 | :10403800F8CF0DF0F5CF0EF00EC0F5FF0DC0F8FF6C | |
5 | :104048000CC0F7FF0BC0F6FF0AC0F4FF09C0F3FF6E | |
6 | :1040580008C0DAFF07C0D9FF06C0E2FF05C0E1FFCC | |
7 | :1040680004C0EAFF03C0E9FF02C0FBFF01C0FAFF7A | |
8 | :1040780011003FEF20F0000142EF20F03DEF20F06B | |
9 | :00000001FF |
Binary diff not shown
Binary diff not shown
0 | 0 | SUBDIRS = \ |
1 | libdfu \ | |
1 | 2 | man |
2 | 3 | |
4 | EXTRA_DIST = \ | |
5 | dfu-metadata-store.md | |
6 | ||
3 | 7 | -include $(top_srcdir)/git.mk |
0 | ||
1 | DFU File Format - Metadata Store Proposal | |
2 | ========================================= | |
3 | ||
4 | Introduction | |
5 | ------------ | |
6 | ||
7 | The DFU specification version 1.1 defines some target-specific data that can | |
8 | optionally be included in the DFU file to ease firmware deployment. | |
9 | These include items such as the runtime vendor, product and device release, | |
10 | but nothing else and with no provision for extra metadata. | |
11 | ||
12 | The DFU file specification does not specify any additional data structures | |
13 | allowing vendors to include additional metadata, although does provide the | |
14 | chance to include future additional data fields in the header in a backwards and | |
15 | forwards compatible way by increasing the `header_len` value. | |
16 | ||
17 | All software reading and writing DFU-format files should already be reading the | |
18 | footer length from the file, rather than assuming a fixed footer length of 0x10 | |
19 | bytes. | |
20 | This ensures that only the raw device firmware is sent to the device and not any | |
21 | additional data fields added in future versions of the DFU specification. | |
22 | ||
23 | There are valid reasons why we would want to add additional metadata into the | |
24 | distributed DFU file. Reasons are listed as follows: | |
25 | ||
26 | * Legal compliance, to tag a file with copyright and licensing information | |
27 | * Business-specific metadata, for instance the SHA-1 git commit for the source | |
28 | * Cryptographic information such as a SHA-256 hash or a detached GPG signature | |
29 | ||
30 | Although the original authors of the specification allowed for future additions | |
31 | to the specification, they only allowed us to extend the footer by 239 bytes as | |
32 | the `header_len` value is specified as just one byte, and 16 bytes are already | |
33 | specified by the specification. | |
34 | ||
35 | This would explain why some vendors are using vendor-specific file prefix data | |
36 | segments, for instance the DfuSe prefix specification from ST. | |
37 | This specification is not aiming to expand or standardize the various | |
38 | incompatible vendor-specific prefix specifications, but tries to squeeze the | |
39 | additional metadata into the existing DFU footer space which is compatible with | |
40 | all existing DFU-compliant software. | |
41 | ||
42 | Specification | |
43 | ------------- | |
44 | ||
45 | An additional structure would be present after the binary firmware data, and | |
46 | notionally contained within the DFU footer itself, although specified as a | |
47 | seporate object. | |
48 | ||
49 | The representation in memory and on disk would be as follows: | |
50 | ||
51 | uint16 signature='MD' | |
52 | uint8 number_of_keys | |
53 | uint8 key(n)_length | |
54 | ... key(n) (no NUL) | |
55 | uint8 value(n)_length | |
56 | ... value(n) (no NUL) | |
57 | <existing DFU footer> | |
58 | ||
59 | If there are no metadata keys being set, it is expected that the metadata table | |
60 | signature is not be written to the file, and that the footer should be again | |
61 | 0x10 bytes in length. | |
62 | ||
63 | The signature of `MD` should also be checked before attempting to parse the | |
64 | metadata store structure to ensure other vendor-specific extensions are not | |
65 | already in use. | |
66 | ||
67 | Conclusions | |
68 | ----------- | |
69 | ||
70 | The metadata store proposal allows us to store a small amount of metadata | |
71 | inside the DFU file footer. | |
72 | If the original specification had included just one more byte for the footer | |
73 | length (for instance a `uint16`, rather than a `uint8`) type then I would have | |
74 | proposed a key type allowing integers, IEEE floating point, and strings, and | |
75 | also made the number of keys and the length of keys much larger. | |
76 | Working with what we've been given, we can support a useful easy-to-parse | |
77 | extension that allows us to solve some of the real-world problems vendors are | |
78 | facing when trying to distribute firmware files for devices that support | |
79 | in-the-field device firmware upgrading. | |
80 | ||
81 | Several deliberate compomises have been made to this proposal due to the | |
82 | restricted space available: | |
83 | ||
84 | * The metadata table signature is just two bytes | |
85 | * The number of keys is limited to just 59 pairs | |
86 | * Keys are limited to just 233 chars maximum | |
87 | * Values are limited to just 233 chars maximum | |
88 | * Types are limited to just strings (which can includes empty strings) | |
89 | * Strings are written without a `NUL` trailing byte | |
90 | * The metadata table uses variable offsets rather than fixed sizes | |
91 | ||
92 | The key-value length in particular leads to some other best practices: | |
93 | ||
94 | * A value for the 'License' key should be in SPDX format, NOT the full licence | |
95 | * A value for the 'Copyright' key should just be the company name | |
96 | ||
97 | The author is not already aware of any vendors using this additional data area, | |
98 | but would be willing to work with any vendors who have implemented a similar | |
99 | proprietary extension already or are planning to do so. |
0 | # This is a blank Makefile.am for using gtk-doc. | |
1 | # Copy this to your project's API docs directory and modify the variables to | |
2 | # suit your project. See the GTK+ Makefiles in gtk+/docs/reference for examples | |
3 | # of using the various options. | |
4 | ||
5 | # The name of the module, e.g. 'glib'. | |
6 | DOC_MODULE=libdfu | |
7 | ||
8 | # Uncomment for versioned docs and specify the version of the module, e.g. '2'. | |
9 | #DOC_MODULE_VERSION=2 | |
10 | ||
11 | # The top-level XML file. | |
12 | DOC_MAIN_SGML_FILE=$(DOC_MODULE)-docs.xml | |
13 | ||
14 | # Directories containing the source code. | |
15 | # gtk-doc will search all .c and .h files beneath these paths | |
16 | # for inline comments documenting functions and macros. | |
17 | # e.g. DOC_SOURCE_DIR=$(top_srcdir)/gtk $(top_srcdir)/gdk | |
18 | DOC_SOURCE_DIR=$(top_srcdir)/libdfu | |
19 | ||
20 | # Extra options to pass to gtkdoc-scangobj. Normally not needed. | |
21 | SCANGOBJ_OPTIONS= | |
22 | ||
23 | # Extra options to supply to gtkdoc-scan. | |
24 | # e.g. SCAN_OPTIONS=--deprecated-guards="GTK_DISABLE_DEPRECATED" | |
25 | SCAN_OPTIONS= | |
26 | ||
27 | # Extra options to supply to gtkdoc-mkdb | |
28 | # e.g. MKDB_OPTIONS=--xml-mode --output-format=xml | |
29 | MKDB_OPTIONS=--xml-mode --output-format=xml | |
30 | ||
31 | # Extra options to supply to gtkdoc-mkhtml | |
32 | MKHTML_OPTIONS= | |
33 | ||
34 | # Extra options to supply to gtkdoc-fixref. Normally not needed. | |
35 | # e.g. FIXXREF_OPTIONS=--extra-dir=../gdk-pixbuf/html --extra-dir=../gdk/html | |
36 | FIXXREF_OPTIONS= | |
37 | ||
38 | # Used for dependencies. The docs will be rebuilt if any of these change. | |
39 | # e.g. HFILE_GLOB=$(top_srcdir)/gtk/*.h | |
40 | # e.g. CFILE_GLOB=$(top_srcdir)/gtk/*.c | |
41 | HFILE_GLOB=$(top_srcdir)/libdfu/*.h | |
42 | CFILE_GLOB=$(top_srcdir)/libdfu/*.c | |
43 | ||
44 | # Extra header to include when scanning, which are not under DOC_SOURCE_DIR | |
45 | # e.g. EXTRA_HFILES=$(top_srcdir}/contrib/extra.h | |
46 | EXTRA_HFILES= | |
47 | ||
48 | # Header files or dirs to ignore when scanning. Use base file/dir names | |
49 | # e.g. IGNORE_HFILES=gtkdebug.h gtkintl.h private_code | |
50 | IGNORE_HFILES= \ | |
51 | dfu-device-private.h \ | |
52 | dfu-element-private.h \ | |
53 | dfu-image-private.h \ | |
54 | dfu-sector-private.h \ | |
55 | dfu-target-private.h | |
56 | ||
57 | # Images to copy into HTML directory. | |
58 | # e.g. HTML_IMAGES=$(top_srcdir)/gtk/stock-icons/stock_about_24.png | |
59 | HTML_IMAGES= | |
60 | ||
61 | # Extra files that are included by $(DOC_MAIN_SGML_FILE). | |
62 | # e.g. content_files=running.xml building.xml changes-2.0.xml | |
63 | content_files= | |
64 | ||
65 | # Files where gtk-doc abbrevations (#GtkWidget) are expanded | |
66 | # e.g. expand_content_files=running.xml | |
67 | expand_content_files= | |
68 | ||
69 | # CFLAGS and LDFLAGS for compiling gtkdoc-scangobj with your library. | |
70 | # Only needed if you are using gtkdoc-scangobj to dynamically query widget | |
71 | # signals and properties. | |
72 | # e.g. GTKDOC_CFLAGS=-I$(top_srcdir) -I$(top_builddir) $(GTK_DEBUG_FLAGS) | |
73 | # e.g. GTKDOC_LIBS=$(top_builddir)/gtk/$(gtktargetlib) | |
74 | GTKDOC_CFLAGS= | |
75 | GTKDOC_LIBS=$(top_builddir)/libdfu/libdfu.la | |
76 | ||
77 | # This includes the standard gtk-doc make rules, copied by gtkdocize. | |
78 | include $(top_srcdir)/gtk-doc.make | |
79 | ||
80 | # Comment this out if you want 'make check' to test you doc status | |
81 | # and run some sanity checks | |
82 | #if ENABLE_GTK_DOC | |
83 | #TESTS_ENVIRONMENT = \ | |
84 | # DOC_MODULE=$(DOC_MODULE) DOC_MAIN_SGML_FILE=$(DOC_MAIN_SGML_FILE) \ | |
85 | # SRCDIR=$(abs_srcdir) BUILDDIR=$(abs_builddir) | |
86 | #TESTS = $(GTKDOC_CHECK) | |
87 | #endif | |
88 | ||
89 | CLEANFILES= \ | |
90 | libdfu.args \ | |
91 | libdfu.hierarchy \ | |
92 | libdfu.interfaces \ | |
93 | libdfu.prerequisites \ | |
94 | libdfu.signals \ | |
95 | libdfu-*.txt \ | |
96 | *.stamp | |
97 | ||
98 | -include $(top_srcdir)/git.mk |
0 | rm -f *.txt | |
1 | rm -f colord-scan* | |
2 | rm -f colord.types | |
3 | rm -rf html/ | |
4 | rm -rf xml/ | |
5 | rm -rf tmpl/ | |
6 | rm -f *.stamp | |
7 |
0 | <?xml version="1.0"?> | |
1 | <!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.3//EN" | |
2 | "http://www.oasis-open.org/docbook/xml/4.3/docbookx.dtd" | |
3 | [ | |
4 | <!ENTITY % local.common.attrib "xmlns:xi CDATA #FIXED 'http://www.w3.org/2003/XInclude'"> | |
5 | ]> | |
6 | <book id="index"> | |
7 | <bookinfo> | |
8 | <title>libdfu Reference Manual</title> | |
9 | <releaseinfo> | |
10 | for libdfu [VERSION]. | |
11 | The latest version of this documentation can be found on-line at | |
12 | <ulink role="online-location" url="http://[SERVER]/libdfu/index.html">http://[SERVER]/libdfu/</ulink>. | |
13 | </releaseinfo> | |
14 | </bookinfo> | |
15 | ||
16 | <reference id="intro"> | |
17 | <title>About libdfu</title> | |
18 | <partintro> | |
19 | <para> | |
20 | libdfu is a library for updating firmware on DFU-capable devices. | |
21 | <ulink url="https://en.wikipedia.org/wiki/USB#DFU">DFU</ulink> | |
22 | is a standardised protocol used on many millions of devices | |
23 | to safely update device firmware by the user. | |
24 | Is is designed to be easy to implement in device bootloaders and | |
25 | also easy to support in native host drivers. | |
26 | Flashing firmware using DFU is supported in Windows, Linux and OS-X. | |
27 | </para> | |
28 | <para> | |
29 | Updating firmware on a device is typically done using | |
30 | <ulink url="http://www.fwupd.org/">fwupd</ulink> or by a session | |
31 | application like GNOME Software. | |
32 | You can also use <ulink url="http://dfu-util.sourceforge.net/">dfu-util</ulink> | |
33 | which may support mode device types than <command>dfu-tool</command> | |
34 | supplied by fwupd. | |
35 | </para> | |
36 | <para> | |
37 | <command>libdfu</command> provides a medium-level API which is | |
38 | available for all languages that support GObject Introspection. | |
39 | It supports cancellation using <varname>GCancellable</varname> | |
40 | and erro reporting using <varname>GError</varname>. | |
41 | To download a device using the API it is as simple as getting | |
42 | a <varname>GUsbDevice</varname> and then doing something like | |
43 | this: | |
44 | </para> | |
45 | <programlisting> | |
46 | dfu_firmware = dfu_firmware_new (); | |
47 | if (!dfu_firmware_parse_data (dfu_firmware, blob_fw, | |
48 | DFU_FIRMWARE_PARSE_FLAG_NONE, error)) | |
49 | return FALSE; | |
50 | dfu_device = dfu_device_new (dev); | |
51 | if (!dfu_device_download (dfu_device, dfu_firmware, | |
52 | DFU_TARGET_TRANSFER_FLAG_DETACH | | |
53 | DFU_TARGET_TRANSFER_FLAG_VERIFY | | |
54 | DFU_TARGET_TRANSFER_FLAG_BOOT_RUNTIME, | |
55 | cancelleable, | |
56 | _progress_cb, userdata, | |
57 | error)) | |
58 | return FALSE; | |
59 | </programlisting> | |
60 | <para> | |
61 | You can read more about DFU in the <ulink url="http://www.usb.org/developers/docs/devclass_docs/DFU_1.1.pdf">official specification</ulink>. | |
62 | </para> | |
63 | </partintro> | |
64 | </reference> | |
65 | ||
66 | <chapter id="object-tree"> | |
67 | <title>Object Hierarchy</title> | |
68 | <xi:include href="xml/tree_index.sgml"/> | |
69 | </chapter> | |
70 | ||
71 | <reference id="libdfu"> | |
72 | <title>libdfu</title> | |
73 | <partintro> | |
74 | <para> | |
75 | Functionality exported by libdfu. | |
76 | </para> | |
77 | </partintro> | |
78 | <xi:include href="xml/dfu-context.xml"/> | |
79 | <xi:include href="xml/dfu-device.xml"/> | |
80 | <xi:include href="xml/dfu-firmware.xml"/> | |
81 | <xi:include href="xml/dfu-image.xml"/> | |
82 | <xi:include href="xml/dfu-element.xml"/> | |
83 | <xi:include href="xml/dfu-sector.xml"/> | |
84 | <xi:include href="xml/dfu-target.xml"/> | |
85 | <xi:include href="xml/dfu-common.xml"/> | |
86 | <xi:include href="xml/dfu-error.xml"/> | |
87 | </reference> | |
88 | ||
89 | <index id="api-index-full"> | |
90 | <title>API Index</title> | |
91 | <xi:include href="xml/api-index-full.xml"><xi:fallback /></xi:include> | |
92 | <xi:include href="xml/api-index-0.5.4.xml"/> | |
93 | </index> | |
94 | ||
95 | <index id="deprecated-api-index" role="deprecated"> | |
96 | <title>Index of deprecated API</title> | |
97 | <xi:include href="xml/api-index-deprecated.xml"><xi:fallback /></xi:include> | |
98 | </index> | |
99 | <xi:include href="xml/annotation-glossary.xml"><xi:fallback /></xi:include> | |
100 | </book> |
0 | dfu_context_get_type | |
1 | dfu_device_get_type | |
2 | dfu_element_get_type | |
3 | dfu_firmware_get_type | |
4 | dfu_image_get_type | |
5 | dfu_target_get_type | |
6 | dfu_sector_get_type |
0 | 0 | man_MANS_DIST = \ |
1 | dfu-tool.1 \ | |
1 | 2 | fwupdmgr.1 |
2 | 3 | |
3 | 4 | EXTRA_DIST = \ |
5 | dfu-tool.sgml \ | |
4 | 6 | fwupdmgr.sgml \ |
5 | 7 | $(man_MANS_DIST) |
6 | 8 | |
8 | 10 | $(man_MANS_DIST) |
9 | 11 | |
10 | 12 | fwupdmgr.1: fwupdmgr.sgml |
13 | $(AM_V_GEN) \ | |
14 | docbook2man $? > /dev/null | |
15 | dfu-tool.1: dfu-tool.sgml | |
11 | 16 | $(AM_V_GEN) \ |
12 | 17 | docbook2man $? > /dev/null |
13 | 18 |
0 | <!doctype refentry PUBLIC "-//OASIS//DTD DocBook V4.1//EN" [ | |
1 | <!-- Please adjust the date whenever revising the manpage. --> | |
2 | <!ENTITY date "<date>26 February,2015</date>"> | |
3 | <!ENTITY package "dfu-tool"> | |
4 | <!ENTITY gnu "<acronym>GNU</acronym>"> | |
5 | <!ENTITY gpl "&gnu; <acronym>GPL</acronym>"> | |
6 | ]> | |
7 | ||
8 | <refentry> | |
9 | <refentryinfo> | |
10 | <address> | |
11 | <email>richard@hughsie.com</email>; | |
12 | </address> | |
13 | <author> | |
14 | <firstname>Richard</firstname> | |
15 | <surname>Hughes</surname> | |
16 | </author> | |
17 | <copyright> | |
18 | <year>2015</year> | |
19 | <holder>Richard Hughes</holder> | |
20 | </copyright> | |
21 | &date; | |
22 | </refentryinfo> | |
23 | <refmeta> | |
24 | <refentrytitle>dfu-tool</refentrytitle> | |
25 | <manvolnum>1</manvolnum> | |
26 | </refmeta> | |
27 | <refnamediv> | |
28 | <refname>&package;</refname> | |
29 | <refpurpose>Device Firmware Upgrade Tool</refpurpose> | |
30 | </refnamediv> | |
31 | <refsynopsisdiv> | |
32 | <cmdsynopsis> | |
33 | <command>&package;</command> | |
34 | <arg><option>--verbose</option></arg> | |
35 | <arg><option>--version</option></arg> | |
36 | <arg><option>--force</option></arg> | |
37 | <arg><option>--device=VID:PID</option></arg> | |
38 | <arg><option>--transfer-size=BYTES</option></arg> | |
39 | </cmdsynopsis> | |
40 | </refsynopsisdiv> | |
41 | <refsect1> | |
42 | <title>DESCRIPTION</title> | |
43 | <para> | |
44 | This manual page documents briefly the <command>&package;</command> command. | |
45 | </para> | |
46 | <para> | |
47 | <command>&package;</command> allows a user to write various kinds of | |
48 | firmware onto devices supporting the USB Device Firmware Upgrade protocol. | |
49 | This tool can be used to switch the device from runtime and DFU modes to | |
50 | allow reading the device contents from supported devices. | |
51 | Either the whole device can be written in one operation, or individual | |
52 | `targets' can be specified with the alternative name or number. | |
53 | </para> | |
54 | <para> | |
55 | <command>&package;</command> uses the <literal>libdfu</literal> shared | |
56 | library to perform actions. | |
57 | Any syncronous actions can be safely cancelled and on failure will return | |
58 | errors with both a type and a full textual description. | |
59 | libdfu supports DFU 1.0, DFU 1.1 and the ST DfuSe vendor extension, and | |
60 | handles many device `quirks' necessary for the real-world implementations | |
61 | of <acronym>DFU</acronym>. | |
62 | </para> | |
63 | <para> | |
64 | Additionally <command>&package;</command> can be used to convert firmware | |
65 | from various different formats, or to modify details about the elements, | |
66 | images and metadata contained inside the firmware file. | |
67 | For example, you can easily convert DFU 1.1 firmware into the | |
68 | vendor-specific DfuSe format, convert a Intel HEX file into a raw file | |
69 | padded to a specific size, or add new copyright and licensing information | |
70 | to an existing file. | |
71 | Fields such as the vendor and product IDs can be changed, and the firmware | |
72 | elements can be encrypted and decrypted using various different methods. | |
73 | Merging two DfuSe files together is also possible, although specifying | |
74 | different alt-setting numbers before merging is a good idea to avoid | |
75 | confusion. | |
76 | </para> | |
77 | <para> | |
78 | Although <command>&package;</command> tries to provide a large number of | |
79 | easy-to-use commands, it may only be possible to do certain operations | |
80 | using the <literal>libdfu</literal> library directly. | |
81 | This is easier than it sounds, as the library is built with GObject | |
82 | Introspection support making it usable in many languages such as C, | |
83 | Javascript and Python. | |
84 | Furthermore, using the library is a good idea if you want to perform | |
85 | multiple operations on large firmware files, for instance, | |
86 | converting from an Intel HEX file, padding to a certain size, setting | |
87 | vendor and adding licensing information and then saving to a remote | |
88 | location. | |
89 | </para> | |
90 | </refsect1> | |
91 | <refsect1> | |
92 | <title>OPTIONS</title> | |
93 | <para> | |
94 | This program follows the usual &gnu; command line syntax, | |
95 | with long options starting with two dashes (<literal>-</literal>). | |
96 | A summary of options is included below. | |
97 | </para> | |
98 | <variablelist> | |
99 | <varlistentry> | |
100 | <term> | |
101 | <option>--help</option> | |
102 | </term> | |
103 | <listitem> | |
104 | <para>Show summary of all the commands available for use.</para> | |
105 | </listitem> | |
106 | </varlistentry> | |
107 | <varlistentry> | |
108 | <term> | |
109 | <option>--version</option> | |
110 | </term> | |
111 | <listitem> | |
112 | <para>Show the version of <command>&package;</command> installed.</para> | |
113 | </listitem> | |
114 | </varlistentry> | |
115 | <varlistentry> | |
116 | <term> | |
117 | <option>--verbose</option> | |
118 | </term> | |
119 | <listitem> | |
120 | <para>Show extra debugging information.</para> | |
121 | </listitem> | |
122 | </varlistentry> | |
123 | <varlistentry> | |
124 | <term> | |
125 | <option>--device=VID:PID</option> | |
126 | </term> | |
127 | <listitem> | |
128 | <para> | |
129 | If multiple DFU-capable devices are attached you can specify the | |
130 | specific vendor and product ID of the DFU device you want to query. | |
131 | </para> | |
132 | </listitem> | |
133 | </varlistentry> | |
134 | <varlistentry> | |
135 | <term> | |
136 | <option>--transfer-size=BYTES</option> | |
137 | </term> | |
138 | <listitem> | |
139 | <para> | |
140 | Manually override the size of each USB transfer, which you may want | |
141 | for unreliable hardware or when the device lies about the maximum | |
142 | packet size it accepts. | |
143 | </para> | |
144 | </listitem> | |
145 | </varlistentry> | |
146 | <varlistentry> | |
147 | <term> | |
148 | <option>--force</option> | |
149 | </term> | |
150 | <listitem> | |
151 | <para> | |
152 | Force the operation, disregarding warnings or sanity checks like | |
153 | file CRC and checksums. | |
154 | This is useful if you really know what you are doing, or in the | |
155 | specialised case of fuzz-testing libdfu. | |
156 | </para> | |
157 | </listitem> | |
158 | </varlistentry> | |
159 | </variablelist> | |
160 | </refsect1> | |
161 | <refsect1> | |
162 | <title>DEVICE COMMANDS</title> | |
163 | <para> | |
164 | These commands are used to interface with DFU-capable devices. | |
165 | </para> | |
166 | <variablelist> | |
167 | <varlistentry> | |
168 | <term> | |
169 | <option>list</option> | |
170 | </term> | |
171 | <listitem> | |
172 | <para> | |
173 | This command lists currently attached DFU capable devices. | |
174 | Some devices do not support the official DFU <literal>runtime</literal> | |
175 | mode and thus do not support auto-discovery using this command. | |
176 | For those devices, putting the device into DFU mode manually (e.g. by | |
177 | holding a button down when rebooting the device) will make it show | |
178 | up here. | |
179 | </para> | |
180 | </listitem> | |
181 | </varlistentry> | |
182 | <varlistentry> | |
183 | <term> | |
184 | <option>detach</option> | |
185 | </term> | |
186 | <listitem> | |
187 | <para> | |
188 | This command detaches the currently attached DFU capable device into | |
189 | a special programming mode. | |
190 | Whilst the device is in this special <acronym>DFU</acronym> mode it | |
191 | can not be used as a normal device. | |
192 | For example, a printer will not accept documents when in DFU mode. | |
193 | </para> | |
194 | </listitem> | |
195 | </varlistentry> | |
196 | <varlistentry> | |
197 | <term> | |
198 | <option>attach</option> | |
199 | </term> | |
200 | <listitem> | |
201 | <para> | |
202 | This command attaches a DFU capable device back to runtime so it can | |
203 | be used as a normal device. | |
204 | Some devices do not support attaching, and need to be manually | |
205 | disconnected and connected before changing modes. | |
206 | </para> | |
207 | </listitem> | |
208 | </varlistentry> | |
209 | <varlistentry> | |
210 | <term> | |
211 | <option>watch</option> | |
212 | </term> | |
213 | <listitem> | |
214 | <para> | |
215 | This command watches DFU devices being hotplugged and can be used to | |
216 | verify <literal>libdfu</literal> matches up the runtime and DFU modes | |
217 | when attaching and detaching. | |
218 | Use <option>CTRL+C</option> to make this command quit. | |
219 | </para> | |
220 | </listitem> | |
221 | </varlistentry> | |
222 | <varlistentry> | |
223 | <term> | |
224 | <option>read FILENAME</option> | |
225 | </term> | |
226 | <listitem> | |
227 | <para> | |
228 | This command uploads all the firmware from device into a file. | |
229 | If the device has multiple partitions exported as different alternative | |
230 | sections then they will all be read into a multi-image DfuSe-format | |
231 | file. | |
232 | If you just want the contents of one partition, <option>read-alt</option> | |
233 | is the command you want. | |
234 | </para> | |
235 | </listitem> | |
236 | </varlistentry> | |
237 | <varlistentry> | |
238 | <term> | |
239 | <option>read-alt FILENAME DEVICE-ALT-NAME|DEVICE-ALT-ID</option> | |
240 | </term> | |
241 | <listitem> | |
242 | <para> | |
243 | This command uploads firmware from one partition into a file. | |
244 | You can specify the partition by either the ALT-ID or ALT-NAME if set. | |
245 | </para> | |
246 | <para>e.g. <command>&package; read-alt backup.dfu SRAM</command></para> | |
247 | </listitem> | |
248 | </varlistentry> | |
249 | <varlistentry> | |
250 | <term> | |
251 | <option>write</option> | |
252 | </term> | |
253 | <listitem> | |
254 | <para> | |
255 | This command downloads firmware from a file into all possible | |
256 | partitions of a device. | |
257 | If you only want to write one partition, <option>write-alt</option> | |
258 | is the command you want. | |
259 | </para> | |
260 | </listitem> | |
261 | </varlistentry> | |
262 | <varlistentry> | |
263 | <term> | |
264 | <option>write-alt FILENAME DEVICE-ALT-NAME|DEVICE-ALT-ID [IMAGE-ALT-NAME|IMAGE-ALT-ID]</option> | |
265 | </term> | |
266 | <listitem> | |
267 | <para> | |
268 | This command downloads firmware from the file into one partition. | |
269 | You can specify the partition by either the ALT-ID or ALT-NAME if set. | |
270 | </para> | |
271 | <para>e.g. <command>&package; sram.dfu SRAM __SRAM</command></para> | |
272 | </listitem> | |
273 | </varlistentry> | |
274 | </variablelist> | |
275 | </refsect1> | |
276 | <refsect1> | |
277 | <title>FIRMWARE COMMANDS</title> | |
278 | <para> | |
279 | These commands are used to read and modify existing firmware files. | |
280 | </para> | |
281 | <variablelist> | |
282 | <varlistentry> | |
283 | <term> | |
284 | <option>dump FILENAME</option> | |
285 | </term> | |
286 | <listitem> | |
287 | <para> | |
288 | This command dumps all know details about a firmware file. | |
289 | The complete memory map is shown, along with any metadata or vendor | |
290 | information about the firmware file. | |
291 | </para> | |
292 | </listitem> | |
293 | </varlistentry> | |
294 | <varlistentry> | |
295 | <term> | |
296 | <option>convert FORMAT FILE-IN FILE-OUT [SIZE]</option> | |
297 | </term> | |
298 | <listitem> | |
299 | <para> | |
300 | This command converts the firmware from one format to another, optionally | |
301 | padding to a certain size. | |
302 | Possible values for the destination <option>FORMAT</option> include: | |
303 | <literal>raw</literal>, <literal>ihex</literal>, | |
304 | <literal>dfu</literal> and <literal>dfuse</literal>. | |
305 | The <option>FILE-IN</option> and <option>FILE-OUT</option> values can | |
306 | be the same if the source file is to be overwritten. | |
307 | Although padding increases the file size with no apparent advantages | |
308 | it can be used to support devices that do not store the runtime image | |
309 | size and where validation of the written firmware is required. | |
310 | </para> | |
311 | <para>e.g. <command>&package; dfu firmware.hex firmware.dfu 8000</command></para> | |
312 | </listitem> | |
313 | </varlistentry> | |
314 | <varlistentry> | |
315 | <term> | |
316 | <option>encrypt FILENAME-IN FILENAME-OUT TYPE KEY</option> | |
317 | </term> | |
318 | <listitem> | |
319 | <para> | |
320 | This command encrypts firmware data. | |
321 | Only the image contents are actually modified, the DFU footer and | |
322 | DfuSe header are left unaltered. | |
323 | Possible values for the destination <option>TYPE</option> include: | |
324 | <literal>xtea</literal> and <literal>nop</literal>. | |
325 | If the <option>KEY</option> is not of the required length it is used | |
326 | as an input to a hash function which can produce a key of the | |
327 | required size. | |
328 | </para> | |
329 | <para>e.g. <command>&package; firmware.dfu firmware.xdfu xtea deadbeef</command></para> | |
330 | </listitem> | |
331 | </varlistentry> | |
332 | <varlistentry> | |
333 | <term> | |
334 | <option>decrypt FILENAME-IN FILENAME-OUT TYPE KEY</option> | |
335 | </term> | |
336 | <listitem> | |
337 | <para> | |
338 | This command decrypts firmware data. | |
339 | Only the image contents are actually modified, the DFU footer and | |
340 | DfuSe header are left unaltered. | |
341 | Possible values for the destination <option>TYPE</option> include: | |
342 | <literal>xtea</literal> and <literal>nop</literal>. | |
343 | If the <option>KEY</option> is not of the required length it is used | |
344 | as an input to a hash function which can produce a key of the | |
345 | required size. | |
346 | </para> | |
347 | <para>e.g. <command>&package; firmware.xdfu firmware.dfu xtea deadbeef</command></para> | |
348 | </listitem> | |
349 | </varlistentry> | |
350 | <varlistentry> | |
351 | <term> | |
352 | <option>merge FILE-OUT FILE1 FILE2 [FILE3...]</option> | |
353 | </term> | |
354 | <listitem> | |
355 | <para> | |
356 | This command merges multiple firmware files into one file. | |
357 | Although you can merge files with the same ALT-ID or ALT-NAME this | |
358 | probably isn't what you want to do. | |
359 | </para> | |
360 | <para>e.g. <command>&package; combined.dfu lib.dfu app.dfu</command></para> | |
361 | </listitem> | |
362 | </varlistentry> | |
363 | <varlistentry> | |
364 | <term> | |
365 | <option>set-alt-setting FILE ALT-ID</option> | |
366 | </term> | |
367 | <listitem> | |
368 | <para> | |
369 | This command modifies the alternative number on firmware file. | |
370 | </para> | |
371 | <para>e.g. <command>&package; firmware.dfu 1</command></para> | |
372 | </listitem> | |
373 | </varlistentry> | |
374 | <varlistentry> | |
375 | <term> | |
376 | <option>set-alt-setting-name</option> | |
377 | </term> | |
378 | <listitem> | |
379 | <para> | |
380 | This command modifies the alternative name on firmware file. | |
381 | </para> | |
382 | <para>e.g. <command>&package; firmware.dfu SRAM</command></para> | |
383 | </listitem> | |
384 | </varlistentry> | |
385 | <varlistentry> | |
386 | <term> | |
387 | <option>set-metadata FILE KEY VALUE</option> | |
388 | </term> | |
389 | <listitem> | |
390 | <para> | |
391 | This command adds or modifies existing metadata on a firmware file. | |
392 | NOTE: There is only very limited metadata storage space in DFU files, | |
393 | so keys and values should be kept as short as possible. | |
394 | In particular, the <literal>License</literal> value should be | |
395 | specified in SPDX format. | |
396 | </para> | |
397 | <para>e.g. <command>&package; firmware.dfu Licence GPL-2.0+</command></para> | |
398 | </listitem> | |
399 | </varlistentry> | |
400 | <varlistentry> | |
401 | <term> | |
402 | <option>set-vendor FILE VID</option> | |
403 | </term> | |
404 | <listitem> | |
405 | <para> | |
406 | This command sets vendor ID on a firmware file that will be used to | |
407 | match specific devices. | |
408 | Values of <literal>ffff</literal> will match any device vendor. | |
409 | </para> | |
410 | <para>e.g. <command>&package; firmware.dfu 273f</command></para> | |
411 | </listitem> | |
412 | </varlistentry> | |
413 | <varlistentry> | |
414 | <term> | |
415 | <option>set-product FILE PID</option> | |
416 | </term> | |
417 | <listitem> | |
418 | <para> | |
419 | This command sets the product ID on a firmware file that will be used to | |
420 | match specific devices. | |
421 | Values of <literal>ffff</literal> will match any device product. | |
422 | </para> | |
423 | <para>e.g. <command>&package; firmware.dfu 1004</command></para> | |
424 | </listitem> | |
425 | </varlistentry> | |
426 | <varlistentry> | |
427 | <term> | |
428 | <option>set-release FILE RELEASE</option> | |
429 | </term> | |
430 | <listitem> | |
431 | <para> | |
432 | This command sets the release version on firmware file that will be used to | |
433 | match specific devices. | |
434 | Values of <literal>ffff</literal> will match any device release. | |
435 | </para> | |
436 | <para>e.g. <command>&package; firmware.dfu ffff</command></para> | |
437 | </listitem> | |
438 | </varlistentry> | |
439 | </variablelist> | |
440 | </refsect1> | |
441 | <refsect1> | |
442 | <title>AUTHOR</title> | |
443 | <para>This manual page was written by Richard Hughes <email>richard@hughsie.com</email>. | |
444 | </para> | |
445 | </refsect1> | |
446 | </refentry> | |
447 | ||
448 | <!-- Keep this comment at the end of the file | |
449 | Local variables: | |
450 | mode: sgml | |
451 | sgml-omittag:t | |
452 | sgml-shorttag:t | |
453 | sgml-minimize-attributes:nil | |
454 | sgml-always-quote-attributes:t | |
455 | sgml-indent-step:2 | |
456 | sgml-indent-data:t | |
457 | sgml-parent-document:nil | |
458 | sgml-default-dtd-file:nil | |
459 | sgml-exposed-tags:nil | |
460 | sgml-local-catalogs:nil | |
461 | sgml-local-ecat-files:nil | |
462 | End: | |
463 | --> |
0 | if HAVE_INTROSPECTION | |
1 | -include $(INTROSPECTION_MAKEFILE) | |
2 | INTROSPECTION_GIRS = | |
3 | INTROSPECTION_SCANNER_ARGS = --add-include-path=$(srcdir) | |
4 | INTROSPECTION_COMPILER_ARGS = --includedir=$(srcdir) | |
5 | endif | |
6 | ||
7 | AM_CPPFLAGS = \ | |
8 | $(APPSTREAM_GLIB_CFLAGS) \ | |
9 | $(GLIB_CFLAGS) \ | |
10 | $(GUSB_CFLAGS) \ | |
11 | $(PIE_CFLAGS) \ | |
12 | -I$(top_srcdir)/libdfu \ | |
13 | -I$(top_srcdir) \ | |
14 | -I$(top_builddir) \ | |
15 | -DG_USB_API_IS_SUBJECT_TO_CHANGE \ | |
16 | -DG_LOG_DOMAIN=\"libdfu\" \ | |
17 | -DTESTDATADIR=\""$(top_srcdir)/data/tests/dfu"\" \ | |
18 | -DLOCALEDIR=\""$(localedir)"\" | |
19 | ||
20 | FWUPD_LIBS = \ | |
21 | $(top_builddir)/libfwupd/libfwupd.la | |
22 | ||
23 | lib_LTLIBRARIES = \ | |
24 | libdfu.la | |
25 | ||
26 | libdfu_includedir = $(includedir)/fwupd-1 | |
27 | libdfu_include_HEADERS = \ | |
28 | dfu.h | |
29 | ||
30 | libdfubase_includedir = $(libdfu_includedir)/libdfu | |
31 | libdfubase_include_HEADERS = \ | |
32 | dfu-common.h \ | |
33 | dfu-context.h \ | |
34 | dfu-device.h \ | |
35 | dfu-element.h \ | |
36 | dfu-error.h \ | |
37 | dfu-firmware.h \ | |
38 | dfu-image.h \ | |
39 | dfu-sector.h \ | |
40 | dfu-target.h | |
41 | ||
42 | libdfu_la_SOURCES = \ | |
43 | dfu.h \ | |
44 | dfu-common.c \ | |
45 | dfu-common.h \ | |
46 | dfu-context.c \ | |
47 | dfu-context.h \ | |
48 | dfu-device.c \ | |
49 | dfu-device.h \ | |
50 | dfu-device-private.h \ | |
51 | dfu-element.c \ | |
52 | dfu-element.h \ | |
53 | dfu-element-private.h \ | |
54 | dfu-error.c \ | |
55 | dfu-error.h \ | |
56 | dfu-firmware.c \ | |
57 | dfu-firmware.h \ | |
58 | dfu-image.c \ | |
59 | dfu-image.h \ | |
60 | dfu-image-private.h \ | |
61 | dfu-sector.c \ | |
62 | dfu-sector.h \ | |
63 | dfu-sector-private.h \ | |
64 | dfu-target.c \ | |
65 | dfu-target.h \ | |
66 | dfu-target-private.h | |
67 | ||
68 | libdfu_la_LIBADD = \ | |
69 | $(FWUPD_LIBS) \ | |
70 | $(GUSB_LIBS) \ | |
71 | $(GLIB_LIBS) \ | |
72 | $(LIBM) | |
73 | ||
74 | libdfu_la_LDFLAGS = \ | |
75 | -version-info $(LT_CURRENT):$(LT_REVISION):$(LT_AGE) \ | |
76 | -export-dynamic \ | |
77 | -no-undefined \ | |
78 | -export-symbols-regex '^dfu_.*' | |
79 | ||
80 | libdfu_la_CFLAGS = \ | |
81 | $(PIE_CFLAGS) \ | |
82 | $(WARNINGFLAGS_C) | |
83 | ||
84 | pkgconfigdir = $(libdir)/pkgconfig | |
85 | pkgconfig_DATA = dfu.pc | |
86 | ||
87 | EXTRA_DIST = \ | |
88 | dfu.pc.in | |
89 | ||
90 | bin_PROGRAMS = \ | |
91 | dfu-tool | |
92 | ||
93 | dfu_tool_SOURCES = \ | |
94 | dfu-tool.c | |
95 | ||
96 | dfu_tool_LDADD = \ | |
97 | $(lib_LTLIBRARIES) \ | |
98 | $(APPSTREAM_GLIB_LIBS) \ | |
99 | $(GLIB_LIBS) \ | |
100 | $(GUSB_LIBS) \ | |
101 | $(LIBM) | |
102 | ||
103 | dfu_tool_CFLAGS = -DEGG_TEST $(AM_CFLAGS) $(WARNINGFLAGS_C) | |
104 | ||
105 | TESTS_ENVIRONMENT = \ | |
106 | libtool --mode=execute valgrind \ | |
107 | --quiet \ | |
108 | --leak-check=full \ | |
109 | --show-possibly-lost=no | |
110 | ||
111 | check_PROGRAMS = \ | |
112 | dfu-self-test | |
113 | ||
114 | dfu_self_test_SOURCES = \ | |
115 | dfu-self-test.c | |
116 | ||
117 | dfu_self_test_LDADD = \ | |
118 | $(lib_LTLIBRARIES) \ | |
119 | $(GLIB_LIBS) \ | |
120 | $(GUSB_LIBS) | |
121 | ||
122 | dfu_self_test_CFLAGS = -DEGG_TEST $(AM_CFLAGS) $(WARNINGFLAGS_C) | |
123 | ||
124 | TESTS = dfu-self-test | |
125 | ||
126 | CLEANFILES = *.log *.trs $(BUILT_SOURCES) | |
127 | ||
128 | MAINTAINERCLEANFILES = *.dfu *.bin | |
129 | if HAVE_INTROSPECTION | |
130 | introspection_sources = \ | |
131 | $(libdfu_la_SOURCES) | |
132 | ||
133 | Dfu-1.0.gir: libdfu.la | |
134 | Dfu_1_0_gir_INCLUDES = GObject-2.0 Gio-2.0 GUsb-1.0 | |
135 | Dfu_1_0_gir_CFLAGS = $(AM_CPPFLAGS) | |
136 | Dfu_1_0_gir_SCANNERFLAGS = --identifier-prefix=Dfu \ | |
137 | --symbol-prefix=dfu \ | |
138 | --warn-all \ | |
139 | --add-include-path=$(srcdir) \ | |
140 | --c-include="dfu.h" | |
141 | Dfu_1_0_gir_EXPORT_PACKAGES = dfu | |
142 | Dfu_1_0_gir_LIBS = libdfu.la | |
143 | Dfu_1_0_gir_FILES = $(introspection_sources) | |
144 | INTROSPECTION_GIRS += Dfu-1.0.gir | |
145 | ||
146 | girdir = $(datadir)/gir-1.0 | |
147 | gir_DATA = $(INTROSPECTION_GIRS) | |
148 | ||
149 | typelibdir = $(libdir)/girepository-1.0 | |
150 | typelib_DATA = $(INTROSPECTION_GIRS:.gir=.typelib) | |
151 | ||
152 | CLEANFILES += $(gir_DATA) $(typelib_DATA) *.log *.trs *.test | |
153 | endif | |
154 | ||
155 | clean-local: | |
156 | rm -f *~ | |
157 | ||
158 | -include $(top_srcdir)/git.mk |
0 | /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- | |
1 | * | |
2 | * Copyright (C) 2015 Richard Hughes <richard@hughsie.com> | |
3 | * | |
4 | * Licensed under the GNU Lesser General Public License Version 2.1 | |
5 | * | |
6 | * This library is free software; you can redistribute it and/or | |
7 | * modify it under the terms of the GNU Lesser General Public | |
8 | * License as published by the Free Software Foundation; either | |
9 | * version 2.1 of the License, or (at your option) any later version. | |
10 | * | |
11 | * This library is distributed in the hope that it will be useful, | |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
14 | * Lesser General Public License for more details. | |
15 | * | |
16 | * You should have received a copy of the GNU Lesser General Public | |
17 | * License along with this library; if not, write to the Free Software | |
18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | |
19 | */ | |
20 | ||
21 | /** | |
22 | * SECTION:dfu-common | |
23 | * @short_description: Common functions for DFU | |
24 | * | |
25 | * These helper objects allow converting from enum values to strings. | |
26 | */ | |
27 | ||
28 | #include "config.h" | |
29 | ||
30 | #include "dfu-common.h" | |
31 | ||
32 | /** | |
33 | * dfu_state_to_string: | |
34 | * @state: a #DfuState, e.g. %DFU_STATE_DFU_MANIFEST | |
35 | * | |
36 | * Converts an enumerated value to a string. | |
37 | * | |
38 | * Return value: a string | |
39 | * | |
40 | * Since: 0.5.4 | |
41 | **/ | |
42 | const gchar * | |
43 | dfu_state_to_string (DfuState state) | |
44 | { | |
45 | if (state == DFU_STATE_APP_IDLE) | |
46 | return "appIDLE"; | |
47 | if (state == DFU_STATE_APP_DETACH) | |
48 | return "appDETACH"; | |
49 | if (state == DFU_STATE_DFU_IDLE) | |
50 | return "dfuIDLE"; | |
51 | if (state == DFU_STATE_DFU_DNLOAD_SYNC) | |
52 | return "dfuDNLOAD-SYNC"; | |
53 | if (state == DFU_STATE_DFU_DNBUSY) | |
54 | return "dfuDNBUSY"; | |
55 | if (state == DFU_STATE_DFU_DNLOAD_IDLE) | |
56 | return "dfuDNLOAD-IDLE"; | |
57 | if (state == DFU_STATE_DFU_MANIFEST_SYNC) | |
58 | return "dfuMANIFEST-SYNC"; | |
59 | if (state == DFU_STATE_DFU_MANIFEST) | |
60 | return "dfuMANIFEST"; | |
61 | if (state == DFU_STATE_DFU_MANIFEST_WAIT_RESET) | |
62 | return "dfuMANIFEST-WAIT-RESET"; | |
63 | if (state == DFU_STATE_DFU_UPLOAD_IDLE) | |
64 | return "dfuUPLOAD-IDLE"; | |
65 | if (state == DFU_STATE_DFU_ERROR) | |
66 | return "dfuERROR"; | |
67 | return NULL; | |
68 | } | |
69 | ||
70 | /** | |
71 | * dfu_status_to_string: | |
72 | * @status: a #DfuStatus, e.g. %DFU_STATUS_ERR_ERASE | |
73 | * | |
74 | * Converts an enumerated value to a string. | |
75 | * | |
76 | * Return value: a string | |
77 | * | |
78 | * Since: 0.5.4 | |
79 | **/ | |
80 | const gchar * | |
81 | dfu_status_to_string (DfuStatus status) | |
82 | { | |
83 | if (status == DFU_STATUS_OK) | |
84 | return "OK"; | |
85 | if (status == DFU_STATUS_ERR_TARGET) | |
86 | return "errTARGET"; | |
87 | if (status == DFU_STATUS_ERR_FILE) | |
88 | return "errFILE"; | |
89 | if (status == DFU_STATUS_ERR_WRITE) | |
90 | return "errwrite"; | |
91 | if (status == DFU_STATUS_ERR_ERASE) | |
92 | return "errERASE"; | |
93 | if (status == DFU_STATUS_ERR_CHECK_ERASED) | |
94 | return "errCHECK_ERASED"; | |
95 | if (status == DFU_STATUS_ERR_PROG) | |
96 | return "errPROG"; | |
97 | if (status == DFU_STATUS_ERR_VERIFY) | |
98 | return "errVERIFY"; | |
99 | if (status == DFU_STATUS_ERR_ADDRESS) | |
100 | return "errADDRESS"; | |
101 | if (status == DFU_STATUS_ERR_NOTDONE) | |
102 | return "errNOTDONE"; | |
103 | if (status == DFU_STATUS_ERR_FIRMWARE) | |
104 | return "errFIRMWARE"; | |
105 | if (status == DFU_STATUS_ERR_VENDOR) | |
106 | return "errVENDOR"; | |
107 | if (status == DFU_STATUS_ERR_USBR) | |
108 | return "errUSBR"; | |
109 | if (status == DFU_STATUS_ERR_POR) | |
110 | return "errPOR"; | |
111 | if (status == DFU_STATUS_ERR_UNKNOWN) | |
112 | return "errUNKNOWN"; | |
113 | if (status == DFU_STATUS_ERR_STALLDPKT) | |
114 | return "errSTALLDPKT"; | |
115 | return NULL; | |
116 | } | |
117 | ||
118 | /** | |
119 | * dfu_mode_to_string: | |
120 | * @mode: a #DfuMode, e.g. %DFU_MODE_RUNTIME | |
121 | * | |
122 | * Converts an enumerated value to a string. | |
123 | * | |
124 | * Return value: a string | |
125 | * | |
126 | * Since: 0.5.4 | |
127 | **/ | |
128 | const gchar * | |
129 | dfu_mode_to_string (DfuMode mode) | |
130 | { | |
131 | if (mode == DFU_MODE_RUNTIME) | |
132 | return "runtime"; | |
133 | if (mode == DFU_MODE_DFU) | |
134 | return "DFU"; | |
135 | return NULL; | |
136 | } | |
137 | ||
138 | /** | |
139 | * dfu_cipher_kind_to_string: | |
140 | * @cipher_kind: a #DfuCipherKind, e.g. %DFU_CIPHER_KIND_XTEA | |
141 | * | |
142 | * Converts an enumerated value to a string. | |
143 | * | |
144 | * Return value: a string | |
145 | * | |
146 | * Since: 0.5.4 | |
147 | **/ | |
148 | const gchar * | |
149 | dfu_cipher_kind_to_string (DfuCipherKind cipher_kind) | |
150 | { | |
151 | if (cipher_kind == DFU_CIPHER_KIND_NONE) | |
152 | return "none"; | |
153 | if (cipher_kind == DFU_CIPHER_KIND_XTEA) | |
154 | return "xtea"; | |
155 | return NULL; | |
156 | } |
0 | /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- | |
1 | * | |
2 | * Copyright (C) 2015 Richard Hughes <richard@hughsie.com> | |
3 | * | |
4 | * Licensed under the GNU Lesser General Public License Version 2.1 | |
5 | * | |
6 | * This library is free software; you can redistribute it and/or | |
7 | * modify it under the terms of the GNU Lesser General Public | |
8 | * License as published by the Free Software Foundation; either | |
9 | * version 2.1 of the License, or (at your option) any later version. | |
10 | * | |
11 | * This library is distributed in the hope that it will be useful, | |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
14 | * Lesser General Public License for more details. | |
15 | * | |
16 | * You should have received a copy of the GNU Lesser General Public | |
17 | * License along with this library; if not, write to the Free Software | |
18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | |
19 | */ | |
20 | ||
21 | #ifndef __DFU_COMMON_H | |
22 | #define __DFU_COMMON_H | |
23 | ||
24 | #include <glib.h> | |
25 | #include <gusb.h> | |
26 | ||
27 | G_BEGIN_DECLS | |
28 | ||
29 | /** | |
30 | * DfuRequest: | |
31 | * @DFU_REQUEST_DETACH: Detach | |
32 | * @DFU_REQUEST_DNLOAD: Download host-to-device | |
33 | * @DFU_REQUEST_UPLOAD: Upload device-to-host | |
34 | * @DFU_REQUEST_GETSTATUS: Get the device status | |
35 | * @DFU_REQUEST_CLRSTATUS: Clear the device status | |
36 | * @DFU_REQUEST_GETSTATE: Get the last set state | |
37 | * @DFU_REQUEST_ABORT: Abort the current transfer | |
38 | * | |
39 | * The DFU request kinds. | |
40 | **/ | |
41 | typedef enum { | |
42 | DFU_REQUEST_DETACH = 0x00, | |
43 | DFU_REQUEST_DNLOAD = 0x01, | |
44 | DFU_REQUEST_UPLOAD = 0x02, | |
45 | DFU_REQUEST_GETSTATUS = 0x03, | |
46 | DFU_REQUEST_CLRSTATUS = 0x04, | |
47 | DFU_REQUEST_GETSTATE = 0x05, | |
48 | DFU_REQUEST_ABORT = 0x06, | |
49 | /*< private >*/ | |
50 | DFU_REQUEST_LAST | |
51 | } DfuRequest; | |
52 | ||
53 | /** | |
54 | * DfuStatus: | |
55 | * @DFU_STATUS_OK: No error condition is present | |
56 | * @DFU_STATUS_ERR_TARGET: File is not targeted for use by this device | |
57 | * @DFU_STATUS_ERR_FILE: File is for this device but fails a verification test | |
58 | * @DFU_STATUS_ERR_WRITE: Device is unable to write memory | |
59 | * @DFU_STATUS_ERR_ERASE: Memory erase function failed | |
60 | * @DFU_STATUS_ERR_CHECK_ERASED: Memory erase check failed | |
61 | * @DFU_STATUS_ERR_PROG: Program memory function failed | |
62 | * @DFU_STATUS_ERR_VERIFY: Programmed memory failed verification | |
63 | * @DFU_STATUS_ERR_ADDRESS: Cannot program memory due to received address that isout of range | |
64 | * @DFU_STATUS_ERR_NOTDONE: Received DFU_DNLOAD with wLength = 0 but data is incomplete | |
65 | * @DFU_STATUS_ERR_FIRMWARE: Device firmware is corrupt | |
66 | * @DFU_STATUS_ERR_VENDOR: iString indicates a vendor-specific error | |
67 | * @DFU_STATUS_ERR_USBR: Device detected unexpected USB reset signaling | |
68 | * @DFU_STATUS_ERR_POR: Device detected unexpected power on reset | |
69 | * @DFU_STATUS_ERR_UNKNOWN: Something unexpected went wrong | |
70 | * @DFU_STATUS_ERR_STALLDPKT: Device stalled an unexpected request | |
71 | * | |
72 | * The status enumerated kind. | |
73 | **/ | |
74 | typedef enum { | |
75 | DFU_STATUS_OK = 0x00, | |
76 | DFU_STATUS_ERR_TARGET = 0x01, | |
77 | DFU_STATUS_ERR_FILE = 0x02, | |
78 | DFU_STATUS_ERR_WRITE = 0x03, | |
79 | DFU_STATUS_ERR_ERASE = 0x04, | |
80 | DFU_STATUS_ERR_CHECK_ERASED = 0x05, | |
81 | DFU_STATUS_ERR_PROG = 0x06, | |
82 | DFU_STATUS_ERR_VERIFY = 0x07, | |
83 | DFU_STATUS_ERR_ADDRESS = 0x08, | |
84 | DFU_STATUS_ERR_NOTDONE = 0x09, | |
85 | DFU_STATUS_ERR_FIRMWARE = 0x0a, | |
86 | DFU_STATUS_ERR_VENDOR = 0x0b, | |
87 | DFU_STATUS_ERR_USBR = 0x0c, | |
88 | DFU_STATUS_ERR_POR = 0x0d, | |
89 | DFU_STATUS_ERR_UNKNOWN = 0x0e, | |
90 | DFU_STATUS_ERR_STALLDPKT = 0x0f, | |
91 | /*< private >*/ | |
92 | DFU_STATUS_LAST | |
93 | } DfuStatus; | |
94 | ||
95 | /** | |
96 | * DfuState: | |
97 | * @DFU_STATE_APP_IDLE: State 0 | |
98 | * @DFU_STATE_APP_DETACH: State 1 | |
99 | * @DFU_STATE_DFU_IDLE: State 2 | |
100 | * @DFU_STATE_DFU_DNLOAD_SYNC: State 3 | |
101 | * @DFU_STATE_DFU_DNBUSY: State 4 | |
102 | * @DFU_STATE_DFU_DNLOAD_IDLE: State 5 | |
103 | * @DFU_STATE_DFU_MANIFEST_SYNC: State 6 | |
104 | * @DFU_STATE_DFU_MANIFEST: State 7 | |
105 | * @DFU_STATE_DFU_MANIFEST_WAIT_RESET: State 8 | |
106 | * @DFU_STATE_DFU_UPLOAD_IDLE: State 9 | |
107 | * @DFU_STATE_DFU_ERROR: State 10 | |
108 | * | |
109 | * The state enumerated kind. | |
110 | **/ | |
111 | typedef enum { | |
112 | DFU_STATE_APP_IDLE = 0x00, | |
113 | DFU_STATE_APP_DETACH = 0x01, | |
114 | DFU_STATE_DFU_IDLE = 0x02, | |
115 | DFU_STATE_DFU_DNLOAD_SYNC = 0x03, | |
116 | DFU_STATE_DFU_DNBUSY = 0x04, | |
117 | DFU_STATE_DFU_DNLOAD_IDLE = 0x05, | |
118 | DFU_STATE_DFU_MANIFEST_SYNC = 0x06, | |
119 | DFU_STATE_DFU_MANIFEST = 0x07, | |
120 | DFU_STATE_DFU_MANIFEST_WAIT_RESET = 0x08, | |
121 | DFU_STATE_DFU_UPLOAD_IDLE = 0x09, | |
122 | DFU_STATE_DFU_ERROR = 0x0a, | |
123 | /*< private >*/ | |
124 | DFU_STATE_LAST | |
125 | } DfuState; | |
126 | ||
127 | /** | |
128 | * DfuMode: | |
129 | * @DFU_MODE_UNKNOWN: Unknown mode | |
130 | * @DFU_MODE_RUNTIME: Runtime mode | |
131 | * @DFU_MODE_DFU: Bootloader mode | |
132 | * | |
133 | * The mode enumerated kind. | |
134 | **/ | |
135 | typedef enum { | |
136 | DFU_MODE_UNKNOWN, | |
137 | DFU_MODE_RUNTIME, | |
138 | DFU_MODE_DFU, | |
139 | /*< private >*/ | |
140 | DFU_MODE_LAST | |
141 | } DfuMode; | |
142 | ||
143 | /** | |
144 | * DfuCipherKind: | |
145 | * @DFU_CIPHER_KIND_NONE: No cipher detected | |
146 | * @DFU_CIPHER_KIND_XTEA: XTEA cipher detected | |
147 | * | |
148 | * The type of cipher used for transfering the firmware. | |
149 | **/ | |
150 | typedef enum { | |
151 | DFU_CIPHER_KIND_NONE, | |
152 | DFU_CIPHER_KIND_XTEA, | |
153 | /*< private >*/ | |
154 | DFU_CIPHER_KIND_LAST | |
155 | } DfuCipherKind; | |
156 | ||
157 | #define DFU_METADATA_KEY_LICENSE "License" | |
158 | #define DFU_METADATA_KEY_COPYRIGHT "Copyright" | |
159 | #define DFU_METADATA_KEY_CIPHER_KIND "CipherKind" | |
160 | ||
161 | const gchar *dfu_state_to_string (DfuState state); | |
162 | const gchar *dfu_status_to_string (DfuStatus status); | |
163 | const gchar *dfu_mode_to_string (DfuMode mode); | |
164 | const gchar *dfu_cipher_kind_to_string (DfuCipherKind cipher_kind); | |
165 | ||
166 | G_END_DECLS | |
167 | ||
168 | #endif /* __DFU_COMMON_H */ |
0 | /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- | |
1 | * | |
2 | * Copyright (C) 2015 Richard Hughes <richard@hughsie.com> | |
3 | * | |
4 | * Licensed under the GNU Lesser General Public License Version 2.1 | |
5 | * | |
6 | * This library is free software; you can redistribute it and/or | |
7 | * modify it under the terms of the GNU Lesser General Public | |
8 | * License as published by the Free Software Foundation; either | |
9 | * version 2.1 of the License, or (at your option) any later version. | |
10 | * | |
11 | * This library is distributed in the hope that it will be useful, | |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
14 | * Lesser General Public License for more details. | |
15 | * | |
16 | * You should have received a copy of the GNU Lesser General Public | |
17 | * License along with this library; if not, write to the Free Software | |
18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | |
19 | */ | |
20 | ||
21 | /** | |
22 | * SECTION:dfu-context | |
23 | * @short_description: A system context for managing DFU-capable devices | |
24 | * | |
25 | * This object allows discovering and monitoring hotpluggable DFU devices. | |
26 | * | |
27 | * When using #DfuContext the device is given some time to re-enumerate after a | |
28 | * detach or reset. This allows client programs to continue using the #DfuDevice | |
29 | * without dealing with the device hotplug and the #GUsbDevice changing. | |
30 | * Using this object may be easier than using GUsbContext directly. | |
31 | * | |
32 | * Please be aware that after device detach or reset the number of #DfuTarget | |
33 | * objects may be different and so need to be re-requested. | |
34 | * | |
35 | * See also: #DfuDevice, #DfuTarget | |
36 | */ | |
37 | ||
38 | #include "config.h" | |
39 | ||
40 | #include <gusb.h> | |
41 | ||
42 | #include "dfu-device-private.h" | |
43 | #include "dfu-error.h" | |
44 | #include "dfu-context.h" | |
45 | ||
46 | static void dfu_context_finalize (GObject *object); | |
47 | ||
48 | /** | |
49 | * DfuContextPrivate: | |
50 | * | |
51 | * Private #DfuContext data | |
52 | **/ | |
53 | typedef struct { | |
54 | GUsbContext *usb_ctx; | |
55 | GPtrArray *devices; /* of DfuContextItem */ | |
56 | guint timeout; /* in ms */ | |
57 | } DfuContextPrivate; | |
58 | ||
59 | typedef struct { | |
60 | DfuContext *context; /* not refcounted */ | |
61 | DfuDevice *device; /* not refcounted */ | |
62 | guint timeout_id; | |
63 | guint state_change_id; | |
64 | } DfuContextItem; | |
65 | ||
66 | enum { | |
67 | SIGNAL_DEVICE_ADDED, | |
68 | SIGNAL_DEVICE_REMOVED, | |
69 | SIGNAL_DEVICE_CHANGED, | |
70 | SIGNAL_LAST | |
71 | }; | |
72 | ||
73 | static guint signals [SIGNAL_LAST] = { 0 }; | |
74 | ||
75 | G_DEFINE_TYPE_WITH_PRIVATE (DfuContext, dfu_context, G_TYPE_OBJECT) | |
76 | #define GET_PRIVATE(o) (dfu_context_get_instance_private (o)) | |
77 | ||
78 | /** | |
79 | * dfu_context_device_free: | |
80 | **/ | |
81 | static void | |
82 | dfu_context_device_free (DfuContextItem *item) | |
83 | { | |
84 | if (item->timeout_id > 0) | |
85 | g_source_remove (item->timeout_id); | |
86 | if (item->timeout_id > 0) { | |
87 | g_signal_handler_disconnect (item->device, | |
88 | item->state_change_id); | |
89 | } | |
90 | g_object_unref (item->device); | |
91 | g_free (item); | |
92 | } | |
93 | ||
94 | /** | |
95 | * dfu_context_class_init: | |
96 | **/ | |
97 | static void | |
98 | dfu_context_class_init (DfuContextClass *klass) | |
99 | { | |
100 | GObjectClass *object_class = G_OBJECT_CLASS (klass); | |
101 | ||
102 | /** | |
103 | * DfuContext::device-added: | |
104 | * @context: the #DfuContext instance that emitted the signal | |
105 | * @device: the #DfuDevice | |
106 | * | |
107 | * The ::device-added signal is emitted when a new DFU device is connected. | |
108 | * | |
109 | * Since: 0.5.4 | |
110 | **/ | |
111 | signals [SIGNAL_DEVICE_ADDED] = | |
112 | g_signal_new ("device-added", | |
113 | G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, | |
114 | G_STRUCT_OFFSET (DfuContextClass, device_added), | |
115 | NULL, NULL, g_cclosure_marshal_VOID__OBJECT, | |
116 | G_TYPE_NONE, 1, DFU_TYPE_DEVICE); | |
117 | ||
118 | /** | |
119 | * DfuContext::device-removed: | |
120 | * @context: the #DfuContext instance that emitted the signal | |
121 | * @device: the #DfuDevice | |
122 | * | |
123 | * The ::device-removed signal is emitted when a DFU device is removed. | |
124 | * | |
125 | * Since: 0.5.4 | |
126 | **/ | |
127 | signals [SIGNAL_DEVICE_REMOVED] = | |
128 | g_signal_new ("device-removed", | |
129 | G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, | |
130 | G_STRUCT_OFFSET (DfuContextClass, device_removed), | |
131 | NULL, NULL, g_cclosure_marshal_VOID__OBJECT, | |
132 | G_TYPE_NONE, 1, DFU_TYPE_DEVICE); | |
133 | ||
134 | /** | |
135 | * DfuContext::device-changed: | |
136 | * @context: the #DfuContext instance that emitted the signal | |
137 | * @device: the #DfuDevice | |
138 | * | |
139 | * The ::device-changed signal is emitted when a DFU device is changed, | |
140 | * typically when it has detached or been reset. | |
141 | * | |
142 | * Since: 0.5.4 | |
143 | **/ | |
144 | signals [SIGNAL_DEVICE_CHANGED] = | |
145 | g_signal_new ("device-changed", | |
146 | G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, | |
147 | G_STRUCT_OFFSET (DfuContextClass, device_changed), | |
148 | NULL, NULL, g_cclosure_marshal_VOID__OBJECT, | |
149 | G_TYPE_NONE, 1, DFU_TYPE_DEVICE); | |
150 | ||
151 | object_class->finalize = dfu_context_finalize; | |
152 | } | |
153 | ||
154 | /** | |
155 | * dfu_context_get_device_id: | |
156 | **/ | |
157 | static gchar * | |
158 | dfu_context_get_device_id (DfuDevice *device) | |
159 | { | |
160 | GUsbDevice *dev; | |
161 | dev = dfu_device_get_usb_dev (device); | |
162 | if (dev == NULL) | |
163 | return g_strdup (dfu_device_get_platform_id (device)); | |
164 | return g_strdup_printf ("%04x:%04x [%s]", | |
165 | g_usb_device_get_vid (dev), | |
166 | g_usb_device_get_pid (dev), | |
167 | g_usb_device_get_platform_id (dev)); | |
168 | } | |
169 | ||
170 | /** | |
171 | * dfu_context_find_item_by_platform_id: | |
172 | **/ | |
173 | static DfuContextItem * | |
174 | dfu_context_find_item_by_platform_id (DfuContext *context, const gchar *platform_id) | |
175 | { | |
176 | DfuContextPrivate *priv = GET_PRIVATE (context); | |
177 | DfuContextItem *item; | |
178 | guint i; | |
179 | ||
180 | /* do we have this device */ | |
181 | for (i = 0; i < priv->devices->len; i++) { | |
182 | item = g_ptr_array_index (priv->devices, i); | |
183 | if (g_strcmp0 (dfu_device_get_platform_id (item->device), platform_id) == 0) | |
184 | return item; | |
185 | } | |
186 | return NULL; | |
187 | } | |
188 | ||
189 | /** | |
190 | * dfu_context_remove_item: | |
191 | **/ | |
192 | static void | |
193 | dfu_context_remove_item (DfuContextItem *item) | |
194 | { | |
195 | DfuContextPrivate *priv = GET_PRIVATE (item->context); | |
196 | g_autofree gchar *device_id = NULL; | |
197 | ||
198 | /* log something */ | |
199 | device_id = dfu_context_get_device_id (item->device); | |
200 | g_debug ("%s was removed", device_id); | |
201 | ||
202 | g_signal_emit (item->context, signals[SIGNAL_DEVICE_REMOVED], 0, item->device); | |
203 | g_ptr_array_remove (priv->devices, item); | |
204 | } | |
205 | ||
206 | /** | |
207 | * dfu_context_device_timeout_cb: | |
208 | **/ | |
209 | static gboolean | |
210 | dfu_context_device_timeout_cb (gpointer user_data) | |
211 | { | |
212 | DfuContextItem *item = (DfuContextItem *) user_data; | |
213 | g_autofree gchar *device_id = NULL; | |
214 | ||
215 | /* bad firmware? */ | |
216 | device_id = dfu_context_get_device_id (item->device); | |
217 | g_debug ("%s did not come back as a DFU capable device", device_id); | |
218 | dfu_context_remove_item (item); | |
219 | return FALSE; | |
220 | } | |
221 | ||
222 | /** | |
223 | * dfu_context_device_state_cb: | |
224 | **/ | |
225 | static void | |
226 | dfu_context_device_state_cb (DfuDevice *device, DfuState state, DfuContext *context) | |
227 | { | |
228 | g_autofree gchar *device_id = NULL; | |
229 | device_id = dfu_context_get_device_id (device); | |
230 | g_debug ("%s state now: %s", device_id, dfu_state_to_string (state)); | |
231 | g_signal_emit (context, signals[SIGNAL_DEVICE_CHANGED], 0, device); | |
232 | } | |
233 | ||
234 | /** | |
235 | * dfu_context_device_added_cb: | |
236 | **/ | |
237 | static void | |
238 | dfu_context_device_added_cb (GUsbContext *usb_context, | |
239 | GUsbDevice *usb_device, | |
240 | DfuContext *context) | |
241 | { | |
242 | DfuContextPrivate *priv = GET_PRIVATE (context); | |
243 | DfuDevice *device; | |
244 | DfuContextItem *item; | |
245 | const gchar *platform_id; | |
246 | g_autofree gchar *device_id = NULL; | |
247 | g_autoptr(GError) error = NULL; | |
248 | ||
249 | /* are we waiting for this device to come back? */ | |
250 | platform_id = g_usb_device_get_platform_id (usb_device); | |
251 | item = dfu_context_find_item_by_platform_id (context, platform_id); | |
252 | if (item != NULL) { | |
253 | device_id = dfu_context_get_device_id (item->device); | |
254 | if (item->timeout_id > 0) { | |
255 | g_debug ("cancelling the remove timeout"); | |
256 | g_source_remove (item->timeout_id); | |
257 | item->timeout_id = 0; | |
258 | } | |
259 | ||
260 | /* try and be helpful; we may be a daemon like fwupd watching a | |
261 | * DFU device after dfu-tool or dfu-util has detached the | |
262 | * device on th command line */ | |
263 | if (!dfu_device_set_new_usb_dev (item->device, usb_device, NULL, &error)) | |
264 | g_warning ("Failed to set new device: %s", error->message); | |
265 | ||
266 | /* inform the UI */ | |
267 | g_signal_emit (context, signals[SIGNAL_DEVICE_CHANGED], 0, item->device); | |
268 | g_debug ("device %s came back", device_id); | |
269 | return; | |
270 | } | |
271 | ||
272 | /* is this a DFU-capable device */ | |
273 | device = dfu_device_new (usb_device); | |
274 | if (device == NULL) { | |
275 | g_debug ("device was not DFU capable"); | |
276 | return; | |
277 | } | |
278 | ||
279 | /* add */ | |
280 | item = g_new0 (DfuContextItem, 1); | |
281 | item->context = context; | |
282 | item->device = device; | |
283 | item->state_change_id = | |
284 | g_signal_connect (item->device, "state-changed", | |
285 | G_CALLBACK (dfu_context_device_state_cb), context); | |
286 | g_ptr_array_add (priv->devices, item); | |
287 | g_signal_emit (context, signals[SIGNAL_DEVICE_ADDED], 0, device); | |
288 | device_id = dfu_context_get_device_id (item->device); | |
289 | g_debug ("device %s was added", device_id); | |
290 | } | |
291 | ||
292 | /** | |
293 | * dfu_context_device_removed_cb: | |
294 | **/ | |
295 | static void | |
296 | dfu_context_device_removed_cb (GUsbContext *usb_context, | |
297 | GUsbDevice *usb_device, | |
298 | DfuContext *context) | |
299 | { | |
300 | DfuContextPrivate *priv = GET_PRIVATE (context); | |
301 | DfuContextItem *item; | |
302 | const gchar *platform_id; | |
303 | ||
304 | /* find the item */ | |
305 | platform_id = g_usb_device_get_platform_id (usb_device); | |
306 | item = dfu_context_find_item_by_platform_id (context, platform_id); | |
307 | if (item == NULL) | |
308 | return; | |
309 | ||
310 | /* mark the backing USB device as invalid */ | |
311 | dfu_device_set_new_usb_dev (item->device, NULL, NULL, NULL); | |
312 | ||
313 | /* this item has just detached */ | |
314 | if (item->timeout_id > 0) | |
315 | g_source_remove (item->timeout_id); | |
316 | item->timeout_id = | |
317 | g_timeout_add (priv->timeout, dfu_context_device_timeout_cb, item); | |
318 | } | |
319 | ||
320 | /** | |
321 | * dfu_context_init: | |
322 | **/ | |
323 | static void | |
324 | dfu_context_init (DfuContext *context) | |
325 | { | |
326 | DfuContextPrivate *priv = GET_PRIVATE (context); | |
327 | priv->timeout = 5000; | |
328 | priv->devices = g_ptr_array_new_with_free_func ((GDestroyNotify) dfu_context_device_free); | |
329 | priv->usb_ctx = g_usb_context_new (NULL); | |
330 | g_signal_connect (priv->usb_ctx, "device-added", | |
331 | G_CALLBACK (dfu_context_device_added_cb), context); | |
332 | g_signal_connect (priv->usb_ctx, "device-removed", | |
333 | G_CALLBACK (dfu_context_device_removed_cb), context); | |
334 | } | |
335 | ||
336 | /** | |
337 | * dfu_context_finalize: | |
338 | **/ | |
339 | static void | |
340 | dfu_context_finalize (GObject *object) | |
341 | { | |
342 | DfuContext *context = DFU_CONTEXT (object); | |
343 | DfuContextPrivate *priv = GET_PRIVATE (context); | |
344 | ||
345 | g_ptr_array_unref (priv->devices); | |
346 | g_object_unref (priv->usb_ctx); | |
347 | ||
348 | G_OBJECT_CLASS (dfu_context_parent_class)->finalize (object); | |
349 | } | |
350 | ||
351 | /** | |
352 | * dfu_context_new: | |
353 | * | |
354 | * Creates a new DFU context object. | |
355 | * | |
356 | * Return value: a new #DfuContext | |
357 | * | |
358 | * Since: 0.5.4 | |
359 | **/ | |
360 | DfuContext * | |
361 | dfu_context_new (void) | |
362 | { | |
363 | DfuContext *context; | |
364 | context = g_object_new (DFU_TYPE_CONTEXT, NULL); | |
365 | return context; | |
366 | } | |
367 | ||
368 | /** | |
369 | * dfu_context_get_timeout: | |
370 | * @context: a #DfuContext | |
371 | * | |
372 | * Gets the wait-for-replug timeout. | |
373 | * | |
374 | * Return value: value in milliseconds | |
375 | * | |
376 | * Since: 0.5.4 | |
377 | **/ | |
378 | guint | |
379 | dfu_context_get_timeout (DfuContext *context) | |
380 | { | |
381 | DfuContextPrivate *priv = GET_PRIVATE (context); | |
382 | g_return_val_if_fail (DFU_IS_CONTEXT (context), 0); | |
383 | return priv->timeout; | |
384 | } | |
385 | ||
386 | ||
387 | /** | |
388 | * dfu_context_set_timeout: | |
389 | * @context: a #DfuContext | |
390 | * @timeout: a timeout in milliseconds | |
391 | * | |
392 | * Sets the wait-for-replug timeout. | |
393 | * This is the longest we will wait for a device to re-enumerate after | |
394 | * disconnecting. Using longer values will result in any UI not updating in a | |
395 | * good time, but using too short values will result in devices being removed | |
396 | * and re-added as different #DfuDevice's. | |
397 | * | |
398 | * Since: 0.5.4 | |
399 | **/ | |
400 | void | |
401 | dfu_context_set_timeout (DfuContext *context, guint timeout) | |
402 | { | |
403 | DfuContextPrivate *priv = GET_PRIVATE (context); | |
404 | g_return_if_fail (DFU_IS_CONTEXT (context)); | |
405 | priv->timeout = timeout; | |
406 | } | |
407 | ||
408 | ||
409 | /** | |
410 | * dfu_context_enumerate: | |
411 | * @context: a #DfuContext | |
412 | * @error: a #GError, or %NULL | |
413 | * | |
414 | * Opens a DFU-capable context. | |
415 | * | |
416 | * Return value: %TRUE for success | |
417 | * | |
418 | * Since: 0.5.4 | |
419 | **/ | |
420 | gboolean | |
421 | dfu_context_enumerate (DfuContext *context, GError **error) | |
422 | { | |
423 | DfuContextPrivate *priv = GET_PRIVATE (context); | |
424 | g_return_val_if_fail (DFU_IS_CONTEXT (context), FALSE); | |
425 | g_return_val_if_fail (error == NULL || *error == NULL, FALSE); | |
426 | g_usb_context_enumerate (priv->usb_ctx); | |
427 | return TRUE; | |
428 | } | |
429 | ||
430 | /** | |
431 | * dfu_context_get_devices: | |
432 | * @context: a #DfuContext | |
433 | * | |
434 | * Gets all the DFU-capable devices on the system. | |
435 | * | |
436 | * Return value: (element-type DfuDevice) (transfer container): array of devices | |
437 | * | |
438 | * Since: 0.5.4 | |
439 | **/ | |
440 | GPtrArray * | |
441 | dfu_context_get_devices (DfuContext *context) | |
442 | { | |
443 | DfuContextPrivate *priv = GET_PRIVATE (context); | |
444 | DfuContextItem *item; | |
445 | GPtrArray *devices; | |
446 | guint i; | |
447 | ||
448 | g_return_val_if_fail (DFU_IS_CONTEXT (context), NULL); | |
449 | ||
450 | devices = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref); | |
451 | for (i = 0; i < priv->devices->len; i++) { | |
452 | item = g_ptr_array_index (priv->devices, i); | |
453 | g_ptr_array_add (devices, g_object_ref (item->device)); | |
454 | } | |
455 | return devices; | |
456 | } | |
457 | ||
458 | /** | |
459 | * dfu_context_get_device_by_vid_pid: | |
460 | * @context: a #DfuContext | |
461 | * @vid: a vendor ID | |
462 | * @pid: a product ID | |
463 | * @error: a #GError, or %NULL | |
464 | * | |
465 | * Finds a device in the context with a specific vendor:product ID. | |
466 | * An error is returned if more than one device matches. | |
467 | * | |
468 | * Return value: (transfer full): a #DfuDevice for success, or %NULL for an error | |
469 | * | |
470 | * Since: 0.5.4 | |
471 | **/ | |
472 | DfuDevice * | |
473 | dfu_context_get_device_by_vid_pid (DfuContext *context, | |
474 | guint16 vid, guint16 pid, | |
475 | GError **error) | |
476 | { | |
477 | DfuContextPrivate *priv = GET_PRIVATE (context); | |
478 | DfuContextItem *item; | |
479 | DfuDevice *device = NULL; | |
480 | GUsbDevice *dev; | |
481 | guint i; | |
482 | ||
483 | g_return_val_if_fail (DFU_IS_CONTEXT (context), FALSE); | |
484 | g_return_val_if_fail (error == NULL || *error == NULL, FALSE); | |
485 | ||
486 | /* search all devices */ | |
487 | for (i = 0; i < priv->devices->len; i++) { | |
488 | ||
489 | /* match */ | |
490 | item = g_ptr_array_index (priv->devices, i); | |
491 | dev = dfu_device_get_usb_dev (item->device); | |
492 | if (g_usb_device_get_vid (dev) == vid && | |
493 | g_usb_device_get_pid (dev) == pid) { | |
494 | if (device != NULL) { | |
495 | g_set_error (error, | |
496 | DFU_ERROR, | |
497 | DFU_ERROR_INVALID_DEVICE, | |
498 | "multiple device matches for %04x:%04x", | |
499 | vid, pid); | |
500 | return NULL; | |
501 | } | |
502 | device = item->device; | |
503 | continue; | |
504 | } | |
505 | } | |
506 | if (device == NULL) { | |
507 | g_set_error (error, | |
508 | DFU_ERROR, | |
509 | DFU_ERROR_NOT_FOUND, | |
510 | "no device matches for %04x:%04x", | |
511 | vid, pid); | |
512 | return NULL; | |
513 | } | |
514 | return g_object_ref (device); | |
515 | } | |
516 | ||
517 | /** | |
518 | * dfu_context_get_device_by_platform_id: | |
519 | * @context: a #DfuContext | |
520 | * @platform_id: a platform ID | |
521 | * @error: a #GError, or %NULL | |
522 | * | |
523 | * Finds a device in the context with a specific platform ID. | |
524 | * | |
525 | * Return value: (transfer full): a #DfuDevice for success, or %NULL for an error | |
526 | * | |
527 | * Since: 0.5.4 | |
528 | **/ | |
529 | DfuDevice * | |
530 | dfu_context_get_device_by_platform_id (DfuContext *context, | |
531 | const gchar *platform_id, | |
532 | GError **error) | |
533 | { | |
534 | DfuContextPrivate *priv = GET_PRIVATE (context); | |
535 | DfuContextItem *item; | |
536 | guint i; | |
537 | ||
538 | g_return_val_if_fail (DFU_IS_CONTEXT (context), FALSE); | |
539 | g_return_val_if_fail (error == NULL || *error == NULL, FALSE); | |
540 | ||
541 | /* search all devices */ | |
542 | for (i = 0; i < priv->devices->len; i++) { | |
543 | item = g_ptr_array_index (priv->devices, i); | |
544 | if (g_strcmp0 (dfu_device_get_platform_id (item->device), | |
545 | platform_id) == 0) { | |
546 | return g_object_ref (item->device); | |
547 | } | |
548 | } | |
549 | g_set_error (error, | |
550 | DFU_ERROR, | |
551 | DFU_ERROR_NOT_FOUND, | |
552 | "no device matches for %s", | |
553 | platform_id); | |
554 | return NULL; | |
555 | } | |
556 | ||
557 | /** | |
558 | * dfu_context_get_device_default: | |
559 | * @context: a #DfuContext | |
560 | * @error: a #GError, or %NULL | |
561 | * | |
562 | * Gets the default device in the context. | |
563 | * An error is returned if more than one device exists. | |
564 | * | |
565 | * Return value: (transfer full): a #DfuDevice for success, or %NULL for an error | |
566 | * | |
567 | * Since: 0.5.4 | |
568 | **/ | |
569 | DfuDevice * | |
570 | dfu_context_get_device_default (DfuContext *context, GError **error) | |
571 | { | |
572 | DfuContextPrivate *priv = GET_PRIVATE (context); | |
573 | DfuContextItem *item; | |
574 | ||
575 | g_return_val_if_fail (DFU_IS_CONTEXT (context), FALSE); | |
576 | g_return_val_if_fail (error == NULL || *error == NULL, FALSE); | |
577 | ||
578 | /* none */ | |
579 | if (priv->devices->len == 0) { | |
580 | g_set_error_literal (error, | |
581 | DFU_ERROR, | |
582 | DFU_ERROR_NOT_FOUND, | |
583 | "no attached DFU device"); | |
584 | return NULL; | |
585 | } | |
586 | ||
587 | /* multiple */ | |
588 | if (priv->devices->len > 1) { | |
589 | g_set_error_literal (error, | |
590 | DFU_ERROR, | |
591 | DFU_ERROR_INVALID_DEVICE, | |
592 | "more than one attached DFU device"); | |
593 | return NULL; | |
594 | } | |
595 | item = g_ptr_array_index (priv->devices, 0); | |
596 | return g_object_ref (item->device); | |
597 | } |
0 | /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- | |
1 | * | |
2 | * Copyright (C) 2015 Richard Hughes <richard@hughsie.com> | |
3 | * | |
4 | * Licensed under the GNU Lesser General Public License Version 2.1 | |
5 | * | |
6 | * This library is free software; you can redistribute it and/or | |
7 | * modify it under the terms of the GNU Lesser General Public | |
8 | * License as published by the Free Software Foundation; either | |
9 | * version 2.1 of the License, or (at your option) any later version. | |
10 | * | |
11 | * This library is distributed in the hope that it will be useful, | |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
14 | * Lesser General Public License for more details. | |
15 | * | |
16 | * You should have received a copy of the GNU Lesser General Public | |
17 | * License along with this library; if not, write to the Free Software | |
18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | |
19 | */ | |
20 | ||
21 | #ifndef __DFU_CONTEXT_H | |
22 | #define __DFU_CONTEXT_H | |
23 | ||
24 | #include <glib-object.h> | |
25 | #include <gio/gio.h> | |
26 | ||
27 | #include "dfu-device.h" | |
28 | ||
29 | G_BEGIN_DECLS | |
30 | ||
31 | #define DFU_TYPE_CONTEXT (dfu_context_get_type ()) | |
32 | G_DECLARE_DERIVABLE_TYPE (DfuContext, dfu_context, DFU, CONTEXT, GObject) | |
33 | ||
34 | struct _DfuContextClass | |
35 | { | |
36 | GObjectClass parent_class; | |
37 | void (*device_added) (DfuContext *context, | |
38 | DfuDevice *device); | |
39 | void (*device_removed) (DfuContext *context, | |
40 | DfuDevice *device); | |
41 | void (*device_changed) (DfuContext *context, | |
42 | DfuDevice *device); | |
43 | /*< private >*/ | |
44 | /* Padding for future expansion */ | |
45 | void (*_dfu_context_reserved1) (void); | |
46 | void (*_dfu_context_reserved2) (void); | |
47 | void (*_dfu_context_reserved3) (void); | |
48 | void (*_dfu_context_reserved4) (void); | |
49 | void (*_dfu_context_reserved5) (void); | |
50 | void (*_dfu_context_reserved6) (void); | |
51 | void (*_dfu_context_reserved7) (void); | |
52 | void (*_dfu_context_reserved8) (void); | |
53 | void (*_dfu_context_reserved9) (void); | |
54 | }; | |
55 | ||
56 | DfuContext *dfu_context_new (void); | |
57 | gboolean dfu_context_enumerate (DfuContext *context, | |
58 | GError **error); | |
59 | GPtrArray *dfu_context_get_devices (DfuContext *context); | |
60 | guint dfu_context_get_timeout (DfuContext *context); | |
61 | void dfu_context_set_timeout (DfuContext *context, | |
62 | guint timeout); | |
63 | DfuDevice *dfu_context_get_device_by_vid_pid (DfuContext *context, | |
64 | guint16 vid, | |
65 | guint16 pid, | |
66 | GError **error); | |
67 | DfuDevice *dfu_context_get_device_by_platform_id (DfuContext *context, | |
68 | const gchar *platform_id, | |
69 | GError **error); | |
70 | DfuDevice *dfu_context_get_device_default (DfuContext *context, | |
71 | GError **error); | |
72 | ||
73 | G_END_DECLS | |
74 | ||
75 | #endif /* __DFU_CONTEXT_H */ |
0 | /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- | |
1 | * | |
2 | * Copyright (C) 2015 Richard Hughes <richard@hughsie.com> | |
3 | * | |
4 | * Licensed under the GNU Lesser General Public License Version 2.1 | |
5 | * | |
6 | * This library is free software; you can redistribute it and/or | |
7 | * modify it under the terms of the GNU Lesser General Public | |
8 | * License as published by the Free Software Foundation; either | |
9 | * version 2.1 of the License, or (at your option) any later version. | |
10 | * | |
11 | * This library is distributed in the hope that it will be useful, | |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
14 | * Lesser General Public License for more details. | |
15 | * | |
16 | * You should have received a copy of the GNU Lesser General Public | |
17 | * License along with this library; if not, write to the Free Software | |
18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | |
19 | */ | |
20 | ||
21 | #ifndef __DFU_DEVICE_PRIVATE_H | |
22 | #define __DFU_DEVICE_PRIVATE_H | |
23 | ||
24 | #include <glib-object.h> | |
25 | #include <gio/gio.h> | |
26 | #include <gusb.h> | |
27 | ||
28 | #include "dfu-device.h" | |
29 | ||
30 | G_BEGIN_DECLS | |
31 | ||
32 | #define DFU_DEVICE_REPLUG_TIMEOUT 5000 /* ms */ | |
33 | ||
34 | /** | |
35 | * DfuDeviceQuirks: | |
36 | * @DFU_DEVICE_QUIRK_NONE: No device quirks | |
37 | * @DFU_DEVICE_QUIRK_IGNORE_POLLTIMEOUT: Ignore the device download timeout | |
38 | * @DFU_DEVICE_QUIRK_FORCE_DFU_MODE: Force DFU mode | |
39 | * @DFU_DEVICE_QUIRK_IGNORE_INVALID_VERSION: Ignore invalid version numbers | |
40 | * @DFU_DEVICE_QUIRK_USE_PROTOCOL_ZERO: Fix up the protocol number | |
41 | * @DFU_DEVICE_QUIRK_NO_PID_CHANGE: Accept the same VID:PID when changing modes | |
42 | * @DFU_DEVICE_QUIRK_NO_GET_STATUS_UPLOAD: Do not do GetStatus when uploading | |
43 | * @DFU_DEVICE_QUIRK_NO_DFU_RUNTIME: No DFU runtime interface is provided | |
44 | * @DFU_DEVICE_QUIRK_ATTACH_UPLOAD_DOWNLOAD: An upload or download is required for attach | |
45 | * | |
46 | * The workarounds for different devices. | |
47 | **/ | |
48 | typedef enum { | |
49 | DFU_DEVICE_QUIRK_NONE = 0, | |
50 | DFU_DEVICE_QUIRK_IGNORE_POLLTIMEOUT = (1 << 0), | |
51 | DFU_DEVICE_QUIRK_FORCE_DFU_MODE = (1 << 1), | |
52 | DFU_DEVICE_QUIRK_IGNORE_INVALID_VERSION = (1 << 2), | |
53 | DFU_DEVICE_QUIRK_USE_PROTOCOL_ZERO = (1 << 3), | |
54 | DFU_DEVICE_QUIRK_NO_PID_CHANGE = (1 << 4), | |
55 | DFU_DEVICE_QUIRK_NO_GET_STATUS_UPLOAD = (1 << 5), | |
56 | DFU_DEVICE_QUIRK_NO_DFU_RUNTIME = (1 << 6), | |
57 | DFU_DEVICE_QUIRK_ATTACH_UPLOAD_DOWNLOAD = (1 << 7), | |
58 | /*< private >*/ | |
59 | DFU_DEVICE_QUIRK_LAST | |
60 | } DfuDeviceQuirks; | |
61 | ||
62 | /** | |
63 | * DfuDeviceAttributes: | |
64 | * @DFU_DEVICE_ATTRIBUTE_NONE: No attributes set | |
65 | * @DFU_DEVICE_ATTRIBUTE_CAN_DOWNLOAD: Can download from host->device | |
66 | * @DFU_DEVICE_ATTRIBUTE_CAN_UPLOAD: Can upload from device->host | |
67 | * @DFU_DEVICE_ATTRIBUTE_MANIFEST_TOL: Can answer GetStatus in manifest | |
68 | * @DFU_DEVICE_ATTRIBUTE_WILL_DETACH: Will self-detach | |
69 | * @DFU_DEVICE_ATTRIBUTE_CAN_ACCELERATE: Use a larger transfer size for speed | |
70 | * | |
71 | * The device DFU attributes. | |
72 | **/ | |
73 | typedef enum { | |
74 | DFU_DEVICE_ATTRIBUTE_NONE = 0, | |
75 | DFU_DEVICE_ATTRIBUTE_CAN_DOWNLOAD = (1 << 0), | |
76 | DFU_DEVICE_ATTRIBUTE_CAN_UPLOAD = (1 << 1), | |
77 | DFU_DEVICE_ATTRIBUTE_MANIFEST_TOL = (1 << 2), | |
78 | DFU_DEVICE_ATTRIBUTE_WILL_DETACH = (1 << 3), | |
79 | DFU_DEVICE_ATTRIBUTE_CAN_ACCELERATE = (1 << 7), | |
80 | /*< private >*/ | |
81 | DFU_DEVICE_ATTRIBUTE_LAST | |
82 | } DfuDeviceAttributes; | |
83 | ||
84 | GUsbDevice *dfu_device_get_usb_dev (DfuDevice *device); | |
85 | ||
86 | gboolean dfu_device_has_dfuse_support (DfuDevice *device); | |
87 | gboolean dfu_device_has_attribute (DfuDevice *device, | |
88 | DfuDeviceAttributes attribute); | |
89 | gboolean dfu_device_has_quirk (DfuDevice *device, | |
90 | DfuDeviceQuirks quirk); | |
91 | ||
92 | void dfu_device_error_fixup (DfuDevice *device, | |
93 | GCancellable *cancellable, | |
94 | GError **error); | |
95 | guint dfu_device_get_download_timeout (DfuDevice *device); | |
96 | gchar *dfu_device_get_quirks_as_string (DfuDevice *device); | |
97 | gboolean dfu_device_set_new_usb_dev (DfuDevice *device, | |
98 | GUsbDevice *dev, | |
99 | GCancellable *cancellable, | |
100 | GError **error); | |
101 | ||
102 | G_END_DECLS | |
103 | ||
104 | #endif /* __DFU_DEVICE_PRIVATE_H */ |
0 | /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- | |
1 | * | |
2 | * Copyright (C) 2015 Richard Hughes <richard@hughsie.com> | |
3 | * | |
4 | * Licensed under the GNU Lesser General Public License Version 2.1 | |
5 | * | |
6 | * This library is free software; you can redistribute it and/or | |
7 | * modify it under the terms of the GNU Lesser General Public | |
8 | * License as published by the Free Software Foundation; either | |
9 | * version 2.1 of the License, or (at your option) any later version. | |
10 | * | |
11 | * This library is distributed in the hope that it will be useful, | |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
14 | * Lesser General Public License for more details. | |
15 | * | |
16 | * You should have received a copy of the GNU Lesser General Public | |
17 | * License along with this library; if not, write to the Free Software | |
18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | |
19 | */ | |
20 | ||
21 | /** | |
22 | * SECTION:dfu-device | |
23 | * @short_description: Object representing a DFU-capable device | |
24 | * | |
25 | * This object allows two things: | |
26 | * | |
27 | * - Downloading from the host to the device, optionally with | |
28 | * verification using a DFU or DfuSe firmware file. | |
29 | * | |
30 | * - Uploading from the device to the host to a DFU or DfuSe firmware | |
31 | * file. The file format is chosen automatically, with DfuSe being | |
32 | * chosen if the device contains more than one target. | |
33 | * | |
34 | * See also: #DfuTarget, #DfuFirmware | |
35 | */ | |
36 | ||
37 | #include "config.h" | |
38 | ||
39 | #include <string.h> | |
40 | ||
41 | #include "dfu-common.h" | |
42 | #include "dfu-device-private.h" | |
43 | #include "dfu-error.h" | |
44 | #include "dfu-target-private.h" | |
45 | ||
46 | static void dfu_device_finalize (GObject *object); | |
47 | ||
48 | /** | |
49 | * DfuDevicePrivate: | |
50 | * | |
51 | * Private #DfuDevice data | |
52 | **/ | |
53 | typedef struct { | |
54 | DfuDeviceAttributes attributes; | |
55 | DfuDeviceQuirks quirks; | |
56 | DfuMode mode; | |
57 | DfuState state; | |
58 | DfuStatus status; | |
59 | GPtrArray *targets; | |
60 | GUsbDevice *dev; | |
61 | gboolean open_new_dev; /* if set new GUsbDevice */ | |
62 | gboolean dfuse_supported; | |
63 | gboolean done_upload_or_download; | |
64 | gchar *display_name; | |
65 | gchar *platform_id; | |
66 | guint16 runtime_pid; | |
67 | guint16 runtime_vid; | |
68 | guint16 runtime_release; | |
69 | guint16 transfer_size; | |
70 | guint8 iface_number; | |
71 | guint dnload_timeout; | |
72 | guint timeout_ms; | |
73 | } DfuDevicePrivate; | |
74 | ||
75 | enum { | |
76 | SIGNAL_STATUS_CHANGED, | |
77 | SIGNAL_STATE_CHANGED, | |
78 | SIGNAL_PERCENTAGE_CHANGED, | |
79 | SIGNAL_LAST | |
80 | }; | |
81 | ||
82 | static guint signals [SIGNAL_LAST] = { 0 }; | |
83 | ||
84 | G_DEFINE_TYPE_WITH_PRIVATE (DfuDevice, dfu_device, G_TYPE_OBJECT) | |
85 | #define GET_PRIVATE(o) (dfu_device_get_instance_private (o)) | |
86 | ||
87 | /** | |
88 | * dfu_device_class_init: | |
89 | **/ | |
90 | static void | |
91 | dfu_device_class_init (DfuDeviceClass *klass) | |
92 | { | |
93 | GObjectClass *object_class = G_OBJECT_CLASS (klass); | |
94 | ||
95 | /** | |
96 | * DfuDevice::status-changed: | |
97 | * @device: the #DfuDevice instance that emitted the signal | |
98 | * @status: the new #DfuStatus | |
99 | * | |
100 | * The ::status-changed signal is emitted when the status changes. | |
101 | * | |
102 | * Since: 0.5.4 | |
103 | **/ | |
104 | signals [SIGNAL_STATUS_CHANGED] = | |
105 | g_signal_new ("status-changed", | |
106 | G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, | |
107 | G_STRUCT_OFFSET (DfuDeviceClass, status_changed), | |
108 | NULL, NULL, g_cclosure_marshal_VOID__UINT, | |
109 | G_TYPE_NONE, 1, G_TYPE_UINT); | |
110 | ||
111 | /** | |
112 | * DfuDevice::state-changed: | |
113 | * @device: the #DfuDevice instance that emitted the signal | |
114 | * @state: the new #DfuState | |
115 | * | |
116 | * The ::state-changed signal is emitted when the state changes. | |
117 | * | |
118 | * Since: 0.5.4 | |
119 | **/ | |
120 | signals [SIGNAL_STATE_CHANGED] = | |
121 | g_signal_new ("state-changed", | |
122 | G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, | |
123 | G_STRUCT_OFFSET (DfuDeviceClass, state_changed), | |
124 | NULL, NULL, g_cclosure_marshal_VOID__UINT, | |
125 | G_TYPE_NONE, 1, G_TYPE_UINT); | |
126 | ||
127 | /** | |
128 | * DfuDevice::percentage-changed: | |
129 | * @device: the #DfuDevice instance that emitted the signal | |
130 | * @percentage: the new percentage | |
131 | * | |
132 | * The ::percentage-changed signal is emitted when the percentage changes. | |
133 | * | |
134 | * Since: 0.5.4 | |
135 | **/ | |
136 | signals [SIGNAL_PERCENTAGE_CHANGED] = | |
137 | g_signal_new ("percentage-changed", | |
138 | G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, | |
139 | G_STRUCT_OFFSET (DfuDeviceClass, percentage_changed), | |
140 | NULL, NULL, g_cclosure_marshal_VOID__UINT, | |
141 | G_TYPE_NONE, 1, G_TYPE_UINT); | |
142 | ||
143 | object_class->finalize = dfu_device_finalize; | |
144 | } | |
145 | ||
146 | /** | |
147 | * dfu_device_init: | |
148 | **/ | |
149 | static void | |
150 | dfu_device_init (DfuDevice *device) | |
151 | { | |
152 | DfuDevicePrivate *priv = GET_PRIVATE (device); | |
153 | priv->iface_number = 0xff; | |
154 | priv->runtime_pid = 0xffff; | |
155 | priv->runtime_vid = 0xffff; | |
156 | priv->runtime_release = 0xffff; | |
157 | priv->state = DFU_STATE_APP_IDLE; | |
158 | priv->status = DFU_STATUS_OK; | |
159 | priv->targets = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref); | |
160 | priv->timeout_ms = 500; | |
161 | priv->transfer_size = 64; | |
162 | } | |
163 | ||
164 | /** | |
165 | * dfu_device_get_transfer_size: | |
166 | * @device: a #GUsbDevice | |
167 | * | |
168 | * Gets the transfer size in bytes. | |
169 | * | |
170 | * Return value: packet size, or 0 for unknown | |
171 | * | |
172 | * Since: 0.5.4 | |
173 | **/ | |
174 | guint16 | |
175 | dfu_device_get_transfer_size (DfuDevice *device) | |
176 | { | |
177 | DfuDevicePrivate *priv = GET_PRIVATE (device); | |
178 | g_return_val_if_fail (DFU_IS_DEVICE (device), 0xffff); | |
179 | return priv->transfer_size; | |
180 | } | |
181 | ||
182 | /** | |
183 | * dfu_device_get_download_timeout: | |
184 | * @device: a #GUsbDevice | |
185 | * | |
186 | * Gets the download timeout in ms. | |
187 | * | |
188 | * Return value: delay, or 0 for unknown | |
189 | * | |
190 | * Since: 0.5.4 | |
191 | **/ | |
192 | guint | |
193 | dfu_device_get_download_timeout (DfuDevice *device) | |
194 | { | |
195 | DfuDevicePrivate *priv = GET_PRIVATE (device); | |
196 | g_return_val_if_fail (DFU_IS_DEVICE (device), 0); | |
197 | return priv->dnload_timeout; | |
198 | } | |
199 | ||
200 | /** | |
201 | * dfu_device_set_transfer_size: | |
202 | * @device: a #GUsbDevice | |
203 | * @transfer_size: maximum packet size | |
204 | * | |
205 | * Sets the transfer size in bytes. | |
206 | * | |
207 | * Since: 0.5.4 | |
208 | **/ | |
209 | void | |
210 | dfu_device_set_transfer_size (DfuDevice *device, guint16 transfer_size) | |
211 | { | |
212 | DfuDevicePrivate *priv = GET_PRIVATE (device); | |
213 | g_return_if_fail (DFU_IS_DEVICE (device)); | |
214 | priv->transfer_size = transfer_size; | |
215 | } | |
216 | ||
217 | /** | |
218 | * dfu_device_finalize: | |
219 | **/ | |
220 | static void | |
221 | dfu_device_finalize (GObject *object) | |
222 | { | |
223 | DfuDevice *device = DFU_DEVICE (object); | |
224 | DfuDevicePrivate *priv = GET_PRIVATE (device); | |
225 | ||
226 | /* don't rely on this */ | |
227 | if (priv->dev != NULL) | |
228 | g_usb_device_close (priv->dev, NULL); | |
229 | ||
230 | g_free (priv->display_name); | |
231 | g_free (priv->platform_id); | |
232 | g_ptr_array_unref (priv->targets); | |
233 | ||
234 | G_OBJECT_CLASS (dfu_device_parent_class)->finalize (object); | |
235 | } | |
236 | ||
237 | typedef struct __attribute__((packed)) { | |
238 | guint8 bLength; | |
239 | guint8 bDescriptorType; | |
240 | guint8 bmAttributes; | |
241 | guint16 wDetachTimeOut; | |
242 | guint16 wTransferSize; | |
243 | guint16 bcdDFUVersion; | |
244 | } DfuFuncDescriptor; | |
245 | ||
246 | /** | |
247 | * dfu_device_parse_iface_data: | |
248 | **/ | |
249 | static void | |
250 | dfu_device_parse_iface_data (DfuDevice *device, GBytes *iface_data) | |
251 | { | |
252 | DfuDevicePrivate *priv = GET_PRIVATE (device); | |
253 | const DfuFuncDescriptor *desc; | |
254 | gsize iface_data_length; | |
255 | ||
256 | /* parse the functional descriptor */ | |
257 | desc = g_bytes_get_data (iface_data, &iface_data_length); | |
258 | if (iface_data_length != 0x09) { | |
259 | g_warning ("interface found, but not interface data"); | |
260 | return; | |
261 | } | |
262 | ||
263 | /* check sanity */ | |
264 | if (desc->bLength != 0x09) { | |
265 | g_warning ("DFU interface data has incorrect length: 0x%02x", | |
266 | desc->bLength); | |
267 | } | |
268 | ||
269 | /* check transfer size */ | |
270 | priv->transfer_size = desc->wTransferSize; | |
271 | if (priv->transfer_size == 0x0000) { | |
272 | g_warning ("DFU transfer size invalid, using default: 0x%04x", | |
273 | desc->wTransferSize); | |
274 | priv->transfer_size = 64; | |
275 | } | |
276 | ||
277 | /* check DFU version */ | |
278 | if (priv->quirks & DFU_DEVICE_QUIRK_IGNORE_INVALID_VERSION) { | |
279 | g_debug ("ignoring quirked DFU version"); | |
280 | } else { | |
281 | if (desc->bcdDFUVersion == 0x0100 || | |
282 | desc->bcdDFUVersion == 0x0101) { | |
283 | g_debug ("basic DFU, no DfuSe support"); | |
284 | priv->dfuse_supported = FALSE; | |
285 | } else if (desc->bcdDFUVersion == 0x011a) { | |
286 | g_debug ("DfuSe support"); | |
287 | priv->dfuse_supported = TRUE; | |
288 | } else { | |
289 | g_warning ("DFU version is invalid: 0x%04x", | |
290 | desc->bcdDFUVersion); | |
291 | } | |
292 | } | |
293 | ||
294 | /* ST-specific */ | |
295 | if (priv->dfuse_supported && | |
296 | desc->bmAttributes & DFU_DEVICE_ATTRIBUTE_CAN_ACCELERATE) | |
297 | priv->transfer_size = 0x1000; | |
298 | ||
299 | /* get attributes about the DFU operation */ | |
300 | priv->attributes = desc->bmAttributes; | |
301 | } | |
302 | ||
303 | /** | |
304 | * dfu_device_update_from_iface: | |
305 | **/ | |
306 | static gboolean | |
307 | dfu_device_update_from_iface (DfuDevice *device, GUsbInterface *iface) | |
308 | { | |
309 | DfuMode target_mode = DFU_MODE_UNKNOWN; | |
310 | DfuDevicePrivate *priv = GET_PRIVATE (device); | |
311 | ||
312 | /* runtime */ | |
313 | if (g_usb_interface_get_protocol (iface) == 0x01) | |
314 | target_mode = DFU_MODE_RUNTIME; | |
315 | ||
316 | /* DFU */ | |
317 | if (g_usb_interface_get_protocol (iface) == 0x02) | |
318 | target_mode = DFU_MODE_DFU; | |
319 | ||
320 | /* the DSO Nano has uses 0 instead of 2 when in DFU target_mode */ | |
321 | if (dfu_device_has_quirk (device, DFU_DEVICE_QUIRK_USE_PROTOCOL_ZERO) && | |
322 | g_usb_interface_get_protocol (iface) == 0x00) | |
323 | target_mode = DFU_MODE_DFU; | |
324 | ||
325 | /* nothing found */ | |
326 | if (target_mode == DFU_MODE_UNKNOWN) | |
327 | return FALSE; | |
328 | ||
329 | /* in DFU mode, the interface is supposed to be 0 */ | |
330 | if (target_mode == DFU_MODE_DFU && g_usb_interface_get_number (iface) != 0) | |
331 | g_warning ("iface has to be 0 in DFU mode, got 0x%02i", | |
332 | g_usb_interface_get_number (iface)); | |
333 | ||
334 | /* some devices set the wrong mode */ | |
335 | if (dfu_device_has_quirk (device, DFU_DEVICE_QUIRK_FORCE_DFU_MODE)) | |
336 | target_mode = DFU_MODE_DFU; | |
337 | ||
338 | /* save for reset */ | |
339 | if (target_mode == DFU_MODE_RUNTIME || | |
340 | (priv->quirks & DFU_DEVICE_QUIRK_NO_PID_CHANGE)) { | |
341 | priv->runtime_vid = g_usb_device_get_vid (priv->dev); | |
342 | priv->runtime_pid = g_usb_device_get_pid (priv->dev); | |
343 | priv->runtime_release = g_usb_device_get_release (priv->dev); | |
344 | } | |
345 | ||
346 | priv->mode = target_mode; | |
347 | return TRUE; | |
348 | } | |
349 | ||
350 | /** | |
351 | * dfu_device_add_targets: | |
352 | **/ | |
353 | static gboolean | |
354 | dfu_device_add_targets (DfuDevice *device) | |
355 | { | |
356 | DfuDevicePrivate *priv = GET_PRIVATE (device); | |
357 | guint i; | |
358 | GUsbInterface *iface; | |
359 | g_autoptr(GPtrArray) ifaces = NULL; | |
360 | ||
361 | /* add all DFU-capable targets */ | |
362 | ifaces = g_usb_device_get_interfaces (priv->dev, NULL); | |
363 | if (ifaces == NULL) | |
364 | return FALSE; | |
365 | g_ptr_array_set_size (priv->targets, 0); | |
366 | for (i = 0; i < ifaces->len; i++) { | |
367 | GBytes *iface_data = NULL; | |
368 | DfuTarget *target; | |
369 | iface = g_ptr_array_index (ifaces, i); | |
370 | if (g_usb_interface_get_class (iface) != G_USB_DEVICE_CLASS_APPLICATION_SPECIFIC) | |
371 | continue; | |
372 | if (g_usb_interface_get_subclass (iface) != 0x01) | |
373 | continue; | |
374 | target = dfu_target_new (device, iface); | |
375 | if (target == NULL) | |
376 | continue; | |
377 | ||
378 | /* add target */ | |
379 | priv->iface_number = g_usb_interface_get_number (iface); | |
380 | g_ptr_array_add (priv->targets, target); | |
381 | dfu_device_update_from_iface (device, iface); | |
382 | ||
383 | /* parse any interface data */ | |
384 | iface_data = g_usb_interface_get_extra (iface); | |
385 | if (g_bytes_get_size (iface_data) > 0) | |
386 | dfu_device_parse_iface_data (device, iface_data); | |
387 | } | |
388 | ||
389 | /* the device has no DFU runtime, so cheat */ | |
390 | if (priv->quirks & DFU_DEVICE_QUIRK_NO_DFU_RUNTIME) { | |
391 | if (priv->targets->len == 0) { | |
392 | g_debug ("no DFU runtime, so faking device"); | |
393 | priv->iface_number = 0xff; | |
394 | } | |
395 | return TRUE; | |
396 | } | |
397 | ||
398 | return priv->targets->len > 0; | |
399 | } | |
400 | ||
401 | /** | |
402 | * dfu_device_has_quirk: (skip) | |
403 | * @device: A #DfuDevice | |
404 | * @quirk: A #DfuDeviceQuirks | |
405 | * | |
406 | * Returns if a device has a specific quirk | |
407 | * | |
408 | * Return value: %TRUE if the device has this quirk | |
409 | * | |
410 | * Since: 0.5.4 | |
411 | **/ | |
412 | gboolean | |
413 | dfu_device_has_quirk (DfuDevice *device, DfuDeviceQuirks quirk) | |
414 | { | |
415 | DfuDevicePrivate *priv = GET_PRIVATE (device); | |
416 | g_return_val_if_fail (DFU_IS_DEVICE (device), 0x0); | |
417 | return (priv->quirks & quirk) > 0; | |
418 | } | |
419 | ||
420 | /** | |
421 | * dfu_device_can_upload: | |
422 | * @device: a #GUsbDevice | |
423 | * | |
424 | * Gets if the device can upload. | |
425 | * | |
426 | * Return value: %TRUE if the device can upload from device to host | |
427 | * | |
428 | * Since: 0.5.4 | |
429 | **/ | |
430 | gboolean | |
431 | dfu_device_can_upload (DfuDevice *device) | |
432 | { | |
433 | DfuDevicePrivate *priv = GET_PRIVATE (device); | |
434 | g_return_val_if_fail (DFU_IS_DEVICE (device), FALSE); | |
435 | return (priv->attributes & DFU_DEVICE_ATTRIBUTE_CAN_UPLOAD) > 0; | |
436 | } | |
437 | ||
438 | /** | |
439 | * dfu_device_can_download: | |
440 | * @device: a #GUsbDevice | |
441 | * | |
442 | * Gets if the device can download. | |
443 | * | |
444 | * Return value: %TRUE if the device can download from host to device | |
445 | * | |
446 | * Since: 0.5.4 | |
447 | **/ | |
448 | gboolean | |
449 | dfu_device_can_download (DfuDevice *device) | |
450 | { | |
451 | DfuDevicePrivate *priv = GET_PRIVATE (device); | |
452 | g_return_val_if_fail (DFU_IS_DEVICE (device), FALSE); | |
453 | return (priv->attributes & DFU_DEVICE_ATTRIBUTE_CAN_DOWNLOAD) > 0; | |
454 | } | |
455 | ||
456 | /** | |
457 | * dfu_device_set_timeout: | |
458 | * @device: a #DfuDevice | |
459 | * @timeout_ms: the timeout in ms | |
460 | * | |
461 | * Sets the USB timeout to use when contacting the USB device. | |
462 | * | |
463 | * Since: 0.5.4 | |
464 | **/ | |
465 | void | |
466 | dfu_device_set_timeout (DfuDevice *device, guint timeout_ms) | |
467 | { | |
468 | DfuDevicePrivate *priv = GET_PRIVATE (device); | |
469 | g_return_if_fail (DFU_IS_DEVICE (device)); | |
470 | priv->timeout_ms = timeout_ms; | |
471 | } | |
472 | ||
473 | /** | |
474 | * dfu_device_get_mode: | |
475 | * @device: a #GUsbDevice | |
476 | * | |
477 | * Gets the device mode. | |
478 | * | |
479 | * Return value: enumerated mode, e.g. %DFU_MODE_RUNTIME | |
480 | * | |
481 | * Since: 0.5.4 | |
482 | **/ | |
483 | DfuMode | |
484 | dfu_device_get_mode (DfuDevice *device) | |
485 | { | |
486 | DfuDevicePrivate *priv = GET_PRIVATE (device); | |
487 | g_return_val_if_fail (DFU_IS_DEVICE (device), DFU_MODE_UNKNOWN); | |
488 | return priv->mode; | |
489 | } | |
490 | ||
491 | /** | |
492 | * dfu_device_get_timeout: | |
493 | * @device: a #GUsbDevice | |
494 | * | |
495 | * Gets the device timeout. | |
496 | * | |
497 | * Return value: enumerated timeout in ms | |
498 | * | |
499 | * Since: 0.5.4 | |
500 | **/ | |
501 | guint | |
502 | dfu_device_get_timeout (DfuDevice *device) | |
503 | { | |
504 | DfuDevicePrivate *priv = GET_PRIVATE (device); | |
505 | g_return_val_if_fail (DFU_IS_DEVICE (device), 0); | |
506 | return priv->timeout_ms; | |
507 | } | |
508 | ||
509 | /** | |
510 | * dfu_device_get_state: | |
511 | * @device: a #GUsbDevice | |
512 | * | |
513 | * Gets the device state. | |
514 | * | |
515 | * Return value: enumerated state, e.g. %DFU_STATE_DFU_UPLOAD_IDLE | |
516 | * | |
517 | * Since: 0.5.4 | |
518 | **/ | |
519 | DfuState | |
520 | dfu_device_get_state (DfuDevice *device) | |
521 | { | |
522 | DfuDevicePrivate *priv = GET_PRIVATE (device); | |
523 | g_return_val_if_fail (DFU_IS_DEVICE (device), 0); | |
524 | return priv->state; | |
525 | } | |
526 | ||
527 | /** | |
528 | * dfu_device_get_status: | |
529 | * @device: a #GUsbDevice | |
530 | * | |
531 | * Gets the device status. | |
532 | * | |
533 | * Return value: enumerated status, e.g. %DFU_STATUS_ERR_ADDRESS | |
534 | * | |
535 | * Since: 0.5.4 | |
536 | **/ | |
537 | DfuStatus | |
538 | dfu_device_get_status (DfuDevice *device) | |
539 | { | |
540 | DfuDevicePrivate *priv = GET_PRIVATE (device); | |
541 | g_return_val_if_fail (DFU_IS_DEVICE (device), 0); | |
542 | return priv->status; | |
543 | } | |
544 | ||
545 | /** | |
546 | * dfu_device_has_attribute: (skip) | |
547 | * @device: A #DfuDevice | |
548 | * @attribute: A #DfuDeviceAttributes, e.g. %DFU_DEVICE_ATTRIBUTE_CAN_DOWNLOAD | |
549 | * | |
550 | * Returns if an attribute set for the device. | |
551 | * | |
552 | * Return value: %TRUE if the attribute is set | |
553 | * | |
554 | * Since: 0.5.4 | |
555 | **/ | |
556 | gboolean | |
557 | dfu_device_has_attribute (DfuDevice *device, DfuDeviceAttributes attribute) | |
558 | { | |
559 | DfuDevicePrivate *priv = GET_PRIVATE (device); | |
560 | g_return_val_if_fail (DFU_IS_DEVICE (device), 0x0); | |
561 | return (priv->attributes & attribute) > 0; | |
562 | } | |
563 | ||
564 | /** | |
565 | * dfu_device_has_dfuse_support: | |
566 | * @device: A #DfuDevice | |
567 | * | |
568 | * Returns is DfuSe is supported on a device. | |
569 | * | |
570 | * Return value: %TRUE for DfuSe | |
571 | * | |
572 | * Since: 0.5.4 | |
573 | **/ | |
574 | gboolean | |
575 | dfu_device_has_dfuse_support (DfuDevice *device) | |
576 | { | |
577 | DfuDevicePrivate *priv = GET_PRIVATE (device); | |
578 | g_return_val_if_fail (DFU_IS_DEVICE (device), FALSE); | |
579 | return priv->dfuse_supported; | |
580 | } | |
581 | ||
582 | /** | |
583 | * dfu_device_set_quirks: | |
584 | **/ | |
585 | static void | |
586 | dfu_target_set_quirks (DfuDevice *device) | |
587 | { | |
588 | DfuDevicePrivate *priv = GET_PRIVATE (device); | |
589 | guint16 vid, pid, release; | |
590 | ||
591 | vid = g_usb_device_get_vid (priv->dev); | |
592 | pid = g_usb_device_get_pid (priv->dev); | |
593 | release = g_usb_device_get_release (priv->dev); | |
594 | ||
595 | /* Openmoko Freerunner / GTA02 */ | |
596 | if ((vid == 0x1d50 || vid == 0x1457) && | |
597 | pid >= 0x5117 && pid <= 0x5126) | |
598 | priv->quirks |= DFU_DEVICE_QUIRK_IGNORE_POLLTIMEOUT | | |
599 | DFU_DEVICE_QUIRK_NO_PID_CHANGE | | |
600 | DFU_DEVICE_QUIRK_NO_DFU_RUNTIME | | |
601 | DFU_DEVICE_QUIRK_NO_GET_STATUS_UPLOAD; | |
602 | ||
603 | /* OpenPCD Reader */ | |
604 | if (vid == 0x16c0 && pid == 0x076b) | |
605 | priv->quirks |= DFU_DEVICE_QUIRK_IGNORE_POLLTIMEOUT; | |
606 | ||
607 | /* Siemens AG, PXM 40 & PXM 50 */ | |
608 | if (vid == 0x0908 && (pid == 0x02c4 || pid == 0x02c5) && release == 0x0) | |
609 | priv->quirks |= DFU_DEVICE_QUIRK_IGNORE_POLLTIMEOUT; | |
610 | ||
611 | /* Midiman M-Audio Transit */ | |
612 | if (vid == 0x0763 && pid == 0x2806) | |
613 | priv->quirks |= DFU_DEVICE_QUIRK_IGNORE_POLLTIMEOUT; | |
614 | ||
615 | /* the LPC DFU bootloader uses the wrong mode */ | |
616 | if (vid == 0x1fc9 && pid == 0x000c) | |
617 | priv->quirks |= DFU_DEVICE_QUIRK_FORCE_DFU_MODE; | |
618 | ||
619 | /* the Leaflabs Maple3 is known broken */ | |
620 | if (vid == 0x1eaf && pid == 0x0003 && release == 0x0200) | |
621 | priv->quirks |= DFU_DEVICE_QUIRK_IGNORE_INVALID_VERSION; | |
622 | ||
623 | /* m-stack DFU implementation */ | |
624 | if (vid == 0x273f && pid == 0x1003) | |
625 | priv->quirks |= DFU_DEVICE_QUIRK_ATTACH_UPLOAD_DOWNLOAD; | |
626 | ||
627 | /* the DSO Nano has uses 0 instead of 2 when in DFU mode */ | |
628 | // quirks |= DFU_DEVICE_QUIRK_USE_PROTOCOL_ZERO; | |
629 | } | |
630 | ||
631 | /** | |
632 | * dfu_device_new: | |
633 | * @dev: A #GUsbDevice | |
634 | * | |
635 | * Creates a new DFU device object. | |
636 | * | |
637 | * Return value: a new #DfuDevice, or %NULL if @dev was not DFU-capable | |
638 | * | |
639 | * Since: 0.5.4 | |
640 | **/ | |
641 | DfuDevice * | |
642 | dfu_device_new (GUsbDevice *dev) | |
643 | { | |
644 | DfuDevicePrivate *priv; | |
645 | DfuDevice *device; | |
646 | device = g_object_new (DFU_TYPE_DEVICE, NULL); | |
647 | priv = GET_PRIVATE (device); | |
648 | priv->dev = g_object_ref (dev); | |
649 | priv->platform_id = g_strdup (g_usb_device_get_platform_id (dev)); | |
650 | ||
651 | /* set any quirks on the device before adding targets */ | |
652 | dfu_target_set_quirks (device); | |
653 | ||
654 | /* add each alternate interface, although typically there will | |
655 | * be only one */ | |
656 | if (!dfu_device_add_targets (device)) { | |
657 | g_object_unref (device); | |
658 | return NULL; | |
659 | } | |
660 | return device; | |
661 | } | |
662 | ||
663 | /** | |
664 | * dfu_device_get_targets: | |
665 | * @device: a #DfuDevice | |
666 | * | |
667 | * Gets all the targets for this device. | |
668 | * | |
669 | * Return value: (transfer none) (element-type DfuTarget): #DfuTarget, or %NULL | |
670 | * | |
671 | * Since: 0.5.4 | |
672 | **/ | |
673 | GPtrArray * | |
674 | dfu_device_get_targets (DfuDevice *device) | |
675 | { | |
676 | DfuDevicePrivate *priv = GET_PRIVATE (device); | |
677 | g_return_val_if_fail (DFU_IS_DEVICE (device), NULL); | |
678 | return priv->targets; | |
679 | } | |
680 | ||
681 | /** | |
682 | * dfu_device_get_target_by_alt_setting: | |
683 | * @device: a #DfuDevice | |
684 | * @alt_setting: the setting used to find | |
685 | * @error: a #GError, or %NULL | |
686 | * | |
687 | * Gets a target with a specific alternative setting. | |
688 | * | |
689 | * Return value: (transfer full): a #DfuTarget, or %NULL | |
690 | * | |
691 | * Since: 0.5.4 | |
692 | **/ | |
693 | DfuTarget * | |
694 | dfu_device_get_target_by_alt_setting (DfuDevice *device, | |
695 | guint8 alt_setting, | |
696 | GError **error) | |
697 | { | |
698 | DfuDevicePrivate *priv = GET_PRIVATE (device); | |
699 | DfuTarget *target; | |
700 | guint i; | |
701 | ||
702 | g_return_val_if_fail (DFU_IS_DEVICE (device), NULL); | |
703 | g_return_val_if_fail (error == NULL || *error == NULL, NULL); | |
704 | ||
705 | /* find by ID */ | |
706 | for (i = 0; i < priv->targets->len; i++) { | |
707 | target = g_ptr_array_index (priv->targets, i); | |
708 | if (dfu_target_get_alt_setting (target) == alt_setting) | |
709 | return g_object_ref (target); | |
710 | } | |
711 | ||
712 | /* failed */ | |
713 | g_set_error (error, | |
714 | DFU_ERROR, | |
715 | DFU_ERROR_NOT_FOUND, | |
716 | "No target with alt-setting %i", | |
717 | alt_setting); | |
718 | return NULL; | |
719 | } | |
720 | ||
721 | /** | |
722 | * dfu_device_get_target_by_alt_name: | |
723 | * @device: a #DfuDevice | |
724 | * @alt_name: the name used to find | |
725 | * @error: a #GError, or %NULL | |
726 | * | |
727 | * Gets a target with a specific alternative name. | |
728 | * | |
729 | * Return value: (transfer full): a #DfuTarget, or %NULL | |
730 | * | |
731 | * Since: 0.5.4 | |
732 | **/ | |
733 | DfuTarget * | |
734 | dfu_device_get_target_by_alt_name (DfuDevice *device, | |
735 | const gchar *alt_name, | |
736 | GError **error) | |
737 | { | |
738 | DfuDevicePrivate *priv = GET_PRIVATE (device); | |
739 | DfuTarget *target; | |
740 | guint i; | |
741 | ||
742 | g_return_val_if_fail (DFU_IS_DEVICE (device), NULL); | |
743 | g_return_val_if_fail (error == NULL || *error == NULL, NULL); | |
744 | ||
745 | /* find by ID */ | |
746 | for (i = 0; i < priv->targets->len; i++) { | |
747 | target = g_ptr_array_index (priv->targets, i); | |
748 | if (g_strcmp0 (dfu_target_get_alt_name (target, NULL), alt_name) == 0) | |
749 | return g_object_ref (target); | |
750 | } | |
751 | ||
752 | /* failed */ | |
753 | g_set_error (error, | |
754 | DFU_ERROR, | |
755 | DFU_ERROR_NOT_FOUND, | |
756 | "No target with alt-name %s", | |
757 | alt_name); | |
758 | return NULL; | |
759 | } | |
760 | ||
761 | /** | |
762 | * dfu_device_get_platform_id: | |
763 | * @device: a #DfuDevice | |
764 | * | |
765 | * Gets the platform ID which normally corresponds to the port in some way. | |
766 | * | |
767 | * Return value: string or %NULL | |
768 | * | |
769 | * Since: 0.5.4 | |
770 | **/ | |
771 | const gchar * | |
772 | dfu_device_get_platform_id (DfuDevice *device) | |
773 | { | |
774 | DfuDevicePrivate *priv = GET_PRIVATE (device); | |
775 | g_return_val_if_fail (DFU_IS_DEVICE (device), NULL); | |
776 | return priv->platform_id; | |
777 | } | |
778 | ||
779 | /** | |
780 | * dfu_device_get_runtime_vid: | |
781 | * @device: a #DfuDevice | |
782 | * | |
783 | * Gets the runtime vendor ID. | |
784 | * | |
785 | * Return value: vendor ID, or 0xffff for unknown | |
786 | * | |
787 | * Since: 0.5.4 | |
788 | **/ | |
789 | guint16 | |
790 | dfu_device_get_runtime_vid (DfuDevice *device) | |
791 | { | |
792 | DfuDevicePrivate *priv = GET_PRIVATE (device); | |
793 | g_return_val_if_fail (DFU_IS_DEVICE (device), 0xffff); | |
794 | return priv->runtime_vid; | |
795 | } | |
796 | ||
797 | /** | |
798 | * dfu_device_get_runtime_pid: | |
799 | * @device: a #DfuDevice | |
800 | * | |
801 | * Gets the runtime product ID. | |
802 | * | |
803 | * Return value: product ID, or 0xffff for unknown | |
804 | * | |
805 | * Since: 0.5.4 | |
806 | **/ | |
807 | guint16 | |
808 | dfu_device_get_runtime_pid (DfuDevice *device) | |
809 | { | |
810 | DfuDevicePrivate *priv = GET_PRIVATE (device); | |
811 | g_return_val_if_fail (DFU_IS_DEVICE (device), 0xffff); | |
812 | return priv->runtime_pid; | |
813 | } | |
814 | ||
815 | /** | |
816 | * dfu_device_get_runtime_release: | |
817 | * @device: a #DfuDevice | |
818 | * | |
819 | * Gets the runtime release number in BCD format. | |
820 | * | |
821 | * Return value: release number, or 0xffff for unknown | |
822 | * | |
823 | * Since: 0.5.4 | |
824 | **/ | |
825 | guint16 | |
826 | dfu_device_get_runtime_release (DfuDevice *device) | |
827 | { | |
828 | DfuDevicePrivate *priv = GET_PRIVATE (device); | |
829 | g_return_val_if_fail (DFU_IS_DEVICE (device), 0xffff); | |
830 | return priv->runtime_release; | |
831 | } | |
832 | ||
833 | /** | |
834 | * dfu_device_get_usb_dev: (skip) | |
835 | * @device: a #DfuDevice | |
836 | * | |
837 | * Gets the internal USB device for the #DfuDevice. | |
838 | * | |
839 | * NOTE: This may change at runtime if the device is replugged or | |
840 | * reset. | |
841 | * | |
842 | * Returns: (transfer none): the internal USB device | |
843 | **/ | |
844 | GUsbDevice * | |
845 | dfu_device_get_usb_dev (DfuDevice *device) | |
846 | { | |
847 | DfuDevicePrivate *priv = GET_PRIVATE (device); | |
848 | g_return_val_if_fail (DFU_IS_DEVICE (device), NULL); | |
849 | return priv->dev; | |
850 | } | |
851 | ||
852 | /** | |
853 | * dfu_device_get_display_name: | |
854 | * @device: a #DfuDevice | |
855 | * | |
856 | * Gets the display name to use for the device. | |
857 | * | |
858 | * Return value: string or %NULL for unset | |
859 | * | |
860 | * Since: 0.5.4 | |
861 | **/ | |
862 | const gchar * | |
863 | dfu_device_get_display_name (DfuDevice *device) | |
864 | { | |
865 | DfuDevicePrivate *priv = GET_PRIVATE (device); | |
866 | g_return_val_if_fail (DFU_IS_DEVICE (device), NULL); | |
867 | return priv->display_name; | |
868 | } | |
869 | ||
870 | /** | |
871 | * dfu_device_set_state: | |
872 | **/ | |
873 | static void | |
874 | dfu_device_set_state (DfuDevice *device, DfuState state) | |
875 | { | |
876 | DfuDevicePrivate *priv = GET_PRIVATE (device); | |
877 | if (priv->state == state) | |
878 | return; | |
879 | priv->state = state; | |
880 | g_signal_emit (device, signals[SIGNAL_STATE_CHANGED], 0, state); | |
881 | } | |
882 | ||
883 | /** | |
884 | * dfu_device_set_status: | |
885 | **/ | |
886 | static void | |
887 | dfu_device_set_status (DfuDevice *device, DfuStatus status) | |
888 | { | |
889 | DfuDevicePrivate *priv = GET_PRIVATE (device); | |
890 | if (priv->status == status) | |
891 | return; | |
892 | priv->status = status; | |
893 | g_signal_emit (device, signals[SIGNAL_STATUS_CHANGED], 0, status); | |
894 | } | |
895 | ||
896 | /** | |
897 | * dfu_device_refresh: | |
898 | * @device: a #DfuDevice | |
899 | * @cancellable: a #GCancellable, or %NULL | |
900 | * @error: a #GError, or %NULL | |
901 | * | |
902 | * Refreshes the cached properties on the DFU device. | |
903 | * | |
904 | * Return value: %TRUE for success | |
905 | * | |
906 | * Since: 0.5.4 | |
907 | **/ | |
908 | gboolean | |
909 | dfu_device_refresh (DfuDevice *device, GCancellable *cancellable, GError **error) | |
910 | { | |
911 | DfuDevicePrivate *priv = GET_PRIVATE (device); | |
912 | gsize actual_length = 0; | |
913 | guint8 buf[6]; | |
914 | g_autoptr(GError) error_local = NULL; | |
915 | ||
916 | g_return_val_if_fail (DFU_IS_DEVICE (device), FALSE); | |
917 | g_return_val_if_fail (error == NULL || *error == NULL, FALSE); | |
918 | ||
919 | /* no backing USB device */ | |
920 | if (priv->dev == NULL) { | |
921 | g_set_error (error, | |
922 | DFU_ERROR, | |
923 | DFU_ERROR_INTERNAL, | |
924 | "failed to refresh: no GUsbDevice for %s", | |
925 | priv->platform_id); | |
926 | return FALSE; | |
927 | } | |
928 | ||
929 | /* the device has no DFU runtime, so cheat */ | |
930 | if (priv->quirks & DFU_DEVICE_QUIRK_NO_DFU_RUNTIME) { | |
931 | g_set_error_literal (error, | |
932 | DFU_ERROR, | |
933 | DFU_ERROR_NOT_SUPPORTED, | |
934 | "not supported as no DFU runtime"); | |
935 | return FALSE; | |
936 | } | |
937 | ||
938 | if (!g_usb_device_control_transfer (priv->dev, | |
939 | G_USB_DEVICE_DIRECTION_DEVICE_TO_HOST, | |
940 | G_USB_DEVICE_REQUEST_TYPE_CLASS, | |
941 | G_USB_DEVICE_RECIPIENT_INTERFACE, | |
942 | DFU_REQUEST_GETSTATUS, | |
943 | 0, | |
944 | priv->iface_number, | |
945 | buf, sizeof(buf), &actual_length, | |
946 | priv->timeout_ms, | |
947 | cancellable, | |
948 | &error_local)) { | |
949 | g_set_error (error, | |
950 | DFU_ERROR, | |
951 | DFU_ERROR_NOT_SUPPORTED, | |
952 | "cannot get device state: %s", | |
953 | error_local->message); | |
954 | return FALSE; | |
955 | } | |
956 | if (actual_length != 6) { | |
957 | g_set_error (error, | |
958 | DFU_ERROR, | |
959 | DFU_ERROR_INTERNAL, | |
960 | "cannot get device status, invalid size: %04x", | |
961 | (guint) actual_length); | |
962 | } | |
963 | ||
964 | /* status or state changed */ | |
965 | dfu_device_set_status (device, buf[0]); | |
966 | dfu_device_set_state (device, buf[4]); | |
967 | if (dfu_device_has_quirk (device, DFU_DEVICE_QUIRK_IGNORE_POLLTIMEOUT)) { | |
968 | priv->dnload_timeout = 5; | |
969 | } else { | |
970 | priv->dnload_timeout = buf[1] + | |
971 | (((guint32) buf[2]) << 8) + | |
972 | (((guint32) buf[3]) << 16); | |
973 | } | |
974 | g_debug ("refreshed status=%s and state=%s", | |
975 | dfu_status_to_string (priv->status), | |
976 | dfu_state_to_string (priv->state)); | |
977 | return TRUE; | |
978 | } | |
979 | ||
980 | /** | |
981 | * dfu_device_detach: | |
982 | * @device: a #DfuDevice | |
983 | * @cancellable: a #GCancellable, or %NULL | |
984 | * @error: a #GError, or %NULL | |
985 | * | |
986 | * Detaches the device putting it into DFU-mode. | |
987 | * | |
988 | * Return value: %TRUE for success | |
989 | * | |
990 | * Since: 0.5.4 | |
991 | **/ | |
992 | gboolean | |
993 | dfu_device_detach (DfuDevice *device, GCancellable *cancellable, GError **error) | |
994 | { | |
995 | DfuDevicePrivate *priv = GET_PRIVATE (device); | |
996 | g_autoptr(GError) error_local = NULL; | |
997 | ||
998 | g_return_val_if_fail (DFU_IS_DEVICE (device), FALSE); | |
999 | g_return_val_if_fail (error == NULL || *error == NULL, FALSE); | |
1000 | ||
1001 | /* already in DFU mode */ | |
1002 | switch (priv->state) { | |
1003 | case DFU_STATE_APP_IDLE: | |
1004 | case DFU_STATE_APP_DETACH: | |
1005 | break; | |
1006 | default: | |
1007 | g_set_error (error, | |
1008 | DFU_ERROR, | |
1009 | DFU_ERROR_NOT_SUPPORTED, | |
1010 | "Already in DFU mode"); | |
1011 | return FALSE; | |
1012 | } | |
1013 | ||
1014 | /* no backing USB device */ | |
1015 | if (priv->dev == NULL) { | |
1016 | g_set_error (error, | |
1017 | DFU_ERROR, | |
1018 | DFU_ERROR_INTERNAL, | |
1019 | "failed to detach: no GUsbDevice for %s", | |
1020 | priv->platform_id); | |
1021 | return FALSE; | |
1022 | } | |
1023 | ||
1024 | /* the device has no DFU runtime, so cheat */ | |
1025 | if (priv->quirks & DFU_DEVICE_QUIRK_NO_DFU_RUNTIME) { | |
1026 | g_set_error_literal (error, | |
1027 | DFU_ERROR, | |
1028 | DFU_ERROR_NOT_SUPPORTED, | |
1029 | "not supported as no DFU runtime"); | |
1030 | return FALSE; | |
1031 | } | |
1032 | ||
1033 | /* inform UI there's going to be a detach:attach */ | |
1034 | dfu_device_set_state (device, DFU_STATE_APP_DETACH); | |
1035 | ||
1036 | if (!g_usb_device_control_transfer (priv->dev, | |
1037 | G_USB_DEVICE_DIRECTION_HOST_TO_DEVICE, | |
1038 | G_USB_DEVICE_REQUEST_TYPE_CLASS, | |
1039 | G_USB_DEVICE_RECIPIENT_INTERFACE, | |
1040 | DFU_REQUEST_DETACH, | |
1041 | 0, | |
1042 | priv->iface_number, | |
1043 | NULL, 0, NULL, | |
1044 | priv->timeout_ms, | |
1045 | cancellable, | |
1046 | &error_local)) { | |
1047 | /* refresh the error code */ | |
1048 | dfu_device_error_fixup (device, cancellable, &error_local); | |
1049 | g_set_error (error, | |
1050 | DFU_ERROR, | |
1051 | DFU_ERROR_NOT_SUPPORTED, | |
1052 | "cannot detach device: %s", | |
1053 | error_local->message); | |
1054 | return FALSE; | |
1055 | } | |
1056 | ||
1057 | /* do a host reset */ | |
1058 | if ((priv->attributes & DFU_DEVICE_ATTRIBUTE_WILL_DETACH) == 0) { | |
1059 | g_debug ("doing device reset as host will not self-reset"); | |
1060 | if (!dfu_device_reset (device, error)) | |
1061 | return FALSE; | |
1062 | } | |
1063 | return TRUE; | |
1064 | } | |
1065 | ||
1066 | /** | |
1067 | * dfu_device_abort: | |
1068 | * @device: a #DfuDevice | |
1069 | * @cancellable: a #GCancellable, or %NULL | |
1070 | * @error: a #GError, or %NULL | |
1071 | * | |
1072 | * Aborts any upload or download in progress. | |
1073 | * | |
1074 | * Return value: %TRUE for success | |
1075 | * | |
1076 | * Since: 0.5.4 | |
1077 | **/ | |
1078 | gboolean | |
1079 | dfu_device_abort (DfuDevice *device, GCancellable *cancellable, GError **error) | |
1080 | { | |
1081 | DfuDevicePrivate *priv = GET_PRIVATE (device); | |
1082 | g_autoptr(GError) error_local = NULL; | |
1083 | ||
1084 | g_return_val_if_fail (DFU_IS_DEVICE (device), FALSE); | |
1085 | g_return_val_if_fail (error == NULL || *error == NULL, FALSE); | |
1086 | ||
1087 | /* no backing USB device */ | |
1088 | if (priv->dev == NULL) { | |
1089 | g_set_error (error, | |
1090 | DFU_ERROR, | |
1091 | DFU_ERROR_INTERNAL, | |
1092 | "failed to abort: no GUsbDevice for %s", | |
1093 | priv->platform_id); | |
1094 | return FALSE; | |
1095 | } | |
1096 | ||
1097 | /* the device has no DFU runtime, so cheat */ | |
1098 | if (priv->quirks & DFU_DEVICE_QUIRK_NO_DFU_RUNTIME) { | |
1099 | g_set_error_literal (error, | |
1100 | DFU_ERROR, | |
1101 | DFU_ERROR_NOT_SUPPORTED, | |
1102 | "not supported as no DFU runtime"); | |
1103 | return FALSE; | |
1104 | } | |
1105 | ||
1106 | if (!g_usb_device_control_transfer (priv->dev, | |
1107 | G_USB_DEVICE_DIRECTION_HOST_TO_DEVICE, | |
1108 | G_USB_DEVICE_REQUEST_TYPE_CLASS, | |
1109 | G_USB_DEVICE_RECIPIENT_INTERFACE, | |
1110 | DFU_REQUEST_ABORT, | |
1111 | 0, | |
1112 | priv->iface_number, | |
1113 | NULL, 0, NULL, | |
1114 | priv->timeout_ms, | |
1115 | cancellable, | |
1116 | &error_local)) { | |
1117 | /* refresh the error code */ | |
1118 | dfu_device_error_fixup (device, cancellable, &error_local); | |
1119 | g_set_error (error, | |
1120 | DFU_ERROR, | |
1121 | DFU_ERROR_NOT_SUPPORTED, | |
1122 | "cannot abort device: %s", | |
1123 | error_local->message); | |
1124 | return FALSE; | |
1125 | } | |
1126 | ||
1127 | return TRUE; | |
1128 | } | |
1129 | ||
1130 | /** | |
1131 | * dfu_device_clear_status: | |
1132 | * @device: a #DfuDevice | |
1133 | * @cancellable: a #GCancellable, or %NULL | |
1134 | * @error: a #GError, or %NULL | |
1135 | * | |
1136 | * Clears any error status on the DFU device. | |
1137 | * | |
1138 | * Return value: %TRUE for success | |
1139 | * | |
1140 | * Since: 0.5.4 | |
1141 | **/ | |
1142 | gboolean | |
1143 | dfu_device_clear_status (DfuDevice *device, GCancellable *cancellable, GError **error) | |
1144 | { | |
1145 | DfuDevicePrivate *priv = GET_PRIVATE (device); | |
1146 | g_autoptr(GError) error_local = NULL; | |
1147 | ||
1148 | g_return_val_if_fail (DFU_IS_DEVICE (device), FALSE); | |
1149 | g_return_val_if_fail (error == NULL || *error == NULL, FALSE); | |
1150 | ||
1151 | /* no backing USB device */ | |
1152 | if (priv->dev == NULL) { | |
1153 | g_set_error (error, | |
1154 | DFU_ERROR, | |
1155 | DFU_ERROR_INTERNAL, | |
1156 | "failed to clear status: no GUsbDevice for %s", | |
1157 | priv->platform_id); | |
1158 | return FALSE; | |
1159 | } | |
1160 | ||
1161 | /* the device has no DFU runtime, so cheat */ | |
1162 | if (priv->quirks & DFU_DEVICE_QUIRK_NO_DFU_RUNTIME) { | |
1163 | g_set_error_literal (error, | |
1164 | DFU_ERROR, | |
1165 | DFU_ERROR_NOT_SUPPORTED, | |
1166 | "not supported as no DFU runtime"); | |
1167 | return FALSE; | |
1168 | } | |
1169 | ||
1170 | if (!g_usb_device_control_transfer (priv->dev, | |
1171 | G_USB_DEVICE_DIRECTION_HOST_TO_DEVICE, | |
1172 | G_USB_DEVICE_REQUEST_TYPE_CLASS, | |
1173 | G_USB_DEVICE_RECIPIENT_INTERFACE, | |
1174 | DFU_REQUEST_CLRSTATUS, | |
1175 | 0, | |
1176 | priv->iface_number, | |
1177 | NULL, 0, NULL, | |
1178 | priv->timeout_ms, | |
1179 | cancellable, | |
1180 | &error_local)) { | |
1181 | /* refresh the error code */ | |
1182 | dfu_device_error_fixup (device, cancellable, &error_local); | |
1183 | g_set_error (error, | |
1184 | DFU_ERROR, | |
1185 | DFU_ERROR_NOT_SUPPORTED, | |
1186 | "cannot clear status on the device: %s", | |
1187 | error_local->message); | |
1188 | return FALSE; | |
1189 | } | |
1190 | return TRUE; | |
1191 | } | |
1192 | ||
1193 | /** | |
1194 | * dfu_device_get_interface: | |
1195 | * @device: a #DfuDevice | |
1196 | * | |
1197 | * Gets the interface number. | |
1198 | * | |
1199 | * Since: 0.5.4 | |
1200 | **/ | |
1201 | guint8 | |
1202 | dfu_device_get_interface (DfuDevice *device) | |
1203 | { | |
1204 | DfuDevicePrivate *priv = GET_PRIVATE (device); | |
1205 | g_return_val_if_fail (DFU_IS_DEVICE (device), 0xff); | |
1206 | return priv->iface_number; | |
1207 | } | |
1208 | ||
1209 | /** | |
1210 | * dfu_device_open: | |
1211 | * @device: a #DfuDevice | |
1212 | * @flags: #DfuDeviceOpenFlags, e.g. %DFU_DEVICE_OPEN_FLAG_NONE | |
1213 | * @cancellable: a #GCancellable, or %NULL | |
1214 | * @error: a #GError, or %NULL | |
1215 | * | |
1216 | * Opens a DFU-capable device. | |
1217 | * | |
1218 | * Return value: %TRUE for success | |
1219 | * | |
1220 | * Since: 0.5.4 | |
1221 | **/ | |
1222 | gboolean | |
1223 | dfu_device_open (DfuDevice *device, DfuDeviceOpenFlags flags, | |
1224 | GCancellable *cancellable, GError **error) | |
1225 | { | |
1226 | DfuDevicePrivate *priv = GET_PRIVATE (device); | |
1227 | guint idx; | |
1228 | g_autoptr(GError) error_local = NULL; | |
1229 | ||
1230 | g_return_val_if_fail (DFU_IS_DEVICE (device), FALSE); | |
1231 | g_return_val_if_fail (error == NULL || *error == NULL, FALSE); | |
1232 | ||
1233 | /* no backing USB device */ | |
1234 | if (priv->dev == NULL) { | |
1235 | g_set_error (error, | |
1236 | DFU_ERROR, | |
1237 | DFU_ERROR_INTERNAL, | |
1238 | "failed to open: no GUsbDevice for %s", | |
1239 | priv->platform_id); | |
1240 | return FALSE; | |
1241 | } | |
1242 | ||
1243 | /* open */ | |
1244 | if (!g_usb_device_open (priv->dev, &error_local)) { | |
1245 | if (g_error_matches (error_local, | |
1246 | G_USB_DEVICE_ERROR, | |
1247 | G_USB_DEVICE_ERROR_ALREADY_OPEN)) { | |
1248 | g_debug ("device already open, ignoring"); | |
1249 | return TRUE; | |
1250 | } | |
1251 | if (g_error_matches (error_local, | |
1252 | G_USB_DEVICE_ERROR, | |
1253 | G_USB_DEVICE_ERROR_PERMISSION_DENIED)) { | |
1254 | g_set_error (error, | |
1255 | DFU_ERROR, | |
1256 | DFU_ERROR_PERMISSION_DENIED, | |
1257 | "%s", error_local->message); | |
1258 | return FALSE; | |
1259 | } | |
1260 | g_set_error (error, | |
1261 | DFU_ERROR, | |
1262 | DFU_ERROR_INVALID_DEVICE, | |
1263 | "cannot open device %s: %s", | |
1264 | g_usb_device_get_platform_id (priv->dev), | |
1265 | error_local->message); | |
1266 | return FALSE; | |
1267 | } | |
1268 | ||
1269 | /* claim the correct interface if set */ | |
1270 | if (priv->iface_number != 0xff) { | |
1271 | if (!g_usb_device_claim_interface (priv->dev, | |
1272 | (gint) priv->iface_number, | |
1273 | 0, | |
1274 | &error_local)) { | |
1275 | g_set_error (error, | |
1276 | DFU_ERROR, | |
1277 | DFU_ERROR_INVALID_DEVICE, | |
1278 | "cannot claim interface %i: %s", | |
1279 | priv->iface_number, error_local->message); | |
1280 | return FALSE; | |
1281 | } | |
1282 | } | |
1283 | ||
1284 | /* get product name if it exists */ | |
1285 | idx = g_usb_device_get_product_index (priv->dev); | |
1286 | if (idx != 0x00) | |
1287 | priv->display_name = g_usb_device_get_string_descriptor (priv->dev, idx, NULL); | |
1288 | ||
1289 | /* the device has no DFU runtime, so cheat */ | |
1290 | if (priv->quirks & DFU_DEVICE_QUIRK_NO_DFU_RUNTIME) { | |
1291 | priv->state = DFU_STATE_APP_IDLE; | |
1292 | priv->status = DFU_STATUS_OK; | |
1293 | flags |= DFU_DEVICE_OPEN_FLAG_NO_AUTO_REFRESH; | |
1294 | } | |
1295 | ||
1296 | /* automatically abort any uploads or downloads */ | |
1297 | if ((flags & DFU_DEVICE_OPEN_FLAG_NO_AUTO_REFRESH) == 0) { | |
1298 | if (!dfu_device_refresh (device, cancellable, error)) | |
1299 | return FALSE; | |
1300 | switch (priv->state) { | |
1301 | case DFU_STATE_DFU_UPLOAD_IDLE: | |
1302 | case DFU_STATE_DFU_DNLOAD_IDLE: | |
1303 | case DFU_STATE_DFU_DNLOAD_SYNC: | |
1304 | g_debug ("aborting transfer %s", dfu_status_to_string (priv->status)); | |
1305 | if (!dfu_device_abort (device, cancellable, error)) | |
1306 | return FALSE; | |
1307 | break; | |
1308 | case DFU_STATE_DFU_ERROR: | |
1309 | g_debug ("clearing error %s", dfu_status_to_string (priv->status)); | |
1310 | if (!dfu_device_clear_status (device, cancellable, error)) | |
1311 | return FALSE; | |
1312 | break; | |
1313 | default: | |
1314 | break; | |
1315 | } | |
1316 | } | |
1317 | ||
1318 | priv->open_new_dev = TRUE; | |
1319 | return TRUE; | |
1320 | } | |
1321 | ||
1322 | /** | |
1323 | * dfu_device_close: | |
1324 | * @device: a #DfuDevice | |
1325 | * @error: a #GError, or %NULL | |
1326 | * | |
1327 | * Closes a DFU device. | |
1328 | * | |
1329 | * Return value: %TRUE for success | |
1330 | * | |
1331 | * Since: 0.5.4 | |
1332 | **/ | |
1333 | gboolean | |
1334 | dfu_device_close (DfuDevice *device, GError **error) | |
1335 | { | |
1336 | DfuDevicePrivate *priv = GET_PRIVATE (device); | |
1337 | g_autoptr(GError) error_local = NULL; | |
1338 | ||
1339 | /* no backing USB device */ | |
1340 | if (priv->dev == NULL) { | |
1341 | g_set_error (error, | |
1342 | DFU_ERROR, | |
1343 | DFU_ERROR_INTERNAL, | |
1344 | "failed to close: no GUsbDevice for %s", | |
1345 | priv->platform_id); | |
1346 | return FALSE; | |
1347 | } | |
1348 | ||
1349 | /* close if open */ | |
1350 | if (!g_usb_device_close (priv->dev, &error_local)) { | |
1351 | if (g_error_matches (error_local, | |
1352 | G_USB_DEVICE_ERROR, | |
1353 | G_USB_DEVICE_ERROR_NOT_OPEN)) { | |
1354 | g_debug ("device not open, so ignoring error for close"); | |
1355 | return TRUE; | |
1356 | } | |
1357 | g_set_error_literal (error, | |
1358 | DFU_ERROR, | |
1359 | DFU_ERROR_INTERNAL, | |
1360 | error_local->message); | |
1361 | return FALSE; | |
1362 | } | |
1363 | priv->open_new_dev = FALSE; | |
1364 | return TRUE; | |
1365 | } | |
1366 | ||
1367 | /** | |
1368 | * dfu_device_set_new_usb_dev: | |
1369 | **/ | |
1370 | gboolean | |
1371 | dfu_device_set_new_usb_dev (DfuDevice *device, GUsbDevice *dev, | |
1372 | GCancellable *cancellable, GError **error) | |
1373 | { | |
1374 | DfuDevicePrivate *priv = GET_PRIVATE (device); | |
1375 | ||
1376 | /* same */ | |
1377 | if (priv->dev == dev) { | |
1378 | g_warning ("setting GUsbDevice with same dev?!"); | |
1379 | return TRUE; | |
1380 | } | |
1381 | ||
1382 | /* device removed */ | |
1383 | if (dev == NULL) { | |
1384 | g_debug ("invalidating backing GUsbDevice"); | |
1385 | g_clear_object (&priv->dev); | |
1386 | g_ptr_array_set_size (priv->targets, 0); | |
1387 | return TRUE; | |
1388 | } | |
1389 | ||
1390 | /* close */ | |
1391 | if (priv->dev != NULL) { | |
1392 | gboolean tmp = priv->open_new_dev; | |
1393 | if (!dfu_device_close (device, error)) | |
1394 | return FALSE; | |
1395 | priv->open_new_dev = tmp; | |
1396 | } | |
1397 | ||
1398 | /* set the new USB device */ | |
1399 | g_set_object (&priv->dev, dev); | |
1400 | ||
1401 | /* should be the same */ | |
1402 | if (g_strcmp0 (priv->platform_id, | |
1403 | g_usb_device_get_platform_id (dev)) != 0) { | |
1404 | g_warning ("platform ID changed when setting new GUsbDevice?!"); | |
1405 | g_free (priv->platform_id); | |
1406 | priv->platform_id = g_strdup (g_usb_device_get_platform_id (dev)); | |
1407 | } | |
1408 | ||
1409 | /* update all the targets */ | |
1410 | if (!dfu_device_add_targets (device)) { | |
1411 | g_set_error_literal (error, | |
1412 | DFU_ERROR, | |
1413 | DFU_ERROR_NOT_SUPPORTED, | |
1414 | "replugged device is not DFU-capable"); | |
1415 | return FALSE; | |
1416 | } | |
1417 | ||
1418 | /* reclaim */ | |
1419 | if (priv->open_new_dev) { | |
1420 | g_debug ("automatically reopening device"); | |
1421 | if (!dfu_device_open (device, DFU_DEVICE_OPEN_FLAG_NONE, | |
1422 | cancellable, error)) | |
1423 | return FALSE; | |
1424 | } | |
1425 | return TRUE; | |
1426 | } | |
1427 | ||
1428 | typedef struct { | |
1429 | DfuDevice *device; | |
1430 | GError **error; | |
1431 | GMainLoop *loop; | |
1432 | GUsbDevice *dev; | |
1433 | guint cnt; | |
1434 | guint timeout; | |
1435 | } DfuDeviceReplugHelper; | |
1436 | ||
1437 | /** | |
1438 | * dfu_device_replug_helper_free: | |
1439 | **/ | |
1440 | static void | |
1441 | dfu_device_replug_helper_free (DfuDeviceReplugHelper *helper) | |
1442 | { | |
1443 | if (helper->dev != NULL) | |
1444 | g_object_unref (helper->dev); | |
1445 | g_object_unref (helper->device); | |
1446 | g_main_loop_unref (helper->loop); | |
1447 | g_free (helper); | |
1448 | } | |
1449 | ||
1450 | /** | |
1451 | * dfu_device_replug_helper_cb: | |
1452 | **/ | |
1453 | static gboolean | |
1454 | dfu_device_replug_helper_cb (gpointer user_data) | |
1455 | { | |
1456 | DfuDeviceReplugHelper *helper = (DfuDeviceReplugHelper *) user_data; | |
1457 | DfuDevicePrivate *priv = GET_PRIVATE (helper->device); | |
1458 | ||
1459 | /* did the backing GUsbDevice change */ | |
1460 | if (helper->dev != priv->dev) { | |
1461 | g_debug ("device changed GUsbDevice %p->%p", | |
1462 | helper->dev, priv->dev); | |
1463 | g_set_object (&helper->dev, priv->dev); | |
1464 | ||
1465 | /* success */ | |
1466 | if (helper->dev != NULL) { | |
1467 | g_main_loop_quit (helper->loop); | |
1468 | return FALSE; | |
1469 | } | |
1470 | } | |
1471 | ||
1472 | /* set a limit */ | |
1473 | if (helper->cnt++ * 100 > helper->timeout) { | |
1474 | g_debug ("gave up waiting for device replug"); | |
1475 | if (helper->dev == NULL) { | |
1476 | g_set_error_literal (helper->error, | |
1477 | DFU_ERROR, | |
1478 | DFU_ERROR_INVALID_DEVICE, | |
1479 | "target went away but did not come back"); | |
1480 | } else { | |
1481 | g_set_error_literal (helper->error, | |
1482 | DFU_ERROR, | |
1483 | DFU_ERROR_INVALID_DEVICE, | |
1484 | "target did not disconnect"); | |
1485 | } | |
1486 | g_main_loop_quit (helper->loop); | |
1487 | return FALSE; | |
1488 | } | |
1489 | ||
1490 | /* continue waiting */ | |
1491 | g_debug ("waiting for device replug for %ims -- state is %s", | |
1492 | helper->cnt * 100, dfu_state_to_string (priv->state)); | |
1493 | return TRUE; | |
1494 | } | |
1495 | ||
1496 | /** | |
1497 | * dfu_device_wait_for_replug: | |
1498 | * @device: a #DfuDevice | |
1499 | * @timeout: the maximum amount of time to wait | |
1500 | * @cancellable: a #GCancellable, or %NULL | |
1501 | * @error: a #GError, or %NULL | |
1502 | * | |
1503 | * Waits for a DFU device to disconnect and reconnect. | |
1504 | * This does rely on a #DfuContext being set up before this is called. | |
1505 | * | |
1506 | * Return value: %TRUE for success | |
1507 | * | |
1508 | * Since: 0.5.4 | |
1509 | **/ | |
1510 | gboolean | |
1511 | dfu_device_wait_for_replug (DfuDevice *device, guint timeout, | |
1512 | GCancellable *cancellable, GError **error) | |
1513 | { | |
1514 | DfuDevicePrivate *priv = GET_PRIVATE (device); | |
1515 | DfuDeviceReplugHelper *helper; | |
1516 | GError *error_tmp = NULL; | |
1517 | const guint replug_poll = 100; /* ms */ | |
1518 | ||
1519 | helper = g_new0 (DfuDeviceReplugHelper, 1); | |
1520 | helper->loop = g_main_loop_new (NULL, FALSE); | |
1521 | helper->device = g_object_ref (device); | |
1522 | helper->dev = g_object_ref (priv->dev); | |
1523 | helper->error = &error_tmp; | |
1524 | helper->timeout = timeout; | |
1525 | g_timeout_add_full (G_PRIORITY_DEFAULT, replug_poll, | |
1526 | dfu_device_replug_helper_cb, helper, | |
1527 | (GDestroyNotify) dfu_device_replug_helper_free); | |
1528 | g_main_loop_run (helper->loop); | |
1529 | if (error_tmp != NULL) { | |
1530 | g_propagate_error (error, error_tmp); | |
1531 | return FALSE; | |
1532 | } | |
1533 | return TRUE; | |
1534 | } | |
1535 | ||
1536 | /** | |
1537 | * dfu_device_reset: | |
1538 | * @device: a #DfuDevice | |
1539 | * @error: a #GError, or %NULL | |
1540 | * | |
1541 | * Resets the USB device. | |
1542 | * | |
1543 | * Return value: %TRUE for success | |
1544 | * | |
1545 | * Since: 0.5.4 | |
1546 | **/ | |
1547 | gboolean | |
1548 | dfu_device_reset (DfuDevice *device, GError **error) | |
1549 | { | |
1550 | DfuDevicePrivate *priv = GET_PRIVATE (device); | |
1551 | g_autoptr(GError) error_local = NULL; | |
1552 | ||
1553 | g_return_val_if_fail (DFU_IS_DEVICE (device), FALSE); | |
1554 | g_return_val_if_fail (error == NULL || *error == NULL, FALSE); | |
1555 | ||
1556 | /* no backing USB device */ | |
1557 | if (priv->dev == NULL) { | |
1558 | g_set_error (error, | |
1559 | DFU_ERROR, | |
1560 | DFU_ERROR_INTERNAL, | |
1561 | "failed to reset: no GUsbDevice for %s", | |
1562 | priv->platform_id); | |
1563 | return FALSE; | |
1564 | } | |
1565 | ||
1566 | if (!g_usb_device_reset (priv->dev, &error_local)) { | |
1567 | g_set_error (error, | |
1568 | DFU_ERROR, | |
1569 | DFU_ERROR_INVALID_DEVICE, | |
1570 | "cannot reset USB device: %s [%i]", | |
1571 | error_local->message, | |
1572 | error_local->code); | |
1573 | return FALSE; | |
1574 | } | |
1575 | return TRUE; | |
1576 | } | |
1577 | ||
1578 | /** | |
1579 | * dfu_device_attach: | |
1580 | * @device: a #DfuDevice | |
1581 | * @error: a #GError, or %NULL | |
1582 | * | |
1583 | * Move device from DFU mode to runtime. | |
1584 | * | |
1585 | * Return value: %TRUE for success | |
1586 | * | |
1587 | * Since: 0.5.4 | |
1588 | **/ | |
1589 | gboolean | |
1590 | dfu_device_attach (DfuDevice *device, GError **error) | |
1591 | { | |
1592 | DfuDevicePrivate *priv = GET_PRIVATE (device); | |
1593 | g_autoptr(GError) error_local = NULL; | |
1594 | ||
1595 | g_return_val_if_fail (DFU_IS_DEVICE (device), FALSE); | |
1596 | g_return_val_if_fail (error == NULL || *error == NULL, FALSE); | |
1597 | ||
1598 | /* already in runtime mode */ | |
1599 | switch (priv->state) { | |
1600 | case DFU_STATE_APP_IDLE: | |
1601 | case DFU_STATE_APP_DETACH: | |
1602 | g_set_error (error, | |
1603 | DFU_ERROR, | |
1604 | DFU_ERROR_NOT_SUPPORTED, | |
1605 | "Already in application runtime mode"); | |
1606 | return FALSE; | |
1607 | default: | |
1608 | break; | |
1609 | } | |
1610 | ||
1611 | /* inform UI there's going to be a re-attach */ | |
1612 | dfu_device_set_state (device, DFU_STATE_DFU_MANIFEST_WAIT_RESET); | |
1613 | ||
1614 | /* handle m-stack DFU bootloaders */ | |
1615 | if (!priv->done_upload_or_download && | |
1616 | (priv->quirks & DFU_DEVICE_QUIRK_ATTACH_UPLOAD_DOWNLOAD) > 0) { | |
1617 | g_autoptr(GBytes) chunk = NULL; | |
1618 | g_autoptr(DfuTarget) target = NULL; | |
1619 | g_debug ("doing dummy upload to work around m-stack quirk"); | |
1620 | target = dfu_device_get_target_by_alt_setting (device, 0, error); | |
1621 | if (target == NULL) | |
1622 | return FALSE; | |
1623 | chunk = dfu_target_upload_chunk (target, 0, NULL, error); | |
1624 | if (chunk == NULL) | |
1625 | return FALSE; | |
1626 | } | |
1627 | ||
1628 | /* there's a a special command for ST devices */ | |
1629 | if (priv->dfuse_supported) { | |
1630 | //FIXME | |
1631 | return TRUE; | |
1632 | } | |
1633 | ||
1634 | /* normal DFU mode just needs a bus reset */ | |
1635 | return dfu_device_reset (device, error); | |
1636 | } | |
1637 | ||
1638 | /** | |
1639 | * dfu_device_percentage_cb: | |
1640 | **/ | |
1641 | static void | |
1642 | dfu_device_percentage_cb (DfuTarget *target, guint percentage, DfuDevice *device) | |
1643 | { | |
1644 | /* FIXME: divide by number of targets? */ | |
1645 | g_signal_emit (device, signals[SIGNAL_PERCENTAGE_CHANGED], 0, percentage); | |
1646 | } | |
1647 | ||
1648 | /** | |
1649 | * dfu_device_upload: | |
1650 | * @device: a #DfuDevice | |
1651 | * @flags: flags to use, e.g. %DFU_TARGET_TRANSFER_FLAG_VERIFY | |
1652 | * @cancellable: a #GCancellable, or %NULL | |
1653 | * @error: a #GError, or %NULL | |
1654 | * | |
1655 | * Uploads firmware from the target to the host. | |
1656 | * | |
1657 | * Return value: (transfer full): the uploaded firmware, or %NULL for error | |
1658 | * | |
1659 | * Since: 0.5.4 | |
1660 | **/ | |
1661 | DfuFirmware * | |
1662 | dfu_device_upload (DfuDevice *device, | |
1663 | DfuTargetTransferFlags flags, | |
1664 | GCancellable *cancellable, | |
1665 | GError **error) | |
1666 | { | |
1667 | DfuDevicePrivate *priv = GET_PRIVATE (device); | |
1668 | guint i; | |
1669 | g_autoptr(DfuFirmware) firmware = NULL; | |
1670 | ||
1671 | /* no backing USB device */ | |
1672 | if (priv->dev == NULL) { | |
1673 | g_set_error (error, | |
1674 | DFU_ERROR, | |
1675 | DFU_ERROR_INTERNAL, | |
1676 | "failed to upload: no GUsbDevice for %s", | |
1677 | priv->platform_id); | |
1678 | return NULL; | |
1679 | } | |
1680 | ||
1681 | /* create ahead of time */ | |
1682 | firmware = dfu_firmware_new (); | |
1683 | dfu_firmware_set_vid (firmware, priv->runtime_vid); | |
1684 | dfu_firmware_set_pid (firmware, priv->runtime_pid); | |
1685 | dfu_firmware_set_release (firmware, 0xffff); | |
1686 | ||
1687 | /* APP -> DFU */ | |
1688 | if (priv->mode == DFU_MODE_RUNTIME) { | |
1689 | if ((flags & DFU_TARGET_TRANSFER_FLAG_DETACH) == 0) { | |
1690 | g_set_error (error, | |
1691 | DFU_ERROR, | |
1692 | DFU_ERROR_NOT_SUPPORTED, | |
1693 | "device is not in DFU mode"); | |
1694 | return FALSE; | |
1695 | } | |
1696 | g_debug ("detaching"); | |
1697 | ||
1698 | /* detach and USB reset */ | |
1699 | if (!dfu_device_detach (device, NULL, error)) | |
1700 | return NULL; | |
1701 | if (!dfu_device_wait_for_replug (device, | |
1702 | DFU_DEVICE_REPLUG_TIMEOUT, | |
1703 | cancellable, | |
1704 | error)) | |
1705 | return NULL; | |
1706 | } | |
1707 | ||
1708 | /* upload from each target */ | |
1709 | for (i = 0; i < priv->targets->len; i++) { | |
1710 | DfuTarget *target; | |
1711 | guint id; | |
1712 | g_autoptr(DfuImage) image = NULL; | |
1713 | ||
1714 | /* upload to target and proxy signals */ | |
1715 | target = g_ptr_array_index (priv->targets, i); | |
1716 | id = g_signal_connect (target, "percentage-changed", | |
1717 | G_CALLBACK (dfu_device_percentage_cb), device); | |
1718 | image = dfu_target_upload (target, | |
1719 | DFU_TARGET_TRANSFER_FLAG_NONE, | |
1720 | cancellable, | |
1721 | error); | |
1722 | g_signal_handler_disconnect (target, id); | |
1723 | if (image == NULL) | |
1724 | return NULL; | |
1725 | dfu_firmware_add_image (firmware, image); | |
1726 | } | |
1727 | ||
1728 | /* do not do the dummy upload for quirked devices */ | |
1729 | priv->done_upload_or_download = TRUE; | |
1730 | ||
1731 | /* choose the most appropriate type */ | |
1732 | if (priv->targets->len > 1) { | |
1733 | g_debug ("switching to DefuSe automatically"); | |
1734 | dfu_firmware_set_format (firmware, DFU_FIRMWARE_FORMAT_DFUSE); | |
1735 | } else { | |
1736 | dfu_firmware_set_format (firmware, DFU_FIRMWARE_FORMAT_DFU_1_0); | |
1737 | } | |
1738 | ||
1739 | /* do host reset */ | |
1740 | if ((flags & DFU_TARGET_TRANSFER_FLAG_ATTACH) > 0 || | |
1741 | (flags & DFU_TARGET_TRANSFER_FLAG_WAIT_RUNTIME) > 0) { | |
1742 | if (!dfu_device_attach (device, error)) | |
1743 | return NULL; | |
1744 | } | |
1745 | ||
1746 | /* boot to runtime */ | |
1747 | if (flags & DFU_TARGET_TRANSFER_FLAG_WAIT_RUNTIME) { | |
1748 | g_debug ("booting to runtime"); | |
1749 | if (!dfu_device_wait_for_replug (device, | |
1750 | DFU_DEVICE_REPLUG_TIMEOUT, | |
1751 | cancellable, | |
1752 | error)) | |
1753 | return NULL; | |
1754 | } | |
1755 | ||
1756 | /* success */ | |
1757 | return g_object_ref (firmware); | |
1758 | } | |
1759 | ||
1760 | /** | |
1761 | * dfu_device_download: | |
1762 | * @device: a #DfuDevice | |
1763 | * @firmware: a #DfuFirmware | |
1764 | * @flags: flags to use, e.g. %DFU_TARGET_TRANSFER_FLAG_VERIFY | |
1765 | * @cancellable: a #GCancellable, or %NULL | |
1766 | * @error: a #GError, or %NULL | |
1767 | * | |
1768 | * Downloads firmware from the host to the target, optionally verifying | |
1769 | * the transfer. | |
1770 | * | |
1771 | * Return value: %TRUE for success | |
1772 | * | |
1773 | * Since: 0.5.4 | |
1774 | **/ | |
1775 | gboolean | |
1776 | dfu_device_download (DfuDevice *device, | |
1777 | DfuFirmware *firmware, | |
1778 | DfuTargetTransferFlags flags, | |
1779 | GCancellable *cancellable, | |
1780 | GError **error) | |
1781 | { | |
1782 | DfuDevicePrivate *priv = GET_PRIVATE (device); | |
1783 | GPtrArray *images; | |
1784 | gboolean ret; | |
1785 | guint i; | |
1786 | g_autoptr(GPtrArray) targets = NULL; | |
1787 | ||
1788 | /* no backing USB device */ | |
1789 | if (priv->dev == NULL) { | |
1790 | g_set_error (error, | |
1791 | DFU_ERROR, | |
1792 | DFU_ERROR_INTERNAL, | |
1793 | "failed to download: no GUsbDevice for %s", | |
1794 | priv->platform_id); | |
1795 | return FALSE; | |
1796 | } | |
1797 | ||
1798 | /* do we allow wildcard VID:PID matches */ | |
1799 | if ((flags & DFU_TARGET_TRANSFER_FLAG_WILDCARD_VID) == 0) { | |
1800 | if (dfu_firmware_get_vid (firmware) == 0xffff) { | |
1801 | g_set_error (error, | |
1802 | DFU_ERROR, | |
1803 | DFU_ERROR_NOT_SUPPORTED, | |
1804 | "firmware vendor ID not specified"); | |
1805 | return FALSE; | |
1806 | } | |
1807 | } | |
1808 | if ((flags & DFU_TARGET_TRANSFER_FLAG_WILDCARD_PID) == 0) { | |
1809 | if (dfu_firmware_get_pid (firmware) == 0xffff) { | |
1810 | g_set_error (error, | |
1811 | DFU_ERROR, | |
1812 | DFU_ERROR_NOT_SUPPORTED, | |
1813 | "firmware product ID not specified"); | |
1814 | return FALSE; | |
1815 | } | |
1816 | } | |
1817 | ||
1818 | /* check vendor matches */ | |
1819 | if (dfu_firmware_get_vid (firmware) != 0xffff && | |
1820 | priv->runtime_pid != 0xffff && | |
1821 | dfu_firmware_get_vid (firmware) != priv->runtime_vid) { | |
1822 | g_set_error (error, | |
1823 | DFU_ERROR, | |
1824 | DFU_ERROR_NOT_SUPPORTED, | |
1825 | "vendor ID incorrect, expected 0x%04x got 0x%04x\n", | |
1826 | dfu_firmware_get_vid (firmware), | |
1827 | priv->runtime_vid); | |
1828 | return FALSE; | |
1829 | } | |
1830 | ||
1831 | /* check product matches */ | |
1832 | if (dfu_firmware_get_pid (firmware) != 0xffff && | |
1833 | priv->runtime_pid != 0xffff && | |
1834 | dfu_firmware_get_pid (firmware) != priv->runtime_pid) { | |
1835 | g_set_error (error, | |
1836 | DFU_ERROR, | |
1837 | DFU_ERROR_NOT_SUPPORTED, | |
1838 | "product ID incorrect, expected 0x%04x got 0x%04x", | |
1839 | dfu_firmware_get_pid (firmware), | |
1840 | priv->runtime_pid); | |
1841 | return FALSE; | |
1842 | } | |
1843 | ||
1844 | /* APP -> DFU */ | |
1845 | if (priv->mode == DFU_MODE_RUNTIME) { | |
1846 | if ((flags & DFU_TARGET_TRANSFER_FLAG_DETACH) == 0) { | |
1847 | g_set_error (error, | |
1848 | DFU_ERROR, | |
1849 | DFU_ERROR_NOT_SUPPORTED, | |
1850 | "device is not in DFU mode"); | |
1851 | return FALSE; | |
1852 | } | |
1853 | ||
1854 | /* detach and USB reset */ | |
1855 | g_debug ("detaching"); | |
1856 | if (!dfu_device_detach (device, NULL, error)) | |
1857 | return FALSE; | |
1858 | if (!dfu_device_wait_for_replug (device, | |
1859 | DFU_DEVICE_REPLUG_TIMEOUT, | |
1860 | NULL, | |
1861 | error)) | |
1862 | return FALSE; | |
1863 | } | |
1864 | ||
1865 | /* download each target */ | |
1866 | images = dfu_firmware_get_images (firmware); | |
1867 | if (images->len == 0) { | |
1868 | g_set_error_literal (error, | |
1869 | DFU_ERROR, | |
1870 | DFU_ERROR_INVALID_FILE, | |
1871 | "no images in firmware file"); | |
1872 | return FALSE; | |
1873 | } | |
1874 | for (i = 0; i < images->len; i++) { | |
1875 | DfuCipherKind cipher_fw; | |
1876 | DfuCipherKind cipher_target; | |
1877 | DfuImage *image; | |
1878 | DfuTargetTransferFlags flags_local = DFU_TARGET_TRANSFER_FLAG_NONE; | |
1879 | const gchar *alt_name; | |
1880 | guint id; | |
1881 | g_autoptr(DfuTarget) target_tmp = NULL; | |
1882 | ||
1883 | image = g_ptr_array_index (images, i); | |
1884 | target_tmp = dfu_device_get_target_by_alt_setting (device, | |
1885 | dfu_image_get_alt_setting (image), | |
1886 | error); | |
1887 | if (target_tmp == NULL) | |
1888 | return FALSE; | |
1889 | ||
1890 | /* we don't actually need to print this, but it makes sure the | |
1891 | * target is setup prior to doing the cipher checks */ | |
1892 | alt_name = dfu_target_get_alt_name (target_tmp, error); | |
1893 | if (alt_name == NULL) | |
1894 | return FALSE; | |
1895 | g_debug ("downloading to target: %s", alt_name); | |
1896 | ||
1897 | /* check we're flashing a compatible firmware */ | |
1898 | cipher_target = dfu_target_get_cipher_kind (target_tmp); | |
1899 | cipher_fw = dfu_firmware_get_cipher_kind (firmware); | |
1900 | if ((flags & DFU_TARGET_TRANSFER_FLAG_ANY_CIPHER) == 0) { | |
1901 | if (cipher_fw != DFU_CIPHER_KIND_NONE && | |
1902 | cipher_target == DFU_CIPHER_KIND_NONE) { | |
1903 | g_set_error (error, | |
1904 | DFU_ERROR, | |
1905 | DFU_ERROR_INVALID_FILE, | |
1906 | "Device is only accepting " | |
1907 | "unsigned firmware, not %s", | |
1908 | dfu_cipher_kind_to_string (cipher_fw)); | |
1909 | return FALSE; | |
1910 | } | |
1911 | if (cipher_fw == DFU_CIPHER_KIND_NONE && | |
1912 | cipher_target != DFU_CIPHER_KIND_NONE) { | |
1913 | g_set_error (error, | |
1914 | DFU_ERROR, | |
1915 | DFU_ERROR_INVALID_FILE, | |
1916 | "Device is only accepting " | |
1917 | "firmware with %s cipher kind", | |
1918 | dfu_cipher_kind_to_string (cipher_target)); | |
1919 | return FALSE; | |
1920 | } | |
1921 | } | |
1922 | ||
1923 | /* download onto target */ | |
1924 | if (flags & DFU_TARGET_TRANSFER_FLAG_VERIFY) | |
1925 | flags_local = DFU_TARGET_TRANSFER_FLAG_VERIFY; | |
1926 | id = g_signal_connect (target_tmp, "percentage-changed", | |
1927 | G_CALLBACK (dfu_device_percentage_cb), device); | |
1928 | ret = dfu_target_download (target_tmp, | |
1929 | image, | |
1930 | flags_local, | |
1931 | cancellable, | |
1932 | error); | |
1933 | g_signal_handler_disconnect (target_tmp, id); | |
1934 | if (!ret) | |
1935 | return FALSE; | |
1936 | } | |
1937 | ||
1938 | /* do not do the dummy upload for quirked devices */ | |
1939 | priv->done_upload_or_download = TRUE; | |
1940 | ||
1941 | /* attempt to switch back to runtime */ | |
1942 | if ((flags & DFU_TARGET_TRANSFER_FLAG_ATTACH) > 0 || | |
1943 | (flags & DFU_TARGET_TRANSFER_FLAG_WAIT_RUNTIME) > 0) { | |
1944 | if (!dfu_device_attach (device, error)) | |
1945 | return FALSE; | |
1946 | } | |
1947 | ||
1948 | /* boot to runtime */ | |
1949 | if (flags & DFU_TARGET_TRANSFER_FLAG_WAIT_RUNTIME) { | |
1950 | g_debug ("booting to runtime to set auto-boot"); | |
1951 | if (!dfu_device_wait_for_replug (device, | |
1952 | DFU_DEVICE_REPLUG_TIMEOUT, | |
1953 | cancellable, | |
1954 | error)) | |
1955 | return FALSE; | |
1956 | } | |
1957 | ||
1958 | return TRUE; | |
1959 | } | |
1960 | ||
1961 | /** | |
1962 | * dfu_device_error_fixup: | |
1963 | **/ | |
1964 | void | |
1965 | dfu_device_error_fixup (DfuDevice *device, | |
1966 | GCancellable *cancellable, | |
1967 | GError **error) | |
1968 | { | |
1969 | DfuDevicePrivate *priv = GET_PRIVATE (device); | |
1970 | ||
1971 | /* sad panda */ | |
1972 | if (error == NULL) | |
1973 | return; | |
1974 | ||
1975 | /* not the right error to query */ | |
1976 | if (!g_error_matches (*error, | |
1977 | G_USB_DEVICE_ERROR, | |
1978 | G_USB_DEVICE_ERROR_NOT_SUPPORTED)) | |
1979 | return; | |
1980 | ||
1981 | /* get the status */ | |
1982 | if (!dfu_device_refresh (device, cancellable, NULL)) | |
1983 | return; | |
1984 | ||
1985 | /* not in an error state */ | |
1986 | if (priv->state != DFU_STATE_DFU_ERROR) | |
1987 | return; | |
1988 | ||
1989 | /* prefix the error */ | |
1990 | switch (priv->status) { | |
1991 | case DFU_STATUS_OK: | |
1992 | /* ignore */ | |
1993 | break; | |
1994 | case DFU_STATUS_ERR_VENDOR: | |
1995 | g_prefix_error (error, "read protection is active: "); | |
1996 | break; | |
1997 | default: | |
1998 | g_prefix_error (error, "[%s,%s]: ", | |
1999 | dfu_state_to_string (priv->state), | |
2000 | dfu_status_to_string (priv->status)); | |
2001 | break; | |
2002 | } | |
2003 | } | |
2004 | ||
2005 | /** | |
2006 | * dfu_device_get_quirks_as_string: (skip) | |
2007 | * @device: a #DfuDevice | |
2008 | * | |
2009 | * Gets a string describing the quirks set for a device. | |
2010 | * | |
2011 | * Return value: string, or %NULL for no quirks | |
2012 | * | |
2013 | * Since: 0.5.4 | |
2014 | **/ | |
2015 | gchar * | |
2016 | dfu_device_get_quirks_as_string (DfuDevice *device) | |
2017 | { | |
2018 | DfuDevicePrivate *priv = GET_PRIVATE (device); | |
2019 | GString *str; | |
2020 | ||
2021 | /* just append to a string */ | |
2022 | str = g_string_new (""); | |
2023 | if (priv->quirks & DFU_DEVICE_QUIRK_IGNORE_POLLTIMEOUT) | |
2024 | g_string_append_printf (str, "ignore-polltimeout|"); | |
2025 | if (priv->quirks & DFU_DEVICE_QUIRK_FORCE_DFU_MODE) | |
2026 | g_string_append_printf (str, "force-dfu-mode|"); | |
2027 | if (priv->quirks & DFU_DEVICE_QUIRK_IGNORE_INVALID_VERSION) | |
2028 | g_string_append_printf (str, "ignore-invalid-version|"); | |
2029 | if (priv->quirks & DFU_DEVICE_QUIRK_USE_PROTOCOL_ZERO) | |
2030 | g_string_append_printf (str, "use-protocol-zero|"); | |
2031 | if (priv->quirks & DFU_DEVICE_QUIRK_NO_PID_CHANGE) | |
2032 | g_string_append_printf (str, "no-pid-change|"); | |
2033 | if (priv->quirks & DFU_DEVICE_QUIRK_NO_GET_STATUS_UPLOAD) | |
2034 | g_string_append_printf (str, "no-get-status-upload|"); | |
2035 | if (priv->quirks & DFU_DEVICE_QUIRK_NO_DFU_RUNTIME) | |
2036 | g_string_append_printf (str, "no-dfu-runtime|"); | |
2037 | if (priv->quirks & DFU_DEVICE_QUIRK_ATTACH_UPLOAD_DOWNLOAD) | |
2038 | g_string_append_printf (str, "attach-upload-download|"); | |
2039 | ||
2040 | /* a well behaved device */ | |
2041 | if (str->len == 0) { | |
2042 | g_string_free (str, TRUE); | |
2043 | return NULL; | |
2044 | } | |
2045 | ||
2046 | /* remove trailing pipe */ | |
2047 | g_string_truncate (str, str->len - 1); | |
2048 | return g_string_free (str, FALSE); | |
2049 | } |
0 | /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- | |
1 | * | |
2 | * Copyright (C) 2015 Richard Hughes <richard@hughsie.com> | |
3 | * | |
4 | * Licensed under the GNU Lesser General Public License Version 2.1 | |
5 | * | |
6 | * This library is free software; you can redistribute it and/or | |
7 | * modify it under the terms of the GNU Lesser General Public | |
8 | * License as published by the Free Software Foundation; either | |
9 | * version 2.1 of the License, or (at your option) any later version. | |
10 | * | |
11 | * This library is distributed in the hope that it will be useful, | |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
14 | * Lesser General Public License for more details. | |
15 | * | |
16 | * You should have received a copy of the GNU Lesser General Public | |
17 | * License along with this library; if not, write to the Free Software | |
18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | |
19 | */ | |
20 | ||
21 | #ifndef __DFU_DEVICE_H | |
22 | #define __DFU_DEVICE_H | |
23 | ||
24 | #include <glib-object.h> | |
25 | #include <gio/gio.h> | |
26 | #include <gusb.h> | |
27 | ||
28 | #include "dfu-common.h" | |
29 | #include "dfu-target.h" | |
30 | #include "dfu-firmware.h" | |
31 | ||
32 | G_BEGIN_DECLS | |
33 | ||
34 | #define DFU_TYPE_DEVICE (dfu_device_get_type ()) | |
35 | G_DECLARE_DERIVABLE_TYPE (DfuDevice, dfu_device, DFU, DEVICE, GObject) | |
36 | ||
37 | /** | |
38 | * DfuDeviceOpenFlags: | |
39 | * @DFU_DEVICE_OPEN_FLAG_NONE: No flags set | |
40 | * @DFU_DEVICE_OPEN_FLAG_NO_AUTO_REFRESH: Do not do the initial GET_STATUS | |
41 | * | |
42 | * The optional flags used for opening the target. | |
43 | **/ | |
44 | typedef enum { | |
45 | DFU_DEVICE_OPEN_FLAG_NONE = 0, | |
46 | DFU_DEVICE_OPEN_FLAG_NO_AUTO_REFRESH = (1 << 0), | |
47 | /*< private >*/ | |
48 | DFU_DEVICE_OPEN_FLAG_LAST, | |
49 | } DfuDeviceOpenFlags; | |
50 | ||
51 | struct _DfuDeviceClass | |
52 | { | |
53 | GObjectClass parent_class; | |
54 | void (*status_changed) (DfuDevice *device, | |
55 | DfuStatus status); | |
56 | void (*state_changed) (DfuDevice *device, | |
57 | DfuState state); | |
58 | void (*percentage_changed) (DfuDevice *device, | |
59 | guint percentage); | |
60 | /*< private >*/ | |
61 | /* Padding for future expansion */ | |
62 | void (*_dfu_device_reserved1) (void); | |
63 | void (*_dfu_device_reserved2) (void); | |
64 | void (*_dfu_device_reserved3) (void); | |
65 | void (*_dfu_device_reserved4) (void); | |
66 | void (*_dfu_device_reserved5) (void); | |
67 | void (*_dfu_device_reserved6) (void); | |
68 | void (*_dfu_device_reserved7) (void); | |
69 | void (*_dfu_device_reserved8) (void); | |
70 | void (*_dfu_device_reserved9) (void); | |
71 | }; | |
72 | ||
73 | DfuDevice *dfu_device_new (GUsbDevice *dev); | |
74 | gboolean dfu_device_open (DfuDevice *device, | |
75 | DfuDeviceOpenFlags flags, | |
76 | GCancellable *cancellable, | |
77 | GError **error); | |
78 | gboolean dfu_device_close (DfuDevice *device, | |
79 | GError **error); | |
80 | const gchar *dfu_device_get_platform_id (DfuDevice *device); | |
81 | GPtrArray *dfu_device_get_targets (DfuDevice *device); | |
82 | DfuTarget *dfu_device_get_target_by_alt_setting (DfuDevice *device, | |
83 | guint8 alt_setting, | |
84 | GError **error); | |
85 | DfuTarget *dfu_device_get_target_by_alt_name (DfuDevice *device, | |
86 | const gchar *alt_name, | |
87 | GError **error); | |
88 | const gchar *dfu_device_get_display_name (DfuDevice *device); | |
89 | guint16 dfu_device_get_runtime_vid (DfuDevice *device); | |
90 | guint16 dfu_device_get_runtime_pid (DfuDevice *device); | |
91 | guint16 dfu_device_get_runtime_release (DfuDevice *device); | |
92 | gboolean dfu_device_reset (DfuDevice *device, | |
93 | GError **error); | |
94 | gboolean dfu_device_attach (DfuDevice *device, | |
95 | GError **error); | |
96 | gboolean dfu_device_wait_for_replug (DfuDevice *device, | |
97 | guint timeout, | |
98 | GCancellable *cancellable, | |
99 | GError **error); | |
100 | DfuFirmware *dfu_device_upload (DfuDevice *device, | |
101 | DfuTargetTransferFlags flags, | |
102 | GCancellable *cancellable, | |
103 | GError **error); | |
104 | gboolean dfu_device_download (DfuDevice *device, | |
105 | DfuFirmware *firmware, | |
106 | DfuTargetTransferFlags flags, | |
107 | GCancellable *cancellable, | |
108 | GError **error); | |
109 | gboolean dfu_device_refresh (DfuDevice *device, | |
110 | GCancellable *cancellable, | |
111 | GError **error); | |
112 | gboolean dfu_device_detach (DfuDevice *device, | |
113 | GCancellable *cancellable, | |
114 | GError **error); | |
115 | gboolean dfu_device_abort (DfuDevice *device, | |
116 | GCancellable *cancellable, | |
117 | GError **error); | |
118 | gboolean dfu_device_clear_status (DfuDevice *device, | |
119 | GCancellable *cancellable, | |
120 | GError **error); | |
121 | ||
122 | guint8 dfu_device_get_interface (DfuDevice *device); | |
123 | DfuMode dfu_device_get_mode (DfuDevice *device); | |
124 | DfuState dfu_device_get_state (DfuDevice *device); | |
125 | DfuStatus dfu_device_get_status (DfuDevice *device); | |
126 | guint16 dfu_device_get_transfer_size (DfuDevice *device); | |
127 | guint dfu_device_get_timeout (DfuDevice *device); | |
128 | gboolean dfu_device_can_upload (DfuDevice *device); | |
129 | gboolean dfu_device_can_download (DfuDevice *device); | |
130 | ||
131 | void dfu_device_set_transfer_size (DfuDevice *device, | |
132 | guint16 transfer_size); | |
133 | void dfu_device_set_timeout (DfuDevice *device, | |
134 | guint timeout_ms); | |
135 | ||
136 | G_END_DECLS | |
137 | ||
138 | #endif /* __DFU_DEVICE_H */ |
0 | /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- | |
1 | * | |
2 | * Copyright (C) 2015 Richard Hughes <richard@hughsie.com> | |
3 | * | |
4 | * Licensed under the GNU Lesser General Public License Version 2.1 | |
5 | * | |
6 | * This library is free software; you can redistribute it and/or | |
7 | * modify it under the terms of the GNU Lesser General Public | |
8 | * License as published by the Free Software Foundation; either | |
9 | * version 2.1 of the License, or (at your option) any later version. | |
10 | * | |
11 | * This library is distributed in the hope that it will be useful, | |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
14 | * Lesser General Public License for more details. | |
15 | * | |
16 | * You should have received a copy of the GNU Lesser General Public | |
17 | * License along with this library; if not, write to the Free Software | |
18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | |
19 | */ | |
20 | ||
21 | #ifndef __DFU_ELEMENT_PRIVATE_H | |
22 | #define __DFU_ELEMENT_PRIVATE_H | |
23 | ||
24 | #include "dfu-element.h" | |
25 | ||
26 | G_BEGIN_DECLS | |
27 | ||
28 | DfuElement *dfu_element_from_dfuse (const guint8 *data, | |
29 | guint32 length, | |
30 | guint32 *consumed, | |
31 | GError **error); | |
32 | GBytes *dfu_element_to_dfuse (DfuElement *element); | |
33 | ||
34 | G_END_DECLS | |
35 | ||
36 | #endif /* __DFU_ELEMENT_PRIVATE_H */ |
0 | /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- | |
1 | * | |
2 | * Copyright (C) 2015 Richard Hughes <richard@hughsie.com> | |
3 | * | |
4 | * Licensed under the GNU Lesser General Public License Version 2.1 | |
5 | * | |
6 | * This library is free software; you can redistribute it and/or | |
7 | * modify it under the terms of the GNU Lesser General Public | |
8 | * License as published by the Free Software Foundation; either | |
9 | * version 2.1 of the License, or (at your option) any later version. | |
10 | * | |
11 | * This library is distributed in the hope that it will be useful, | |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
14 | * Lesser General Public License for more details. | |
15 | * | |
16 | * You should have received a copy of the GNU Lesser General Public | |
17 | * License along with this library; if not, write to the Free Software | |
18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | |
19 | */ | |
20 | ||
21 | /** | |
22 | * SECTION:dfu-element | |
23 | * @short_description: Object representing a binary element | |
24 | * | |
25 | * This object represents an binary blob of data at a specific address. | |
26 | * | |
27 | * This allows relocatable data segments to be stored in different | |
28 | * locations on the device itself. | |
29 | * | |
30 | * See also: #DfuImage, #DfuFirmware | |
31 | */ | |
32 | ||
33 | #include "config.h" | |
34 | ||
35 | #include <string.h> | |
36 | #include <stdio.h> | |
37 | ||
38 | #include "dfu-common.h" | |
39 | #include "dfu-element-private.h" | |
40 | #include "dfu-error.h" | |
41 | ||
42 | static void dfu_element_finalize (GObject *object); | |
43 | ||
44 | /** | |
45 | * DfuElementPrivate: | |
46 | * | |
47 | * Private #DfuElement data | |
48 | **/ | |
49 | typedef struct { | |
50 | GBytes *contents; | |
51 | guint32 target_size; | |
52 | guint32 address; | |
53 | } DfuElementPrivate; | |
54 | ||
55 | G_DEFINE_TYPE_WITH_PRIVATE (DfuElement, dfu_element, G_TYPE_OBJECT) | |
56 | #define GET_PRIVATE(o) (dfu_element_get_instance_private (o)) | |
57 | ||
58 | /** | |
59 | * dfu_element_class_init: | |
60 | **/ | |
61 | static void | |
62 | dfu_element_class_init (DfuElementClass *klass) | |
63 | { | |
64 | GObjectClass *object_class = G_OBJECT_CLASS (klass); | |
65 | object_class->finalize = dfu_element_finalize; | |
66 | } | |
67 | ||
68 | /** | |
69 | * dfu_element_init: | |
70 | **/ | |
71 | static void | |
72 | dfu_element_init (DfuElement *element) | |
73 | { | |
74 | } | |
75 | ||
76 | /** | |
77 | * dfu_element_finalize: | |
78 | **/ | |
79 | static void | |
80 | dfu_element_finalize (GObject *object) | |
81 | { | |
82 | DfuElement *element = DFU_ELEMENT (object); | |
83 | DfuElementPrivate *priv = GET_PRIVATE (element); | |
84 | ||
85 | if (priv->contents != NULL) | |
86 | g_bytes_unref (priv->contents); | |
87 | ||
88 | G_OBJECT_CLASS (dfu_element_parent_class)->finalize (object); | |
89 | } | |
90 | ||
91 | /** | |
92 | * dfu_element_new: | |
93 | * | |
94 | * Creates a new DFU element object. | |
95 | * | |
96 | * Return value: a new #DfuElement | |
97 | * | |
98 | * Since: 0.5.4 | |
99 | **/ | |
100 | DfuElement * | |
101 | dfu_element_new (void) | |
102 | { | |
103 | DfuElement *element; | |
104 | element = g_object_new (DFU_TYPE_ELEMENT, NULL); | |
105 | return element; | |
106 | } | |
107 | ||
108 | /** | |
109 | * dfu_element_get_contents: | |
110 | * @element: a #DfuElement | |
111 | * | |
112 | * Gets the element data. | |
113 | * | |
114 | * Return value: (transfer none): element data | |
115 | * | |
116 | * Since: 0.5.4 | |
117 | **/ | |
118 | GBytes * | |
119 | dfu_element_get_contents (DfuElement *element) | |
120 | { | |
121 | DfuElementPrivate *priv = GET_PRIVATE (element); | |
122 | g_return_val_if_fail (DFU_IS_ELEMENT (element), NULL); | |
123 | return priv->contents; | |
124 | } | |
125 | ||
126 | /** | |
127 | * dfu_element_get_address: | |
128 | * @element: a #DfuElement | |
129 | * | |
130 | * Gets the alternate setting. | |
131 | * | |
132 | * Return value: integer, or 0x00 for unset | |
133 | * | |
134 | * Since: 0.5.4 | |
135 | **/ | |
136 | guint32 | |
137 | dfu_element_get_address (DfuElement *element) | |
138 | { | |
139 | DfuElementPrivate *priv = GET_PRIVATE (element); | |
140 | g_return_val_if_fail (DFU_IS_ELEMENT (element), 0x00); | |
141 | return priv->address; | |
142 | } | |
143 | ||
144 | /** | |
145 | * dfu_element_set_contents: | |
146 | * @element: a #DfuElement | |
147 | * @contents: element data | |
148 | * | |
149 | * Sets the element data. | |
150 | * | |
151 | * Since: 0.5.4 | |
152 | **/ | |
153 | void | |
154 | dfu_element_set_contents (DfuElement *element, GBytes *contents) | |
155 | { | |
156 | DfuElementPrivate *priv = GET_PRIVATE (element); | |
157 | g_return_if_fail (DFU_IS_ELEMENT (element)); | |
158 | g_return_if_fail (contents != NULL); | |
159 | if (priv->contents == contents) | |
160 | return; | |
161 | if (priv->contents != NULL) | |
162 | g_bytes_unref (priv->contents); | |
163 | priv->contents = g_bytes_ref (contents); | |
164 | } | |
165 | ||
166 | /** | |
167 | * dfu_element_set_address: | |
168 | * @element: a #DfuElement | |
169 | * @address: vendor ID, or 0xffff for unset | |
170 | * | |
171 | * Sets the vendor ID. | |
172 | * | |
173 | * Since: 0.5.4 | |
174 | **/ | |
175 | void | |
176 | dfu_element_set_address (DfuElement *element, guint32 address) | |
177 | { | |
178 | DfuElementPrivate *priv = GET_PRIVATE (element); | |
179 | g_return_if_fail (DFU_IS_ELEMENT (element)); | |
180 | priv->address = address; | |
181 | } | |
182 | ||
183 | /** | |
184 | * dfu_element_to_string: | |
185 | * @element: a #DfuElement | |
186 | * | |
187 | * Returns a string representaiton of the object. | |
188 | * | |
189 | * Return value: NULL terminated string, or %NULL for invalid | |
190 | * | |
191 | * Since: 0.5.4 | |
192 | **/ | |
193 | gchar * | |
194 | dfu_element_to_string (DfuElement *element) | |
195 | { | |
196 | DfuElementPrivate *priv = GET_PRIVATE (element); | |
197 | GString *str; | |
198 | ||
199 | g_return_val_if_fail (DFU_IS_ELEMENT (element), NULL); | |
200 | ||
201 | str = g_string_new (""); | |
202 | g_string_append_printf (str, "address: 0x%02x\n", priv->address); | |
203 | if (priv->target_size > 0) { | |
204 | g_string_append_printf (str, "target: 0x%04x\n", | |
205 | priv->target_size); | |
206 | } | |
207 | if (priv->contents != NULL) { | |
208 | g_string_append_printf (str, "contents: 0x%04x\n", | |
209 | (guint32) g_bytes_get_size (priv->contents)); | |
210 | } | |
211 | ||
212 | g_string_truncate (str, str->len - 1); | |
213 | return g_string_free (str, FALSE); | |
214 | } | |
215 | ||
216 | /** | |
217 | * dfu_element_set_target_size: | |
218 | * @element: a #DfuElement | |
219 | * @target_size: size in bytes | |
220 | * | |
221 | * Sets a target size for the element. If the prepared element is smaller | |
222 | * than this then it will be padded with NUL bytes up to the required size. | |
223 | * | |
224 | * Since: 0.5.4 | |
225 | **/ | |
226 | void | |
227 | dfu_element_set_target_size (DfuElement *element, guint32 target_size) | |
228 | { | |
229 | DfuElementPrivate *priv = GET_PRIVATE (element); | |
230 | const guint8 *data; | |
231 | gsize length; | |
232 | guint8 *buf; | |
233 | g_autoptr(GBytes) contents_padded = NULL; | |
234 | ||
235 | g_return_if_fail (DFU_IS_ELEMENT (element)); | |
236 | ||
237 | /* save for dump */ | |
238 | priv->target_size = target_size; | |
239 | ||
240 | /* no need to pad */ | |
241 | if (priv->contents == NULL) | |
242 | return; | |
243 | if (g_bytes_get_size (priv->contents) >= target_size) | |
244 | return; | |
245 | ||
246 | /* reallocate and pad */ | |
247 | data = g_bytes_get_data (priv->contents, &length); | |
248 | buf = g_malloc0 (target_size); | |
249 | g_assert (buf != NULL); | |
250 | memcpy (buf, data, length); | |
251 | ||
252 | /* replace */ | |
253 | g_bytes_unref (priv->contents); | |
254 | priv->contents = g_bytes_new_take (buf, target_size); | |
255 | } | |
256 | ||
257 | /* DfuSe element header */ | |
258 | typedef struct __attribute__((packed)) { | |
259 | guint32 address; | |
260 | guint32 size; | |
261 | } DfuSeElementPrefix; | |
262 | ||
263 | /** | |
264 | * dfu_element_from_dfuse: (skip) | |
265 | * @data: data buffer | |
266 | * @length: length of @data we can access | |
267 | * @consumed: (out): the number of bytes we consued | |
268 | * @error: a #GError, or %NULL | |
269 | * | |
270 | * Unpacks an element from DfuSe data. | |
271 | * | |
272 | * Returns: a #DfuElement, or %NULL for error | |
273 | **/ | |
274 | DfuElement * | |
275 | dfu_element_from_dfuse (const guint8 *data, | |
276 | guint32 length, | |
277 | guint32 *consumed, | |
278 | GError **error) | |
279 | { | |
280 | DfuElement *element = NULL; | |
281 | DfuElementPrivate *priv; | |
282 | DfuSeElementPrefix *el = (DfuSeElementPrefix *) data; | |
283 | guint32 size; | |
284 | ||
285 | g_assert_cmpint(sizeof(DfuSeElementPrefix), ==, 8); | |
286 | ||
287 | /* check input buffer size */ | |
288 | if (length < sizeof(DfuSeElementPrefix)) { | |
289 | g_set_error (error, | |
290 | DFU_ERROR, | |
291 | DFU_ERROR_INTERNAL, | |
292 | "invalid element data size %u", | |
293 | (guint32) length); | |
294 | return NULL; | |
295 | } | |
296 | ||
297 | /* check size */ | |
298 | size = GUINT32_FROM_LE (el->size); | |
299 | if (size + sizeof(DfuSeElementPrefix) > length) { | |
300 | g_set_error (error, | |
301 | DFU_ERROR, | |
302 | DFU_ERROR_INTERNAL, | |
303 | "invalid element size %u, only %u bytes left", | |
304 | size, | |
305 | (guint32) (length - sizeof(DfuSeElementPrefix))); | |
306 | return NULL; | |
307 | } | |
308 | ||
309 | /* create new element */ | |
310 | element = dfu_element_new (); | |
311 | priv = GET_PRIVATE (element); | |
312 | priv->address = GUINT32_FROM_LE (el->address); | |
313 | priv->contents = g_bytes_new (data + sizeof(DfuSeElementPrefix), size); | |
314 | ||
315 | /* return size */ | |
316 | if (consumed != NULL) | |
317 | *consumed = sizeof(DfuSeElementPrefix) + size; | |
318 | ||
319 | return element; | |
320 | } | |
321 | ||
322 | /** | |
323 | * dfu_element_to_dfuse: (skip) | |
324 | * @element: a #DfuElement | |
325 | * | |
326 | * Packs a DfuSe element. | |
327 | * | |
328 | * Returns: (transfer full): the packed data | |
329 | **/ | |
330 | GBytes * | |
331 | dfu_element_to_dfuse (DfuElement *element) | |
332 | { | |
333 | DfuElementPrivate *priv = GET_PRIVATE (element); | |
334 | DfuSeElementPrefix *el; | |
335 | const guint8 *data; | |
336 | gsize length; | |
337 | guint8 *buf; | |
338 | ||
339 | data = g_bytes_get_data (priv->contents, &length); | |
340 | buf = g_malloc0 (length + sizeof (DfuSeElementPrefix)); | |
341 | el = (DfuSeElementPrefix *) buf; | |
342 | el->address = GUINT32_TO_LE (priv->address); | |
343 | el->size = GUINT32_TO_LE (length); | |
344 | ||
345 | memcpy (buf + sizeof (DfuSeElementPrefix), data, length); | |
346 | return g_bytes_new_take (buf, length + sizeof (DfuSeElementPrefix)); | |
347 | } |
0 | /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- | |
1 | * | |
2 | * Copyright (C) 2015 Richard Hughes <richard@hughsie.com> | |
3 | * | |
4 | * Licensed under the GNU Lesser General Public License Version 2.1 | |
5 | * | |
6 | * This library is free software; you can redistribute it and/or | |
7 | * modify it under the terms of the GNU Lesser General Public | |
8 | * License as published by the Free Software Foundation; either | |
9 | * version 2.1 of the License, or (at your option) any later version. | |
10 | * | |
11 | * This library is distributed in the hope that it will be useful, | |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
14 | * Lesser General Public License for more details. | |
15 | * | |
16 | * You should have received a copy of the GNU Lesser General Public | |
17 | * License along with this library; if not, write to the Free Software | |
18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | |
19 | */ | |
20 | ||
21 | #ifndef __DFU_ELEMENT_H | |
22 | #define __DFU_ELEMENT_H | |
23 | ||
24 | #include <glib-object.h> | |
25 | #include <gio/gio.h> | |
26 | ||
27 | G_BEGIN_DECLS | |
28 | ||
29 | #define DFU_TYPE_ELEMENT (dfu_element_get_type ()) | |
30 | G_DECLARE_DERIVABLE_TYPE (DfuElement, dfu_element, DFU, ELEMENT, GObject) | |
31 | ||
32 | struct _DfuElementClass | |
33 | { | |
34 | GObjectClass parent_class; | |
35 | /*< private >*/ | |
36 | /* Padding for future expansion */ | |
37 | void (*_dfu_element_reserved1) (void); | |
38 | void (*_dfu_element_reserved2) (void); | |
39 | void (*_dfu_element_reserved3) (void); | |
40 | void (*_dfu_element_reserved4) (void); | |
41 | void (*_dfu_element_reserved5) (void); | |
42 | void (*_dfu_element_reserved6) (void); | |
43 | void (*_dfu_element_reserved7) (void); | |
44 | void (*_dfu_element_reserved8) (void); | |
45 | void (*_dfu_element_reserved9) (void); | |
46 | }; | |
47 | ||
48 | DfuElement *dfu_element_new (void); | |
49 | ||
50 | GBytes *dfu_element_get_contents (DfuElement *element); | |
51 | guint32 dfu_element_get_address (DfuElement *element); | |
52 | ||
53 | void dfu_element_set_contents (DfuElement *element, | |
54 | GBytes *contents); | |
55 | void dfu_element_set_address (DfuElement *element, | |
56 | guint32 address); | |
57 | void dfu_element_set_target_size (DfuElement *element, | |
58 | guint32 target_size); | |
59 | ||
60 | gchar *dfu_element_to_string (DfuElement *element); | |
61 | ||
62 | G_END_DECLS | |
63 | ||
64 | #endif /* __DFU_ELEMENT_H */ |
0 | /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- | |
1 | * | |
2 | * Copyright (C) 2015 Richard Hughes <richard@hughsie.com> | |
3 | * | |
4 | * Licensed under the GNU Lesser General Public License Version 2.1 | |
5 | * | |
6 | * This library is free software; you can redistribute it and/or | |
7 | * modify it under the terms of the GNU Lesser General Public | |
8 | * License as published by the Free Software Foundation; either | |
9 | * version 2.1 of the License, or (at your option) any later version. | |
10 | * | |
11 | * This library is distributed in the hope that it will be useful, | |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
14 | * Lesser General Public License for more details. | |
15 | * | |
16 | * You should have received a copy of the GNU Lesser General Public | |
17 | * License along with this library; if not, write to the Free Software | |
18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | |
19 | */ | |
20 | ||
21 | /** | |
22 | * SECTION:dfu-error | |
23 | * @short_description: Error defines for libdfu | |
24 | * | |
25 | * This documents the error domain and codes used by libdfu. | |
26 | */ | |
27 | ||
28 | #include "config.h" | |
29 | ||
30 | #include <gio/gio.h> | |
31 | ||
32 | #include "dfu-error.h" | |
33 | ||
34 | /** | |
35 | * dfu_error_quark: | |
36 | * | |
37 | * Return value: An error quark. | |
38 | * | |
39 | * Since: 0.5.4 | |
40 | **/ | |
41 | GQuark | |
42 | dfu_error_quark (void) | |
43 | { | |
44 | static GQuark quark = 0; | |
45 | if (!quark) | |
46 | quark = g_quark_from_static_string ("DfuError"); | |
47 | return quark; | |
48 | } |
0 | /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- | |
1 | * | |
2 | * Copyright (C) 2015 Richard Hughes <richard@hughsie.com> | |
3 | * | |
4 | * Licensed under the GNU Lesser General Public License Version 2.1 | |
5 | * | |
6 | * This library is free software; you can redistribute it and/or | |
7 | * modify it under the terms of the GNU Lesser General Public | |
8 | * License as published by the Free Software Foundation; either | |
9 | * version 2.1 of the License, or (at your option) any later version. | |
10 | * | |
11 | * This library is distributed in the hope that it will be useful, | |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
14 | * Lesser General Public License for more details. | |
15 | * | |
16 | * You should have received a copy of the GNU Lesser General Public | |
17 | * License along with this library; if not, write to the Free Software | |
18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | |
19 | */ | |
20 | ||
21 | #ifndef __DFU_ERROR_H | |
22 | #define __DFU_ERROR_H | |
23 | ||
24 | #include <glib.h> | |
25 | ||
26 | #define DFU_ERROR dfu_error_quark() | |
27 | ||
28 | /** | |
29 | * DfuError: | |
30 | * @DFU_ERROR_INTERNAL: Internal error | |
31 | * @DFU_ERROR_VERIFY_FAILED: Failed to verify write | |
32 | * @DFU_ERROR_INVALID_FILE: Invalid file format | |
33 | * @DFU_ERROR_INVALID_DEVICE: Invalid device type | |
34 | * @DFU_ERROR_NOT_FOUND: Resource not found | |
35 | * @DFU_ERROR_NOT_SUPPORTED: Action was not supported | |
36 | * @DFU_ERROR_PERMISSION_DENIED: Failed due to access permissions | |
37 | * | |
38 | * The error code. | |
39 | **/ | |
40 | typedef enum { | |
41 | DFU_ERROR_INTERNAL, /* Since: 0.5.4 */ | |
42 | DFU_ERROR_VERIFY_FAILED, /* Since: 0.5.4 */ | |
43 | DFU_ERROR_INVALID_FILE, /* Since: 0.5.4 */ | |
44 | DFU_ERROR_INVALID_DEVICE, /* Since: 0.5.4 */ | |
45 | DFU_ERROR_NOT_FOUND, /* Since: 0.5.4 */ | |
46 | DFU_ERROR_NOT_SUPPORTED, /* Since: 0.5.4 */ | |
47 | DFU_ERROR_PERMISSION_DENIED, /* Since: 0.5.4 */ | |
48 | /*< private >*/ | |
49 | DFU_ERROR_LAST | |
50 | } DfuError; | |
51 | ||
52 | GQuark dfu_error_quark (void); | |
53 | ||
54 | #endif /* __DFU_ERROR_H */ |
0 | /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- | |
1 | * | |
2 | * Copyright (C) 2015 Richard Hughes <richard@hughsie.com> | |
3 | * | |
4 | * Licensed under the GNU Lesser General Public License Version 2.1 | |
5 | * | |
6 | * This library is free software; you can redistribute it and/or | |
7 | * modify it under the terms of the GNU Lesser General Public | |
8 | * License as published by the Free Software Foundation; either | |
9 | * version 2.1 of the License, or (at your option) any later version. | |
10 | * | |
11 | * This library is distributed in the hope that it will be useful, | |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
14 | * Lesser General Public License for more details. | |
15 | * | |
16 | * You should have received a copy of the GNU Lesser General Public | |
17 | * License along with this library; if not, write to the Free Software | |
18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | |
19 | */ | |
20 | ||
21 | /** | |
22 | * SECTION:dfu-firmware | |
23 | * @short_description: Object representing a DFU or DfuSe firmware file | |
24 | * | |
25 | * This object allows reading and writing firmware files either in | |
26 | * raw, DFU or DfuSe formats. | |
27 | * | |
28 | * A #DfuFirmware can be made up of several #DfuImages, although | |
29 | * typically there is only one. | |
30 | * | |
31 | * See also: #DfuImage | |
32 | */ | |
33 | ||
34 | #include "config.h" | |
35 | ||
36 | #include <string.h> | |
37 | #include <stdio.h> | |
38 | ||
39 | #include "dfu-common.h" | |
40 | #include "dfu-error.h" | |
41 | #include "dfu-firmware.h" | |
42 | #include "dfu-image-private.h" | |
43 | ||
44 | static void dfu_firmware_finalize (GObject *object); | |
45 | ||
46 | /** | |
47 | * DfuFirmwarePrivate: | |
48 | * | |
49 | * Private #DfuFirmware data | |
50 | **/ | |
51 | typedef struct { | |
52 | GHashTable *metadata; | |
53 | GPtrArray *images; | |
54 | guint16 vid; | |
55 | guint16 pid; | |
56 | guint16 release; | |
57 | guint32 crc; | |
58 | DfuCipherKind cipher_kind; | |
59 | DfuFirmwareFormat format; | |
60 | } DfuFirmwarePrivate; | |
61 | ||
62 | G_DEFINE_TYPE_WITH_PRIVATE (DfuFirmware, dfu_firmware, G_TYPE_OBJECT) | |
63 | #define GET_PRIVATE(o) (dfu_firmware_get_instance_private (o)) | |
64 | ||
65 | /** | |
66 | * dfu_firmware_class_init: | |
67 | **/ | |
68 | static void | |
69 | dfu_firmware_class_init (DfuFirmwareClass *klass) | |
70 | { | |
71 | GObjectClass *object_class = G_OBJECT_CLASS (klass); | |
72 | object_class->finalize = dfu_firmware_finalize; | |
73 | } | |
74 | ||
75 | /** | |
76 | * dfu_firmware_init: | |
77 | **/ | |
78 | static void | |
79 | dfu_firmware_init (DfuFirmware *firmware) | |
80 | { | |
81 | DfuFirmwarePrivate *priv = GET_PRIVATE (firmware); | |
82 | priv->vid = 0xffff; | |
83 | priv->pid = 0xffff; | |
84 | priv->release = 0xffff; | |
85 | priv->images = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref); | |
86 | priv->metadata = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); | |
87 | } | |
88 | ||
89 | /** | |
90 | * dfu_firmware_finalize: | |
91 | **/ | |
92 | static void | |
93 | dfu_firmware_finalize (GObject *object) | |
94 | { | |
95 | DfuFirmware *firmware = DFU_FIRMWARE (object); | |
96 | DfuFirmwarePrivate *priv = GET_PRIVATE (firmware); | |
97 | ||
98 | g_ptr_array_unref (priv->images); | |
99 | g_hash_table_destroy (priv->metadata); | |
100 | ||
101 | G_OBJECT_CLASS (dfu_firmware_parent_class)->finalize (object); | |
102 | } | |
103 | ||
104 | /** | |
105 | * dfu_firmware_new: | |
106 | * | |
107 | * Creates a new DFU firmware object. | |
108 | * | |
109 | * Return value: a new #DfuFirmware | |
110 | * | |
111 | * Since: 0.5.4 | |
112 | **/ | |
113 | DfuFirmware * | |
114 | dfu_firmware_new (void) | |
115 | { | |
116 | DfuFirmware *firmware; | |
117 | firmware = g_object_new (DFU_TYPE_FIRMWARE, NULL); | |
118 | return firmware; | |
119 | } | |
120 | ||
121 | /** | |
122 | * dfu_firmware_get_image: | |
123 | * @firmware: a #DfuFirmware | |
124 | * @alt_setting: an alternative setting, typically 0x00 | |
125 | * | |
126 | * Gets an image from the firmware file. | |
127 | * | |
128 | * Return value: (transfer none): a #DfuImage, or %NULL for not found | |
129 | * | |
130 | * Since: 0.5.4 | |
131 | **/ | |
132 | DfuImage * | |
133 | dfu_firmware_get_image (DfuFirmware *firmware, guint8 alt_setting) | |
134 | { | |
135 | DfuFirmwarePrivate *priv = GET_PRIVATE (firmware); | |
136 | DfuImage *im; | |
137 | guint i; | |
138 | ||
139 | g_return_val_if_fail (DFU_IS_FIRMWARE (firmware), NULL); | |
140 | ||
141 | /* find correct image */ | |
142 | for (i = 0; i < priv->images->len; i++) { | |
143 | im = g_ptr_array_index (priv->images, i); | |
144 | if (dfu_image_get_alt_setting (im) == alt_setting) | |
145 | return im; | |
146 | } | |
147 | return NULL; | |
148 | } | |
149 | ||
150 | /** | |
151 | * dfu_firmware_get_image_by_name: | |
152 | * @firmware: a #DfuFirmware | |
153 | * @name: an alternative setting name | |
154 | * | |
155 | * Gets an image from the firmware file. | |
156 | * | |
157 | * Return value: (transfer none): a #DfuImage, or %NULL for not found | |
158 | * | |
159 | * Since: 0.5.4 | |
160 | **/ | |
161 | DfuImage * | |
162 | dfu_firmware_get_image_by_name (DfuFirmware *firmware, const gchar *name) | |
163 | { | |
164 | DfuFirmwarePrivate *priv = GET_PRIVATE (firmware); | |
165 | DfuImage *im; | |
166 | guint i; | |
167 | ||
168 | g_return_val_if_fail (DFU_IS_FIRMWARE (firmware), NULL); | |
169 | ||
170 | /* find correct image */ | |
171 | for (i = 0; i < priv->images->len; i++) { | |
172 | im = g_ptr_array_index (priv->images, i); | |
173 | if (g_strcmp0 (dfu_image_get_name (im), name) == 0) | |
174 | return im; | |
175 | } | |
176 | return NULL; | |
177 | } | |
178 | ||
179 | /** | |
180 | * dfu_firmware_get_image_default: | |
181 | * @firmware: a #DfuFirmware | |
182 | * | |
183 | * Gets the default image from the firmware file. | |
184 | * | |
185 | * Return value: (transfer none): a #DfuImage, or %NULL for not found | |
186 | * | |
187 | * Since: 0.5.4 | |
188 | **/ | |
189 | DfuImage * | |
190 | dfu_firmware_get_image_default (DfuFirmware *firmware) | |
191 | { | |
192 | DfuFirmwarePrivate *priv = GET_PRIVATE (firmware); | |
193 | g_return_val_if_fail (DFU_IS_FIRMWARE (firmware), NULL); | |
194 | if (priv->images->len == 0) | |
195 | return NULL; | |
196 | return g_ptr_array_index (priv->images, 0); | |
197 | } | |
198 | ||
199 | /** | |
200 | * dfu_firmware_get_images: | |
201 | * @firmware: a #DfuFirmware | |
202 | * | |
203 | * Gets all the images contained in this firmware file. | |
204 | * | |
205 | * Return value: (transfer none) (element-type DfuImage): list of images | |
206 | * | |
207 | * Since: 0.5.4 | |
208 | **/ | |
209 | GPtrArray * | |
210 | dfu_firmware_get_images (DfuFirmware *firmware) | |
211 | { | |
212 | DfuFirmwarePrivate *priv = GET_PRIVATE (firmware); | |
213 | g_return_val_if_fail (DFU_IS_FIRMWARE (firmware), NULL); | |
214 | return priv->images; | |
215 | } | |
216 | ||
217 | /** | |
218 | * dfu_firmware_get_size: | |
219 | * @firmware: a #DfuFirmware | |
220 | * | |
221 | * Gets the size of all the images in the firmware. | |
222 | * | |
223 | * This only returns actual data that would be sent to the device and | |
224 | * does not include any padding. | |
225 | * | |
226 | * Return value: a integer value, or 0 if there are no images. | |
227 | * | |
228 | * Since: 0.5.4 | |
229 | **/ | |
230 | guint32 | |
231 | dfu_firmware_get_size (DfuFirmware *firmware) | |
232 | { | |
233 | DfuFirmwarePrivate *priv = GET_PRIVATE (firmware); | |
234 | guint32 length = 0; | |
235 | guint i; | |
236 | g_return_val_if_fail (DFU_IS_FIRMWARE (firmware), 0); | |
237 | for (i = 0; i < priv->images->len; i++) { | |
238 | DfuImage *image = g_ptr_array_index (priv->images, i); | |
239 | length += dfu_image_get_size (image); | |
240 | } | |
241 | return length; | |
242 | } | |
243 | ||
244 | /** | |
245 | * dfu_firmware_add_image: | |
246 | * @firmware: a #DfuFirmware | |
247 | * @image: a #DfuImage | |
248 | * | |
249 | * Adds an image to the list of images. | |
250 | * | |
251 | * Since: 0.5.4 | |
252 | **/ | |
253 | void | |
254 | dfu_firmware_add_image (DfuFirmware *firmware, DfuImage *image) | |
255 | { | |
256 | DfuFirmwarePrivate *priv = GET_PRIVATE (firmware); | |
257 | g_return_if_fail (DFU_IS_FIRMWARE (firmware)); | |
258 | g_return_if_fail (DFU_IS_IMAGE (image)); | |
259 | g_ptr_array_add (priv->images, g_object_ref (image)); | |
260 | } | |
261 | ||
262 | /** | |
263 | * dfu_firmware_get_vid: | |
264 | * @firmware: a #DfuFirmware | |
265 | * | |
266 | * Gets the vendor ID. | |
267 | * | |
268 | * Return value: a vendor ID, or 0xffff for unset | |
269 | * | |
270 | * Since: 0.5.4 | |
271 | **/ | |
272 | guint16 | |
273 | dfu_firmware_get_vid (DfuFirmware *firmware) | |
274 | { | |
275 | DfuFirmwarePrivate *priv = GET_PRIVATE (firmware); | |
276 | g_return_val_if_fail (DFU_IS_FIRMWARE (firmware), 0xffff); | |
277 | return priv->vid; | |
278 | } | |
279 | ||
280 | /** | |
281 | * dfu_firmware_get_pid: | |
282 | * @firmware: a #DfuFirmware | |
283 | * | |
284 | * Gets the product ID. | |
285 | * | |
286 | * Return value: a product ID, or 0xffff for unset | |
287 | * | |
288 | * Since: 0.5.4 | |
289 | **/ | |
290 | guint16 | |
291 | dfu_firmware_get_pid (DfuFirmware *firmware) | |
292 | { | |
293 | DfuFirmwarePrivate *priv = GET_PRIVATE (firmware); | |
294 | g_return_val_if_fail (DFU_IS_FIRMWARE (firmware), 0xffff); | |
295 | return priv->pid; | |
296 | } | |
297 | ||
298 | /** | |
299 | * dfu_firmware_get_release: | |
300 | * @firmware: a #DfuFirmware | |
301 | * | |
302 | * Gets the device ID. | |
303 | * | |
304 | * Return value: a device ID, or 0xffff for unset | |
305 | * | |
306 | * Since: 0.5.4 | |
307 | **/ | |
308 | guint16 | |
309 | dfu_firmware_get_release (DfuFirmware *firmware) | |
310 | { | |
311 | DfuFirmwarePrivate *priv = GET_PRIVATE (firmware); | |
312 | g_return_val_if_fail (DFU_IS_FIRMWARE (firmware), 0xffff); | |
313 | return priv->release; | |
314 | } | |
315 | ||
316 | /** | |
317 | * dfu_firmware_get_format: | |
318 | * @firmware: a #DfuFirmware | |
319 | * | |
320 | * Gets the DFU version. | |
321 | * | |
322 | * Return value: a version, or 0x0 for unset | |
323 | * | |
324 | * Since: 0.5.4 | |
325 | **/ | |
326 | guint16 | |
327 | dfu_firmware_get_format (DfuFirmware *firmware) | |
328 | { | |
329 | DfuFirmwarePrivate *priv = GET_PRIVATE (firmware); | |
330 | g_return_val_if_fail (DFU_IS_FIRMWARE (firmware), 0xffff); | |
331 | return priv->format; | |
332 | } | |
333 | ||
334 | /** | |
335 | * dfu_firmware_set_vid: | |
336 | * @firmware: a #DfuFirmware | |
337 | * @vid: vendor ID, or 0xffff for unset | |
338 | * | |
339 | * Sets the vendor ID. | |
340 | * | |
341 | * Since: 0.5.4 | |
342 | **/ | |
343 | void | |
344 | dfu_firmware_set_vid (DfuFirmware *firmware, guint16 vid) | |
345 | { | |
346 | DfuFirmwarePrivate *priv = GET_PRIVATE (firmware); | |
347 | g_return_if_fail (DFU_IS_FIRMWARE (firmware)); | |
348 | priv->vid = vid; | |
349 | } | |
350 | ||
351 | /** | |
352 | * dfu_firmware_set_pid: | |
353 | * @firmware: a #DfuFirmware | |
354 | * @pid: product ID, or 0xffff for unset | |
355 | * | |
356 | * Sets the product ID. | |
357 | * | |
358 | * Since: 0.5.4 | |
359 | **/ | |
360 | void | |
361 | dfu_firmware_set_pid (DfuFirmware *firmware, guint16 pid) | |
362 | { | |
363 | DfuFirmwarePrivate *priv = GET_PRIVATE (firmware); | |
364 | g_return_if_fail (DFU_IS_FIRMWARE (firmware)); | |
365 | priv->pid = pid; | |
366 | } | |
367 | ||
368 | /** | |
369 | * dfu_firmware_set_release: | |
370 | * @firmware: a #DfuFirmware | |
371 | * @release: device ID, or 0xffff for unset | |
372 | * | |
373 | * Sets the device ID. | |
374 | * | |
375 | * Since: 0.5.4 | |
376 | **/ | |
377 | void | |
378 | dfu_firmware_set_release (DfuFirmware *firmware, guint16 release) | |
379 | { | |
380 | DfuFirmwarePrivate *priv = GET_PRIVATE (firmware); | |
381 | g_return_if_fail (DFU_IS_FIRMWARE (firmware)); | |
382 | priv->release = release; | |
383 | } | |
384 | ||
385 | /** | |
386 | * dfu_firmware_set_format: | |
387 | * @firmware: a #DfuFirmware | |
388 | * @format: a #DfuFirmwareFormat, e.g. %DFU_FIRMWARE_FORMAT_DFUSE | |
389 | * | |
390 | * Sets the DFU version in BCD format. | |
391 | * | |
392 | * Since: 0.5.4 | |
393 | **/ | |
394 | void | |
395 | dfu_firmware_set_format (DfuFirmware *firmware, DfuFirmwareFormat format) | |
396 | { | |
397 | DfuFirmwarePrivate *priv = GET_PRIVATE (firmware); | |
398 | g_return_if_fail (DFU_IS_FIRMWARE (firmware)); | |
399 | priv->format = format; | |
400 | } | |
401 | ||
402 | typedef struct __attribute__((packed)) { | |
403 | guint16 release; | |
404 | guint16 pid; | |
405 | guint16 vid; | |
406 | guint16 ver; | |
407 | guint8 sig[3]; | |
408 | guint8 len; | |
409 | guint32 crc; | |
410 | } DfuFirmwareFooter; | |
411 | ||
412 | static guint32 _crctbl[] = { | |
413 | 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, | |
414 | 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, | |
415 | 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2, | |
416 | 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, | |
417 | 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, | |
418 | 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, | |
419 | 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c, | |
420 | 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, | |
421 | 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, | |
422 | 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, | |
423 | 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106, | |
424 | 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, | |
425 | 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, | |
426 | 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, | |
427 | 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, | |
428 | 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, | |
429 | 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, | |
430 | 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, | |
431 | 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, | |
432 | 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, | |
433 | 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, | |
434 | 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, | |
435 | 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84, | |
436 | 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, | |
437 | 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, | |
438 | 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, | |
439 | 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e, | |
440 | 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, | |
441 | 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, | |
442 | 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, | |
443 | 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28, | |
444 | 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, | |
445 | 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, | |
446 | 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, | |
447 | 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, | |
448 | 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, | |
449 | 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, | |
450 | 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, | |
451 | 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, | |
452 | 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, | |
453 | 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, | |
454 | 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, | |
455 | 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d }; | |
456 | ||
457 | /** | |
458 | * dfu_firmware_generate_crc32: | |
459 | **/ | |
460 | static guint32 | |
461 | dfu_firmware_generate_crc32 (const guint8 *data, gsize length) | |
462 | { | |
463 | guint i; | |
464 | guint32 accum = 0xffffffff; | |
465 | for (i = 0; i < length; i++) | |
466 | accum = _crctbl[(accum^data[i]) & 0xff] ^ (accum >> 8); | |
467 | return accum; | |
468 | } | |
469 | ||
470 | /** | |
471 | * dfu_firmware_ihex_parse_uint8: | |
472 | **/ | |
473 | static guint8 | |
474 | dfu_firmware_ihex_parse_uint8 (const gchar *data, guint pos) | |
475 | { | |
476 | gchar buffer[3]; | |
477 | memcpy (buffer, data + pos, 2); | |
478 | buffer[2] = '\0'; | |
479 | return g_ascii_strtoull (buffer, NULL, 16); | |
480 | } | |
481 | ||
482 | /** | |
483 | * dfu_firmware_ihex_parse_uint16: | |
484 | **/ | |
485 | static guint16 | |
486 | dfu_firmware_ihex_parse_uint16 (const gchar *data, guint pos) | |
487 | { | |
488 | gchar buffer[5]; | |
489 | memcpy (buffer, data + pos, 4); | |
490 | buffer[4] = '\0'; | |
491 | return g_ascii_strtoull (buffer, NULL, 16); | |
492 | } | |
493 | ||
494 | #define DFU_INHX32_RECORD_TYPE_DATA 0 | |
495 | #define DFU_INHX32_RECORD_TYPE_EOF 1 | |
496 | #define DFU_INHX32_RECORD_TYPE_EXTENDED 4 | |
497 | ||
498 | /** | |
499 | * dfu_firmware_add_ihex: | |
500 | **/ | |
501 | static gboolean | |
502 | dfu_firmware_add_ihex (DfuFirmware *firmware, GBytes *bytes, | |
503 | DfuFirmwareParseFlags flags, GError **error) | |
504 | { | |
505 | const gchar *in_buffer; | |
506 | gsize len_in; | |
507 | guint16 addr_high = 0; | |
508 | guint16 addr_low = 0; | |
509 | guint32 addr32 = 0; | |
510 | guint32 addr32_last = 0; | |
511 | guint8 checksum; | |
512 | guint8 data_tmp; | |
513 | guint8 len_tmp; | |
514 | guint8 type; | |
515 | guint end; | |
516 | guint i; | |
517 | guint j; | |
518 | guint offset = 0; | |
519 | g_autoptr(DfuElement) element = NULL; | |
520 | g_autoptr(DfuImage) image = NULL; | |
521 | g_autoptr(GBytes) contents = NULL; | |
522 | g_autoptr(GString) string = NULL; | |
523 | ||
524 | g_return_val_if_fail (bytes != NULL, FALSE); | |
525 | ||
526 | /* create element */ | |
527 | image = dfu_image_new (); | |
528 | dfu_image_set_name (image, "ihex"); | |
529 | element = dfu_element_new (); | |
530 | ||
531 | /* parse records */ | |
532 | in_buffer = g_bytes_get_data (bytes, &len_in); | |
533 | string = g_string_new (""); | |
534 | while (offset < len_in) { | |
535 | ||
536 | /* check starting token */ | |
537 | if (in_buffer[offset] != ':') { | |
538 | g_set_error (error, | |
539 | DFU_ERROR, | |
540 | DFU_ERROR_INVALID_FILE, | |
541 | "invalid starting token, got %c at %x", | |
542 | in_buffer[offset], offset); | |
543 | return FALSE; | |
544 | } | |
545 | ||
546 | /* check there's enough data for the smallest possible record */ | |
547 | if (offset + 12 > (guint) len_in) { | |
548 | g_set_error (error, | |
549 | DFU_ERROR, | |
550 | DFU_ERROR_INVALID_FILE, | |
551 | "record incomplete at %i, length %i", | |
552 | offset, (guint) len_in); | |
553 | return FALSE; | |
554 | } | |
555 | ||
556 | /* length, 16-bit address, type */ | |
557 | len_tmp = dfu_firmware_ihex_parse_uint8 (in_buffer, offset+1); | |
558 | addr_low = dfu_firmware_ihex_parse_uint16 (in_buffer, offset+3); | |
559 | type = dfu_firmware_ihex_parse_uint8 (in_buffer, offset+7); | |
560 | ||
561 | /* position of checksum */ | |
562 | end = offset + 9 + len_tmp * 2; | |
563 | if (end > (guint) len_in) { | |
564 | g_set_error (error, | |
565 | DFU_ERROR, | |
566 | DFU_ERROR_INVALID_FILE, | |
567 | "checksum > file length: %u", | |
568 | end); | |
569 | return FALSE; | |
570 | } | |
571 | ||
572 | /* verify checksum */ | |
573 | if ((flags & DFU_FIRMWARE_PARSE_FLAG_NO_CRC_TEST) == 0) { | |
574 | checksum = 0; | |
575 | for (i = offset + 1; i < end + 2; i += 2) { | |
576 | data_tmp = dfu_firmware_ihex_parse_uint8 (in_buffer, i); | |
577 | checksum += data_tmp; | |
578 | } | |
579 | if (checksum != 0) { | |
580 | g_set_error_literal (error, | |
581 | DFU_ERROR, | |
582 | DFU_ERROR_INVALID_FILE, | |
583 | "invalid record checksum"); | |
584 | return FALSE; | |
585 | } | |
586 | } | |
587 | ||
588 | /* process different record types */ | |
589 | switch (type) { | |
590 | case DFU_INHX32_RECORD_TYPE_DATA: | |
591 | /* if not contiguous with previous record */ | |
592 | if ((addr_high + addr_low) != addr32) { | |
593 | if (addr32 == 0x0) { | |
594 | g_debug ("base address %04x", addr_low); | |
595 | dfu_element_set_address (element, addr_low); | |
596 | } | |
597 | addr32 = addr_high + addr_low; | |
598 | } | |
599 | ||
600 | /* parse bytes from line */ | |
601 | for (i = offset + 9; i < end; i += 2) { | |
602 | /* any holes in the hex record */ | |
603 | len_tmp = addr32 - addr32_last; | |
604 | if (addr32_last > 0x0 && len_tmp > 1) { | |
605 | for (j = 1; j < len_tmp; j++) { | |
606 | g_debug ("filling address 0x%04x", | |
607 | addr32_last + j); | |
608 | /* although 0xff might be clearer, | |
609 | * we can't write 0xffff to pic14 */ | |
610 | g_string_append_c (string, 0x00); | |
611 | } | |
612 | } | |
613 | /* write into buf */ | |
614 | data_tmp = dfu_firmware_ihex_parse_uint8 (in_buffer, i); | |
615 | g_string_append_c (string, data_tmp); | |
616 | g_debug ("writing address 0x%04x", addr32); | |
617 | addr32_last = addr32++; | |
618 | } | |
619 | break; | |
620 | case DFU_INHX32_RECORD_TYPE_EOF: | |
621 | break; | |
622 | case DFU_INHX32_RECORD_TYPE_EXTENDED: | |
623 | addr_high = dfu_firmware_ihex_parse_uint16 (in_buffer, offset+9); | |
624 | g_error ("set base address %x", addr_high); | |
625 | addr_high <<= 16; | |
626 | addr32 = addr_high + addr_low; | |
627 | break; | |
628 | default: | |
629 | g_set_error (error, | |
630 | DFU_ERROR, | |
631 | DFU_ERROR_INVALID_FILE, | |
632 | "invalid ihex record type %i", | |
633 | type); | |
634 | return FALSE; | |
635 | } | |
636 | ||
637 | /* ignore any line return */ | |
638 | offset = end + 2; | |
639 | for (; offset < len_in; offset++) { | |
640 | if (in_buffer[offset] != '\n' && | |
641 | in_buffer[offset] != '\r') | |
642 | break; | |
643 | } | |
644 | } | |
645 | ||
646 | /* add single image */ | |
647 | contents = g_bytes_new (string->str, string->len); | |
648 | dfu_element_set_contents (element, contents); | |
649 | dfu_image_add_element (image, element); | |
650 | dfu_firmware_add_image (firmware, image); | |
651 | dfu_firmware_set_format (firmware, DFU_FIRMWARE_FORMAT_INTEL_HEX); | |
652 | return TRUE; | |
653 | } | |
654 | ||
655 | /** | |
656 | * dfu_firmware_write_data_ihex_element: | |
657 | **/ | |
658 | static gboolean | |
659 | dfu_firmware_write_data_ihex_element (DfuElement *element, | |
660 | GString *str, | |
661 | GError **error) | |
662 | { | |
663 | GBytes *contents; | |
664 | const guint8 *data; | |
665 | const guint chunk_size = 16; | |
666 | gsize len; | |
667 | guint chunk_len; | |
668 | guint i; | |
669 | guint j; | |
670 | ||
671 | /* get number of chunks */ | |
672 | contents = dfu_element_get_contents (element); | |
673 | data = g_bytes_get_data (contents, &len); | |
674 | for (i = 0; i < len; i += chunk_size) { | |
675 | guint8 checksum = 0; | |
676 | ||
677 | /* length, 16-bit address, type */ | |
678 | chunk_len = MIN (len - i, 16); | |
679 | g_string_append_printf (str, ":%02X%04X%02X", | |
680 | chunk_len, | |
681 | dfu_element_get_address (element) + i, | |
682 | DFU_INHX32_RECORD_TYPE_DATA); | |
683 | for (j = 0; j < chunk_len; j++) | |
684 | g_string_append_printf (str, "%02X", data[i+j]); | |
685 | ||
686 | /* add checksum */ | |
687 | for (j = 0; j < (chunk_len * 2) + 8; j++) | |
688 | checksum += str->str[str->len - (j + 1)]; | |
689 | g_string_append_printf (str, "%02X\n", checksum); | |
690 | } | |
691 | return TRUE; | |
692 | } | |
693 | ||
694 | /** | |
695 | * dfu_firmware_write_data_ihex: | |
696 | **/ | |
697 | static GBytes * | |
698 | dfu_firmware_write_data_ihex (DfuFirmware *firmware, GError **error) | |
699 | { | |
700 | DfuFirmwarePrivate *priv = GET_PRIVATE (firmware); | |
701 | DfuElement *element; | |
702 | DfuImage *image; | |
703 | GPtrArray *elements; | |
704 | guint i; | |
705 | guint j; | |
706 | g_autoptr(GString) str = NULL; | |
707 | ||
708 | /* write all the element data */ | |
709 | str = g_string_new (""); | |
710 | for (i = 0; i < priv->images->len; i++) { | |
711 | image = g_ptr_array_index (priv->images, i); | |
712 | elements = dfu_image_get_elements (image); | |
713 | for (j = 0; j < elements->len; j++) { | |
714 | element = g_ptr_array_index (elements, j); | |
715 | if (!dfu_firmware_write_data_ihex_element (element, | |
716 | str, | |
717 | error)) | |
718 | return FALSE; | |
719 | } | |
720 | } | |
721 | ||
722 | /* add EOF */ | |
723 | g_string_append_printf (str, ":000000%02XFF\n", DFU_INHX32_RECORD_TYPE_EOF); | |
724 | return g_bytes_new (str->str, str->len); | |
725 | } | |
726 | ||
727 | /** | |
728 | * dfu_firmware_add_binary: | |
729 | **/ | |
730 | static gboolean | |
731 | dfu_firmware_add_binary (DfuFirmware *firmware, GBytes *bytes, GError **error) | |
732 | { | |
733 | g_autoptr(DfuElement) element = NULL; | |
734 | g_autoptr(DfuImage) image = NULL; | |
735 | image = dfu_image_new (); | |
736 | element = dfu_element_new (); | |
737 | dfu_element_set_contents (element, bytes); | |
738 | dfu_image_add_element (image, element); | |
739 | dfu_firmware_add_image (firmware, image); | |
740 | return TRUE; | |
741 | } | |
742 | ||
743 | /* DfuSe header */ | |
744 | typedef struct __attribute__((packed)) { | |
745 | guint8 sig[5]; | |
746 | guint8 ver; | |
747 | guint32 image_size; | |
748 | guint8 targets; | |
749 | } DfuSePrefix; | |
750 | ||
751 | /** | |
752 | * dfu_firmware_add_dfuse: | |
753 | **/ | |
754 | static gboolean | |
755 | dfu_firmware_add_dfuse (DfuFirmware *firmware, GBytes *bytes, GError **error) | |
756 | { | |
757 | DfuSePrefix *prefix; | |
758 | gsize len; | |
759 | guint32 offset = sizeof(DfuSePrefix); | |
760 | guint8 *data; | |
761 | guint i; | |
762 | ||
763 | /* check the prefix (BE) */ | |
764 | data = (guint8 *) g_bytes_get_data (bytes, &len); | |
765 | prefix = (DfuSePrefix *) data; | |
766 | if (memcmp (prefix->sig, "DfuSe", 5) != 0) { | |
767 | g_set_error_literal (error, | |
768 | DFU_ERROR, | |
769 | DFU_ERROR_INTERNAL, | |
770 | "invalid DfuSe prefix"); | |
771 | return FALSE; | |
772 | } | |
773 | ||
774 | /* check the version */ | |
775 | if (prefix->ver != 0x01) { | |
776 | g_set_error (error, | |
777 | DFU_ERROR, | |
778 | DFU_ERROR_INTERNAL, | |
779 | "invalid DfuSe version, got %02x", | |
780 | prefix->ver); | |
781 | return FALSE; | |
782 | } | |
783 | ||
784 | /* check image size */ | |
785 | if (GUINT32_FROM_LE (prefix->image_size) != len) { | |
786 | g_set_error (error, | |
787 | DFU_ERROR, | |
788 | DFU_ERROR_INTERNAL, | |
789 | "invalid DfuSe image size, " | |
790 | "got %" G_GUINT32_FORMAT ", " | |
791 | "expected %" G_GSIZE_FORMAT, | |
792 | GUINT32_FROM_LE (prefix->image_size), | |
793 | len); | |
794 | return FALSE; | |
795 | } | |
796 | ||
797 | /* parse the image targets */ | |
798 | len -= sizeof(DfuSePrefix); | |
799 | for (i = 0; i < prefix->targets; i++) { | |
800 | guint consumed; | |
801 | g_autoptr(DfuImage) image = NULL; | |
802 | image = dfu_image_from_dfuse (data + offset, len, | |
803 | &consumed, error); | |
804 | if (image == NULL) | |
805 | return FALSE; | |
806 | dfu_firmware_add_image (firmware, image); | |
807 | offset += consumed; | |
808 | len -= consumed; | |
809 | } | |
810 | return TRUE; | |
811 | } | |
812 | ||
813 | /** | |
814 | * dfu_firmware_write_data_dfuse: | |
815 | **/ | |
816 | static GBytes * | |
817 | dfu_firmware_write_data_dfuse (DfuFirmware *firmware, GError **error) | |
818 | { | |
819 | DfuFirmwarePrivate *priv = GET_PRIVATE (firmware); | |
820 | DfuSePrefix *prefix; | |
821 | guint i; | |
822 | guint32 image_size_total = 0; | |
823 | guint32 offset = sizeof (DfuSePrefix); | |
824 | guint8 *buf; | |
825 | g_autoptr(GPtrArray) dfuse_images = NULL; | |
826 | ||
827 | /* get all the image data */ | |
828 | dfuse_images = g_ptr_array_new_with_free_func ((GDestroyNotify) g_bytes_unref); | |
829 | for (i = 0; i < priv->images->len; i++) { | |
830 | DfuImage *im = g_ptr_array_index (priv->images, i); | |
831 | GBytes *contents; | |
832 | contents = dfu_image_to_dfuse (im); | |
833 | image_size_total += g_bytes_get_size (contents); | |
834 | g_ptr_array_add (dfuse_images, contents); | |
835 | } | |
836 | g_debug ("image_size_total: %i", image_size_total); | |
837 | ||
838 | buf = g_malloc0 (sizeof (DfuSePrefix) + image_size_total); | |
839 | ||
840 | /* DfuSe header */ | |
841 | prefix = (DfuSePrefix *) buf; | |
842 | memcpy (prefix->sig, "DfuSe", 5); | |
843 | prefix->ver = 0x01; | |
844 | prefix->image_size = offset + image_size_total; | |
845 | prefix->targets = priv->images->len; | |
846 | ||
847 | /* copy images */ | |
848 | for (i = 0; i < dfuse_images->len; i++) { | |
849 | GBytes *contents = g_ptr_array_index (dfuse_images, i); | |
850 | gsize length; | |
851 | const guint8 *data; | |
852 | data = g_bytes_get_data (contents, &length); | |
853 | memcpy (buf + offset, data, length); | |
854 | offset += length; | |
855 | } | |
856 | ||
857 | /* return blob */ | |
858 | return g_bytes_new_take (buf, sizeof (DfuSePrefix) + image_size_total); | |
859 | } | |
860 | ||
861 | /** | |
862 | * dfu_firmware_parse_metadata: | |
863 | * | |
864 | * The representation in memory is as follows: | |
865 | * | |
866 | * uint16 signature='MD' | |
867 | * uint8 number_of_keys | |
868 | * uint8 number_of_keys | |
869 | * uint8 key(n)_length | |
870 | * ... key(n) (no NUL) | |
871 | * uint8 value(n)_length | |
872 | * ... value(n) (no NUL) | |
873 | * <existing DFU footer> | |
874 | **/ | |
875 | static gboolean | |
876 | dfu_firmware_parse_metadata (DfuFirmware *firmware, | |
877 | const guint8 *data, | |
878 | guint data_length, | |
879 | guint32 footer_size, | |
880 | GError **error) | |
881 | { | |
882 | guint i; | |
883 | guint idx = data_length - footer_size + 2; | |
884 | guint kvlen; | |
885 | guint number_keys; | |
886 | ||
887 | /* not big enough */ | |
888 | if (footer_size <= 0x10) | |
889 | return TRUE; | |
890 | ||
891 | /* signature invalid */ | |
892 | if (memcmp (&data[data_length - footer_size], "MD", 2) != 0) | |
893 | return TRUE; | |
894 | ||
895 | /* parse key=value store */ | |
896 | number_keys = data[idx++]; | |
897 | for (i = 0; i < number_keys; i++) { | |
898 | g_autofree gchar *key = NULL; | |
899 | g_autofree gchar *value = NULL; | |
900 | ||
901 | /* parse key */ | |
902 | kvlen = data[idx++]; | |
903 | if (kvlen > 233) { | |
904 | g_set_error (error, | |
905 | DFU_ERROR, | |
906 | DFU_ERROR_INTERNAL, | |
907 | "metadata table corrupt, key=%i", | |
908 | kvlen); | |
909 | return FALSE; | |
910 | } | |
911 | if (idx + kvlen + 0x10 > data_length) { | |
912 | g_set_error_literal (error, | |
913 | DFU_ERROR, | |
914 | DFU_ERROR_INTERNAL, | |
915 | "metadata table corrupt"); | |
916 | return FALSE; | |
917 | } | |
918 | key = g_strndup ((const gchar *) data + idx, kvlen); | |
919 | idx += kvlen; | |
920 | ||
921 | /* parse value */ | |
922 | kvlen = data[idx++]; | |
923 | if (kvlen > 233) { | |
924 | g_set_error (error, | |
925 | DFU_ERROR, | |
926 | DFU_ERROR_INTERNAL, | |
927 | "metadata table corrupt, value=%i", | |
928 | kvlen); | |
929 | return FALSE; | |
930 | } | |
931 | if (idx + kvlen + 0x10 > data_length) { | |
932 | g_set_error_literal (error, | |
933 | DFU_ERROR, | |
934 | DFU_ERROR_INTERNAL, | |
935 | "metadata table corrupt"); | |
936 | return FALSE; | |
937 | } | |
938 | value = g_strndup ((const gchar *) data + idx, kvlen); | |
939 | idx += kvlen; | |
940 | dfu_firmware_set_metadata (firmware, key, value); | |
941 | } | |
942 | return TRUE; | |
943 | } | |
944 | ||
945 | /** | |
946 | * dfu_firmware_parse_data: | |
947 | * @firmware: a #DfuFirmware | |
948 | * @bytes: raw firmware data | |
949 | * @flags: optional flags, e.g. %DFU_FIRMWARE_PARSE_FLAG_NO_CRC_TEST | |
950 | * @error: a #GError, or %NULL | |
951 | * | |
952 | * Parses firmware data which may have an optional DFU suffix. | |
953 | * | |
954 | * Return value: %TRUE for success | |
955 | * | |
956 | * Since: 0.5.4 | |
957 | **/ | |
958 | gboolean | |
959 | dfu_firmware_parse_data (DfuFirmware *firmware, GBytes *bytes, | |
960 | DfuFirmwareParseFlags flags, GError **error) | |
961 | { | |
962 | DfuFirmwareFooter *ftr; | |
963 | DfuFirmwarePrivate *priv = GET_PRIVATE (firmware); | |
964 | const gchar *cipher_str; | |
965 | gsize len; | |
966 | guint32 crc_new; | |
967 | guint32 size; | |
968 | guint8 *data; | |
969 | g_autoptr(GBytes) contents = NULL; | |
970 | ||
971 | g_return_val_if_fail (DFU_IS_FIRMWARE (firmware), FALSE); | |
972 | g_return_val_if_fail (bytes != NULL, FALSE); | |
973 | g_return_val_if_fail (error == NULL || *error == NULL, FALSE); | |
974 | ||
975 | /* sanity check */ | |
976 | g_assert_cmpint(sizeof(DfuSePrefix), ==, 11); | |
977 | ||
978 | /* set defaults */ | |
979 | priv->vid = 0xffff; | |
980 | priv->pid = 0xffff; | |
981 | priv->release = 0xffff; | |
982 | ||
983 | /* this is ihex */ | |
984 | data = (guint8 *) g_bytes_get_data (bytes, &len); | |
985 | if (data[0] == ':') | |
986 | return dfu_firmware_add_ihex (firmware, bytes, flags, error); | |
987 | ||
988 | /* too small to be a DFU file */ | |
989 | if (len < 16) { | |
990 | priv->format = DFU_FIRMWARE_FORMAT_RAW; | |
991 | return dfu_firmware_add_binary (firmware, bytes, error); | |
992 | } | |
993 | ||
994 | /* check for DFU signature */ | |
995 | ftr = (DfuFirmwareFooter *) &data[len-sizeof(DfuFirmwareFooter)]; | |
996 | if (memcmp (ftr->sig, "UFD", 3) != 0) { | |
997 | priv->format = DFU_FIRMWARE_FORMAT_RAW; | |
998 | return dfu_firmware_add_binary (firmware, bytes, error); | |
999 | } | |
1000 | ||
1001 | /* check version */ | |
1002 | priv->format = GUINT16_FROM_LE (ftr->ver); | |
1003 | if ((flags & DFU_FIRMWARE_PARSE_FLAG_NO_VERSION_TEST) == 0) { | |
1004 | if (priv->format != DFU_FIRMWARE_FORMAT_DFU_1_0 && | |
1005 | priv->format != DFU_FIRMWARE_FORMAT_DFUSE) { | |
1006 | g_set_error (error, | |
1007 | DFU_ERROR, | |
1008 | DFU_ERROR_INTERNAL, | |
1009 | "version check failed, got %04x", | |
1010 | priv->format); | |
1011 | return FALSE; | |
1012 | } | |
1013 | } | |
1014 | ||
1015 | /* verify the checksum */ | |
1016 | priv->crc = GUINT32_FROM_LE (ftr->crc); | |
1017 | if ((flags & DFU_FIRMWARE_PARSE_FLAG_NO_CRC_TEST) == 0) { | |
1018 | crc_new = dfu_firmware_generate_crc32 (data, len - 4); | |
1019 | if (priv->crc != crc_new) { | |
1020 | g_set_error (error, | |
1021 | DFU_ERROR, | |
1022 | DFU_ERROR_INTERNAL, | |
1023 | "CRC failed, expected %04x, got %04x", | |
1024 | crc_new, GUINT32_FROM_LE (ftr->crc)); | |
1025 | return FALSE; | |
1026 | } | |
1027 | } | |
1028 | ||
1029 | /* set from footer */ | |
1030 | dfu_firmware_set_vid (firmware, GUINT16_FROM_LE (ftr->vid)); | |
1031 | dfu_firmware_set_pid (firmware, GUINT16_FROM_LE (ftr->pid)); | |
1032 | dfu_firmware_set_release (firmware, GUINT16_FROM_LE (ftr->release)); | |
1033 | ||
1034 | /* check reported length */ | |
1035 | size = GUINT16_FROM_LE (ftr->len); | |
1036 | if (size > len) { | |
1037 | g_set_error (error, | |
1038 | DFU_ERROR, | |
1039 | DFU_ERROR_INTERNAL, | |
1040 | "reported firmware size %04x larger than file %04x", | |
1041 | (guint) size, (guint) len); | |
1042 | return FALSE; | |
1043 | } | |
1044 | ||
1045 | /* parse the optional metadata segment */ | |
1046 | if ((flags & DFU_FIRMWARE_PARSE_FLAG_NO_METADATA) == 0) { | |
1047 | if (!dfu_firmware_parse_metadata (firmware, data, len, size, error)) | |
1048 | return FALSE; | |
1049 | } | |
1050 | ||
1051 | /* set this automatically */ | |
1052 | cipher_str = dfu_firmware_get_metadata (firmware, DFU_METADATA_KEY_CIPHER_KIND); | |
1053 | if (cipher_str != NULL) { | |
1054 | if (g_strcmp0 (cipher_str, "XTEA") == 0) | |
1055 | priv->cipher_kind = DFU_CIPHER_KIND_XTEA; | |
1056 | else | |
1057 | g_warning ("Unknown CipherKind: %s", cipher_str); | |
1058 | } | |
1059 | ||
1060 | /* parse DfuSe prefix */ | |
1061 | contents = g_bytes_new_from_bytes (bytes, 0, len - size); | |
1062 | if (priv->format == DFU_FIRMWARE_FORMAT_DFUSE) | |
1063 | return dfu_firmware_add_dfuse (firmware, contents, error); | |
1064 | ||
1065 | /* just copy old-plain DFU file */ | |
1066 | return dfu_firmware_add_binary (firmware, contents, error); | |
1067 | } | |
1068 | ||
1069 | /** | |
1070 | * dfu_firmware_parse_file: | |
1071 | * @firmware: a #DfuFirmware | |
1072 | * @file: a #GFile to load and parse | |
1073 | * @flags: optional flags, e.g. %DFU_FIRMWARE_PARSE_FLAG_NO_CRC_TEST | |
1074 | * @cancellable: a #GCancellable, or %NULL | |
1075 | * @error: a #GError, or %NULL | |
1076 | * | |
1077 | * Parses a DFU firmware, which may contain an optional footer. | |
1078 | * | |
1079 | * Return value: %TRUE for success | |
1080 | * | |
1081 | * Since: 0.5.4 | |
1082 | **/ | |
1083 | gboolean | |
1084 | dfu_firmware_parse_file (DfuFirmware *firmware, GFile *file, | |
1085 | DfuFirmwareParseFlags flags, | |
1086 | GCancellable *cancellable, GError **error) | |
1087 | { | |
1088 | DfuFirmwarePrivate *priv = GET_PRIVATE (firmware); | |
1089 | gchar *contents = NULL; | |
1090 | gsize length = 0; | |
1091 | g_autofree gchar *basename = NULL; | |
1092 | g_autoptr(GBytes) bytes = NULL; | |
1093 | ||
1094 | g_return_val_if_fail (DFU_IS_FIRMWARE (firmware), FALSE); | |
1095 | g_return_val_if_fail (G_IS_FILE (file), FALSE); | |
1096 | g_return_val_if_fail (error == NULL || *error == NULL, FALSE); | |
1097 | ||
1098 | /* guess cipher kind based on file extension */ | |
1099 | basename = g_file_get_basename (file); | |
1100 | if (g_str_has_suffix (basename, ".xdfu")) | |
1101 | priv->cipher_kind = DFU_CIPHER_KIND_XTEA; | |
1102 | ||
1103 | if (!g_file_load_contents (file, cancellable, &contents, | |
1104 | &length, NULL, error)) | |
1105 | return FALSE; | |
1106 | bytes = g_bytes_new_take (contents, length); | |
1107 | return dfu_firmware_parse_data (firmware, bytes, flags, error); | |
1108 | } | |
1109 | ||
1110 | /** | |
1111 | * dfu_firmware_get_metadata: | |
1112 | * @firmware: a #DfuFirmware | |
1113 | * @key: metadata string key | |
1114 | * | |
1115 | * Gets metadata from the store with a specific key. | |
1116 | * | |
1117 | * Return value: the metadata value, or %NULL for unset | |
1118 | * | |
1119 | * Since: 0.5.4 | |
1120 | **/ | |
1121 | const gchar * | |
1122 | dfu_firmware_get_metadata (DfuFirmware *firmware, const gchar *key) | |
1123 | { | |
1124 | DfuFirmwarePrivate *priv = GET_PRIVATE (firmware); | |
1125 | return g_hash_table_lookup (priv->metadata, key); | |
1126 | } | |
1127 | ||
1128 | /** | |
1129 | * dfu_firmware_set_metadata: | |
1130 | * @firmware: a #DfuFirmware | |
1131 | * @key: metadata string key | |
1132 | * @value: metadata string value | |
1133 | * | |
1134 | * Sets a metadata value with a specific key. | |
1135 | * | |
1136 | * Since: 0.5.4 | |
1137 | **/ | |
1138 | void | |
1139 | dfu_firmware_set_metadata (DfuFirmware *firmware, const gchar *key, const gchar *value) | |
1140 | { | |
1141 | DfuFirmwarePrivate *priv = GET_PRIVATE (firmware); | |
1142 | g_debug ("adding metadata %s=%s", key, value); | |
1143 | g_hash_table_insert (priv->metadata, g_strdup (key), g_strdup (value)); | |
1144 | } | |
1145 | ||
1146 | /** | |
1147 | * dfu_firmware_remove_metadata: | |
1148 | * @firmware: a #DfuFirmware | |
1149 | * @key: metadata string key | |
1150 | * | |
1151 | * Removes a metadata item from the store | |
1152 | * | |
1153 | * Since: 0.5.4 | |
1154 | **/ | |
1155 | void | |
1156 | dfu_firmware_remove_metadata (DfuFirmware *firmware, const gchar *key) | |
1157 | { | |
1158 | DfuFirmwarePrivate *priv = GET_PRIVATE (firmware); | |
1159 | g_debug ("removing metadata %s", key); | |
1160 | g_hash_table_remove (priv->metadata, key); | |
1161 | } | |
1162 | ||
1163 | /** | |
1164 | * dfu_firmware_build_metadata_table: | |
1165 | **/ | |
1166 | static GBytes * | |
1167 | dfu_firmware_build_metadata_table (DfuFirmware *firmware, GError **error) | |
1168 | { | |
1169 | DfuFirmwarePrivate *priv = GET_PRIVATE (firmware); | |
1170 | GList *l; | |
1171 | guint8 mdbuf[239]; | |
1172 | guint idx = 0; | |
1173 | guint number_keys; | |
1174 | g_autoptr(GList) keys = NULL; | |
1175 | ||
1176 | /* no metadata */ | |
1177 | if (g_hash_table_size (priv->metadata) == 0) | |
1178 | return g_bytes_new (NULL, 0); | |
1179 | ||
1180 | /* check the number of keys */ | |
1181 | keys = g_hash_table_get_keys (priv->metadata); | |
1182 | number_keys = g_list_length (keys); | |
1183 | if (number_keys > 59) { | |
1184 | g_set_error (error, | |
1185 | DFU_ERROR, | |
1186 | DFU_ERROR_NOT_SUPPORTED, | |
1187 | "too many metadata keys (%i)", | |
1188 | number_keys); | |
1189 | return NULL; | |
1190 | } | |
1191 | ||
1192 | /* write the signature */ | |
1193 | mdbuf[idx++] = 'M'; | |
1194 | mdbuf[idx++] = 'D'; | |
1195 | mdbuf[idx++] = number_keys; | |
1196 | for (l = keys; l != NULL; l = l->next) { | |
1197 | const gchar *key; | |
1198 | const gchar *value; | |
1199 | guint key_len; | |
1200 | guint value_len; | |
1201 | ||
1202 | /* check key and value length */ | |
1203 | key = l->data; | |
1204 | key_len = strlen (key); | |
1205 | if (key_len > 233) { | |
1206 | g_set_error (error, | |
1207 | DFU_ERROR, | |
1208 | DFU_ERROR_NOT_SUPPORTED, | |
1209 | "metdata key too long: %s", | |
1210 | key); | |
1211 | return NULL; | |
1212 | } | |
1213 | value = g_hash_table_lookup (priv->metadata, key); | |
1214 | value_len = strlen (value); | |
1215 | if (value_len > 233) { | |
1216 | g_set_error (error, | |
1217 | DFU_ERROR, | |
1218 | DFU_ERROR_NOT_SUPPORTED, | |
1219 | "value too long: %s", | |
1220 | value); | |
1221 | return NULL; | |
1222 | } | |
1223 | ||
1224 | /* do we still have space? */ | |
1225 | if (idx + key_len + value_len + 2 > sizeof(mdbuf)) { | |
1226 | g_set_error (error, | |
1227 | DFU_ERROR, | |
1228 | DFU_ERROR_NOT_SUPPORTED, | |
1229 | "not enough space in metadata table, " | |
1230 | "already used %i bytes", idx); | |
1231 | return NULL; | |
1232 | } | |
1233 | ||
1234 | /* write the key */ | |
1235 | mdbuf[idx++] = key_len; | |
1236 | memcpy(mdbuf + idx, key, key_len); | |
1237 | idx += key_len; | |
1238 | ||
1239 | /* write the value */ | |
1240 | mdbuf[idx++] = value_len; | |
1241 | memcpy(mdbuf + idx, value, value_len); | |
1242 | idx += value_len; | |
1243 | } | |
1244 | g_debug ("metadata table was %i/%i bytes", idx, (guint) sizeof(mdbuf)); | |
1245 | return g_bytes_new (mdbuf, idx); | |
1246 | } | |
1247 | ||
1248 | /** | |
1249 | * dfu_firmware_add_footer: | |
1250 | **/ | |
1251 | static GBytes * | |
1252 | dfu_firmware_add_footer (DfuFirmware *firmware, GBytes *contents, GError **error) | |
1253 | { | |
1254 | DfuFirmwarePrivate *priv = GET_PRIVATE (firmware); | |
1255 | DfuFirmwareFooter *ftr; | |
1256 | const guint8 *data_bin; | |
1257 | const guint8 *data_md; | |
1258 | gsize length_bin = 0; | |
1259 | gsize length_md = 0; | |
1260 | guint8 *buf; | |
1261 | g_autoptr(GBytes) metadata_table = NULL; | |
1262 | ||
1263 | /* get any file metadata */ | |
1264 | metadata_table = dfu_firmware_build_metadata_table (firmware, error); | |
1265 | if (metadata_table == NULL) | |
1266 | return NULL; | |
1267 | data_md = g_bytes_get_data (metadata_table, &length_md); | |
1268 | ||
1269 | /* add the raw firmware data */ | |
1270 | data_bin = g_bytes_get_data (contents, &length_bin); | |
1271 | buf = g_malloc0 (length_bin + length_md + 0x10); | |
1272 | memcpy (buf + 0, data_bin, length_bin); | |
1273 | ||
1274 | /* add the metadata table */ | |
1275 | memcpy (buf + length_bin, data_md, length_md); | |
1276 | ||
1277 | /* set up LE footer */ | |
1278 | ftr = (DfuFirmwareFooter *) (buf + length_bin + length_md); | |
1279 | ftr->release = GUINT16_TO_LE (priv->release); | |
1280 | ftr->pid = GUINT16_TO_LE (priv->pid); | |
1281 | ftr->vid = GUINT16_TO_LE (priv->vid); | |
1282 | ftr->ver = GUINT16_TO_LE (priv->format); | |
1283 | ftr->len = GUINT16_TO_LE (0x10 + length_md); | |
1284 | memcpy(ftr->sig, "UFD", 3); | |
1285 | ftr->crc = dfu_firmware_generate_crc32 (buf, length_bin + length_md + 12); | |
1286 | ||
1287 | /* return all data */ | |
1288 | return g_bytes_new_take (buf, length_bin + length_md + 0x10); | |
1289 | } | |
1290 | ||
1291 | /** | |
1292 | * dfu_firmware_write_data: | |
1293 | * @firmware: a #DfuFirmware | |
1294 | * @error: a #GError, or %NULL | |
1295 | * | |
1296 | * Writes DFU data to a data blob with a DFU-specific footer. | |
1297 | * | |
1298 | * Return value: (transfer none): firmware data | |
1299 | * | |
1300 | * Since: 0.5.4 | |
1301 | **/ | |
1302 | GBytes * | |
1303 | dfu_firmware_write_data (DfuFirmware *firmware, GError **error) | |
1304 | { | |
1305 | DfuFirmwarePrivate *priv = GET_PRIVATE (firmware); | |
1306 | DfuImage *image; | |
1307 | ||
1308 | g_return_val_if_fail (DFU_IS_FIRMWARE (firmware), NULL); | |
1309 | g_return_val_if_fail (error == NULL || *error == NULL, NULL); | |
1310 | ||
1311 | /* at least one image */ | |
1312 | if (priv->images == 0) { | |
1313 | g_set_error_literal (error, | |
1314 | DFU_ERROR, | |
1315 | DFU_ERROR_INTERNAL, | |
1316 | "no image data to write"); | |
1317 | return NULL; | |
1318 | } | |
1319 | ||
1320 | /* DFU only supports one image */ | |
1321 | if (priv->images->len > 1 && | |
1322 | priv->format != DFU_FIRMWARE_FORMAT_DFUSE) { | |
1323 | g_set_error (error, | |
1324 | DFU_ERROR, | |
1325 | DFU_ERROR_INTERNAL, | |
1326 | "only DfuSe format supports multiple images (%i)", | |
1327 | priv->images->len); | |
1328 | return NULL; | |
1329 | } | |
1330 | ||
1331 | /* raw */ | |
1332 | if (priv->format == DFU_FIRMWARE_FORMAT_RAW) { | |
1333 | GBytes *contents; | |
1334 | DfuElement *element; | |
1335 | image = dfu_firmware_get_image_default (firmware); | |
1336 | g_assert (image != NULL); | |
1337 | element = dfu_image_get_element (image, 0); | |
1338 | if (element == NULL) { | |
1339 | g_set_error (error, | |
1340 | DFU_ERROR, | |
1341 | DFU_ERROR_NOT_FOUND, | |
1342 | "no firmware element data to write"); | |
1343 | return NULL; | |
1344 | } | |
1345 | contents = dfu_element_get_contents (element); | |
1346 | return g_bytes_ref (contents); | |
1347 | } | |
1348 | ||
1349 | /* plain-old DFU */ | |
1350 | if (priv->format == DFU_FIRMWARE_FORMAT_DFU_1_0) { | |
1351 | GBytes *contents; | |
1352 | DfuElement *element; | |
1353 | image = dfu_firmware_get_image_default (firmware); | |
1354 | g_assert (image != NULL); | |
1355 | element = dfu_image_get_element (image, 0); | |
1356 | if (element == NULL) { | |
1357 | g_set_error (error, | |
1358 | DFU_ERROR, | |
1359 | DFU_ERROR_NOT_FOUND, | |
1360 | "no firmware element data to write"); | |
1361 | return NULL; | |
1362 | } | |
1363 | contents = dfu_element_get_contents (element); | |
1364 | g_assert (contents != NULL); | |
1365 | return dfu_firmware_add_footer (firmware, contents, error); | |
1366 | } | |
1367 | ||
1368 | /* DfuSe */ | |
1369 | if (priv->format == DFU_FIRMWARE_FORMAT_DFUSE) { | |
1370 | g_autoptr(GBytes) contents = NULL; | |
1371 | contents = dfu_firmware_write_data_dfuse (firmware, error); | |
1372 | if (contents == NULL) | |
1373 | return NULL; | |
1374 | return dfu_firmware_add_footer (firmware, contents, error); | |
1375 | } | |
1376 | ||
1377 | /* Intel HEX */ | |
1378 | if (priv->format == DFU_FIRMWARE_FORMAT_INTEL_HEX) | |
1379 | return dfu_firmware_write_data_ihex (firmware, error); | |
1380 | ||
1381 | /* invalid */ | |
1382 | g_set_error (error, | |
1383 | DFU_ERROR, | |
1384 | DFU_ERROR_INTERNAL, | |
1385 | "invalid format for write (0x%04x)", | |
1386 | priv->format); | |
1387 | return NULL; | |
1388 | } | |
1389 | ||
1390 | /** | |
1391 | * dfu_firmware_write_file: | |
1392 | * @firmware: a #DfuFirmware | |
1393 | * @file: a #GFile | |
1394 | * @cancellable: a #GCancellable, or %NULL | |
1395 | * @error: a #GError, or %NULL | |
1396 | * | |
1397 | * Writes a DFU firmware with the optional footer. | |
1398 | * | |
1399 | * Return value: %TRUE for success | |
1400 | * | |
1401 | * Since: 0.5.4 | |
1402 | **/ | |
1403 | gboolean | |
1404 | dfu_firmware_write_file (DfuFirmware *firmware, GFile *file, | |
1405 | GCancellable *cancellable, GError **error) | |
1406 | { | |
1407 | const guint8 *data; | |
1408 | gsize length = 0; | |
1409 | g_autoptr(GBytes) bytes = NULL; | |
1410 | ||
1411 | g_return_val_if_fail (DFU_IS_FIRMWARE (firmware), FALSE); | |
1412 | g_return_val_if_fail (G_IS_FILE (file), FALSE); | |
1413 | g_return_val_if_fail (error == NULL || *error == NULL, FALSE); | |
1414 | ||
1415 | /* get blob */ | |
1416 | bytes = dfu_firmware_write_data (firmware, error); | |
1417 | if (bytes == NULL) | |
1418 | return FALSE; | |
1419 | ||
1420 | /* save to firmware */ | |
1421 | data = g_bytes_get_data (bytes, &length); | |
1422 | return g_file_replace_contents (file, | |
1423 | (const gchar *) data, | |
1424 | length, | |
1425 | NULL, | |
1426 | FALSE, | |
1427 | G_FILE_CREATE_NONE, | |
1428 | NULL, | |
1429 | cancellable, | |
1430 | error); | |
1431 | } | |
1432 | ||
1433 | /** | |
1434 | * dfu_firmware_to_string: | |
1435 | * @firmware: a #DfuFirmware | |
1436 | * | |
1437 | * Returns a string representaiton of the object. | |
1438 | * | |
1439 | * Return value: NULL terminated string, or %NULL for invalid | |
1440 | * | |
1441 | * Since: 0.5.4 | |
1442 | **/ | |
1443 | gchar * | |
1444 | dfu_firmware_to_string (DfuFirmware *firmware) | |
1445 | { | |
1446 | DfuFirmwarePrivate *priv = GET_PRIVATE (firmware); | |
1447 | DfuImage *image; | |
1448 | GList *l; | |
1449 | GString *str; | |
1450 | guint i; | |
1451 | g_autoptr(GList) keys = NULL; | |
1452 | ||
1453 | g_return_val_if_fail (DFU_IS_FIRMWARE (firmware), NULL); | |
1454 | ||
1455 | str = g_string_new (""); | |
1456 | g_string_append_printf (str, "vid: 0x%04x\n", priv->vid); | |
1457 | g_string_append_printf (str, "pid: 0x%04x\n", priv->pid); | |
1458 | g_string_append_printf (str, "release: 0x%04x\n", priv->release); | |
1459 | g_string_append_printf (str, "crc: 0x%08x\n", priv->crc); | |
1460 | g_string_append_printf (str, "format: %s [0x%04x]\n", | |
1461 | dfu_firmware_format_to_string (priv->format), | |
1462 | priv->format); | |
1463 | g_string_append_printf (str, "cipher: %s\n", | |
1464 | dfu_cipher_kind_to_string (priv->cipher_kind)); | |
1465 | ||
1466 | /* print metadata */ | |
1467 | keys = g_hash_table_get_keys (priv->metadata); | |
1468 | for (l = keys; l != NULL; l = l->next) { | |
1469 | const gchar *key; | |
1470 | const gchar *value; | |
1471 | key = l->data; | |
1472 | value = g_hash_table_lookup (priv->metadata, key); | |
1473 | g_string_append_printf (str, "metadata: %s=%s\n", key, value); | |
1474 | } | |
1475 | ||
1476 | /* print images */ | |
1477 | for (i = 0; i < priv->images->len; i++) { | |
1478 | g_autofree gchar *tmp = NULL; | |
1479 | image = g_ptr_array_index (priv->images, i); | |
1480 | tmp = dfu_image_to_string (image); | |
1481 | g_string_append_printf (str, "= IMAGE %i =\n", i); | |
1482 | g_string_append_printf (str, "%s\n", tmp); | |
1483 | } | |
1484 | ||
1485 | g_string_truncate (str, str->len - 1); | |
1486 | return g_string_free (str, FALSE); | |
1487 | } | |
1488 | ||
1489 | /** | |
1490 | * dfu_firmware_format_to_string: | |
1491 | * @format: a #DfuFirmwareFormat, e.g. %DFU_FIRMWARE_FORMAT_DFU_1_0 | |
1492 | * | |
1493 | * Returns a string representaiton of the format. | |
1494 | * | |
1495 | * Return value: NULL terminated string, or %NULL for invalid | |
1496 | * | |
1497 | * Since: 0.5.4 | |
1498 | **/ | |
1499 | const gchar * | |
1500 | dfu_firmware_format_to_string (DfuFirmwareFormat format) | |
1501 | { | |
1502 | if (format == DFU_FIRMWARE_FORMAT_RAW) | |
1503 | return "RAW"; | |
1504 | if (format == DFU_FIRMWARE_FORMAT_DFU_1_0) | |
1505 | return "DFU"; | |
1506 | if (format == DFU_FIRMWARE_FORMAT_DFUSE) | |
1507 | return "DfuSe"; | |
1508 | if (format == DFU_FIRMWARE_FORMAT_INTEL_HEX) | |
1509 | return "IHEX"; | |
1510 | return NULL; | |
1511 | } | |
1512 | ||
1513 | /** | |
1514 | * dfu_firmware_get_cipher_kind: | |
1515 | * @firmware: a #DfuFirmware | |
1516 | * | |
1517 | * Returns the kind of cipher used by the firmware file. | |
1518 | * | |
1519 | * NOTE: this value is based on a heuristic, and may not be accurate. | |
1520 | * The value %DFU_CIPHER_KIND_NONE will be returned when the cipher | |
1521 | * is not recognised. | |
1522 | * | |
1523 | * Return value: NULL terminated string, or %NULL for invalid | |
1524 | * | |
1525 | * Since: 0.5.4 | |
1526 | **/ | |
1527 | DfuCipherKind | |
1528 | dfu_firmware_get_cipher_kind (DfuFirmware *firmware) | |
1529 | { | |
1530 | DfuFirmwarePrivate *priv = GET_PRIVATE (firmware); | |
1531 | g_return_val_if_fail (DFU_IS_FIRMWARE (firmware), 0); | |
1532 | return priv->cipher_kind; | |
1533 | } |
0 | /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- | |
1 | * | |
2 | * Copyright (C) 2015 Richard Hughes <richard@hughsie.com> | |
3 | * | |
4 | * Licensed under the GNU Lesser General Public License Version 2.1 | |
5 | * | |
6 | * This library is free software; you can redistribute it and/or | |
7 | * modify it under the terms of the GNU Lesser General Public | |
8 | * License as published by the Free Software Foundation; either | |
9 | * version 2.1 of the License, or (at your option) any later version. | |
10 | * | |
11 | * This library is distributed in the hope that it will be useful, | |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
14 | * Lesser General Public License for more details. | |
15 | * | |
16 | * You should have received a copy of the GNU Lesser General Public | |
17 | * License along with this library; if not, write to the Free Software | |
18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | |
19 | */ | |
20 | ||
21 | #ifndef __DFU_FIRMWARE_H | |
22 | #define __DFU_FIRMWARE_H | |
23 | ||
24 | #include <glib-object.h> | |
25 | #include <gio/gio.h> | |
26 | ||
27 | #include "dfu-common.h" | |
28 | #include "dfu-image.h" | |
29 | ||
30 | G_BEGIN_DECLS | |
31 | ||
32 | #define DFU_TYPE_FIRMWARE (dfu_firmware_get_type ()) | |
33 | G_DECLARE_DERIVABLE_TYPE (DfuFirmware, dfu_firmware, DFU, FIRMWARE, GObject) | |
34 | ||
35 | struct _DfuFirmwareClass | |
36 | { | |
37 | GObjectClass parent_class; | |
38 | /*< private >*/ | |
39 | /* Padding for future expansion */ | |
40 | void (*_dfu_firmware_reserved1) (void); | |
41 | void (*_dfu_firmware_reserved2) (void); | |
42 | void (*_dfu_firmware_reserved3) (void); | |
43 | void (*_dfu_firmware_reserved4) (void); | |
44 | void (*_dfu_firmware_reserved5) (void); | |
45 | void (*_dfu_firmware_reserved6) (void); | |
46 | void (*_dfu_firmware_reserved7) (void); | |
47 | void (*_dfu_firmware_reserved8) (void); | |
48 | void (*_dfu_firmware_reserved9) (void); | |
49 | }; | |
50 | ||
51 | /** | |
52 | * DfuFirmwareParseFlags: | |
53 | * @DFU_FIRMWARE_PARSE_FLAG_NONE: No flags set | |
54 | * @DFU_FIRMWARE_PARSE_FLAG_NO_CRC_TEST: Do not verify the CRC | |
55 | * @DFU_FIRMWARE_PARSE_FLAG_NO_VERSION_TEST: Do not verify the DFU version | |
56 | * @DFU_FIRMWARE_PARSE_FLAG_NO_METADATA: Do not read the metadata table | |
57 | * | |
58 | * The optional flags used for parsing. | |
59 | **/ | |
60 | typedef enum { | |
61 | DFU_FIRMWARE_PARSE_FLAG_NONE = 0, | |
62 | DFU_FIRMWARE_PARSE_FLAG_NO_CRC_TEST = (1 << 0), | |
63 | DFU_FIRMWARE_PARSE_FLAG_NO_VERSION_TEST = (1 << 1), | |
64 | DFU_FIRMWARE_PARSE_FLAG_NO_METADATA = (1 << 2), | |
65 | /*< private >*/ | |
66 | DFU_FIRMWARE_PARSE_FLAG_LAST, | |
67 | } DfuFirmwareParseFlags; | |
68 | ||
69 | /** | |
70 | * DfuFirmwareFormat: | |
71 | * @DFU_FIRMWARE_FORMAT_UNKNOWN: Format unknown | |
72 | * @DFU_FIRMWARE_FORMAT_RAW: Raw format | |
73 | * @DFU_FIRMWARE_FORMAT_DFU_1_0: DFU 1.0 | |
74 | * @DFU_FIRMWARE_FORMAT_DFUSE: DfuSe extension | |
75 | * @DFU_FIRMWARE_FORMAT_INTEL_HEX: Intel HEX | |
76 | * | |
77 | * The known versions of the DFU standard in BCD format. | |
78 | **/ | |
79 | typedef enum { | |
80 | DFU_FIRMWARE_FORMAT_UNKNOWN = 0, | |
81 | DFU_FIRMWARE_FORMAT_RAW = 0x0001, | |
82 | DFU_FIRMWARE_FORMAT_DFU_1_0 = 0x0100, | |
83 | DFU_FIRMWARE_FORMAT_DFUSE = 0x011a, | |
84 | DFU_FIRMWARE_FORMAT_INTEL_HEX = 0x0002, | |
85 | /*< private >*/ | |
86 | DFU_FIRMWARE_FORMAT_LAST, | |
87 | } DfuFirmwareFormat; | |
88 | ||
89 | DfuFirmware *dfu_firmware_new (void); | |
90 | ||
91 | const gchar *dfu_firmware_format_to_string (DfuFirmwareFormat format); | |
92 | ||
93 | DfuImage *dfu_firmware_get_image (DfuFirmware *firmware, | |
94 | guint8 alt_setting); | |
95 | DfuImage *dfu_firmware_get_image_by_name (DfuFirmware *firmware, | |
96 | const gchar *name); | |
97 | DfuImage *dfu_firmware_get_image_default (DfuFirmware *firmware); | |
98 | GPtrArray *dfu_firmware_get_images (DfuFirmware *firmware); | |
99 | guint16 dfu_firmware_get_vid (DfuFirmware *firmware); | |
100 | guint16 dfu_firmware_get_pid (DfuFirmware *firmware); | |
101 | guint16 dfu_firmware_get_release (DfuFirmware *firmware); | |
102 | guint16 dfu_firmware_get_format (DfuFirmware *firmware); | |
103 | guint32 dfu_firmware_get_size (DfuFirmware *firmware); | |
104 | DfuCipherKind dfu_firmware_get_cipher_kind (DfuFirmware *firmware); | |
105 | ||
106 | void dfu_firmware_add_image (DfuFirmware *firmware, | |
107 | DfuImage *image); | |
108 | void dfu_firmware_set_vid (DfuFirmware *firmware, | |
109 | guint16 vid); | |
110 | void dfu_firmware_set_pid (DfuFirmware *firmware, | |
111 | guint16 pid); | |
112 | void dfu_firmware_set_release (DfuFirmware *firmware, | |
113 | guint16 release); | |
114 | void dfu_firmware_set_format (DfuFirmware *firmware, | |
115 | DfuFirmwareFormat format); | |
116 | ||
117 | gboolean dfu_firmware_parse_data (DfuFirmware *firmware, | |
118 | GBytes *bytes, | |
119 | DfuFirmwareParseFlags flags, | |
120 | GError **error); | |
121 | gboolean dfu_firmware_parse_file (DfuFirmware *firmware, | |
122 | GFile *file, | |
123 | DfuFirmwareParseFlags flags, | |
124 | GCancellable *cancellable, | |
125 | GError **error); | |
126 | ||
127 | GBytes *dfu_firmware_write_data (DfuFirmware *firmware, | |
128 | GError **error); | |
129 | gboolean dfu_firmware_write_file (DfuFirmware *firmware, | |
130 | GFile *file, | |
131 | GCancellable *cancellable, | |
132 | GError **error); | |
133 | gchar *dfu_firmware_to_string (DfuFirmware *firmware); | |
134 | ||
135 | const gchar *dfu_firmware_get_metadata (DfuFirmware *firmware, | |
136 | const gchar *key); | |
137 | void dfu_firmware_set_metadata (DfuFirmware *firmware, | |
138 | const gchar *key, | |
139 | const gchar *value); | |
140 | void dfu_firmware_remove_metadata (DfuFirmware *firmware, | |
141 | const gchar *key); | |
142 | ||
143 | G_END_DECLS | |
144 | ||
145 | #endif /* __DFU_FIRMWARE_H */ |
0 | /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- | |
1 | * | |
2 | * Copyright (C) 2015 Richard Hughes <richard@hughsie.com> | |
3 | * | |
4 | * Licensed under the GNU Lesser General Public License Version 2.1 | |
5 | * | |
6 | * This library is free software; you can redistribute it and/or | |
7 | * modify it under the terms of the GNU Lesser General Public | |
8 | * License as published by the Free Software Foundation; either | |
9 | * version 2.1 of the License, or (at your option) any later version. | |
10 | * | |
11 | * This library is distributed in the hope that it will be useful, | |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
14 | * Lesser General Public License for more details. | |
15 | * | |
16 | * You should have received a copy of the GNU Lesser General Public | |
17 | * License along with this library; if not, write to the Free Software | |
18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | |
19 | */ | |
20 | ||
21 | #ifndef __DFU_IMAGE_PRIVATE_H | |
22 | #define __DFU_IMAGE_PRIVATE_H | |
23 | ||
24 | #include "dfu-image.h" | |
25 | ||
26 | G_BEGIN_DECLS | |
27 | ||
28 | DfuImage *dfu_image_from_dfuse (const guint8 *data, | |
29 | guint32 length, | |
30 | guint32 *consumed, | |
31 | GError **error); | |
32 | GBytes *dfu_image_to_dfuse (DfuImage *image); | |
33 | ||
34 | G_END_DECLS | |
35 | ||
36 | #endif /* __DFU_IMAGE_PRIVATE_H */ |
0 | /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- | |
1 | * | |
2 | * Copyright (C) 2015 Richard Hughes <richard@hughsie.com> | |
3 | * | |
4 | * Licensed under the GNU Lesser General Public License Version 2.1 | |
5 | * | |
6 | * This library is free software; you can redistribute it and/or | |
7 | * modify it under the terms of the GNU Lesser General Public | |
8 | * License as published by the Free Software Foundation; either | |
9 | * version 2.1 of the License, or (at your option) any later version. | |
10 | * | |
11 | * This library is distributed in the hope that it will be useful, | |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
14 | * Lesser General Public License for more details. | |
15 | * | |
16 | * You should have received a copy of the GNU Lesser General Public | |
17 | * License along with this library; if not, write to the Free Software | |
18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | |
19 | */ | |
20 | ||
21 | /** | |
22 | * SECTION:dfu-image | |
23 | * @short_description: Object representing a a firmware image | |
24 | * | |
25 | * A #DfuImage is typically made up of several #DfuElements, although | |
26 | * typically there will only be one. | |
27 | * | |
28 | * See also: #DfuElement | |
29 | */ | |
30 | ||
31 | #include "config.h" | |
32 | ||
33 | #include <string.h> | |
34 | #include <stdio.h> | |
35 | ||
36 | #include "dfu-common.h" | |
37 | #include "dfu-element-private.h" | |
38 | #include "dfu-error.h" | |
39 | #include "dfu-image-private.h" | |
40 | ||
41 | static void dfu_image_finalize (GObject *object); | |
42 | ||
43 | /** | |
44 | * DfuImagePrivate: | |
45 | * | |
46 | * Private #DfuImage data | |
47 | **/ | |
48 | typedef struct { | |
49 | GPtrArray *elements; | |
50 | gchar name[255]; | |
51 | guint8 alt_setting; | |
52 | } DfuImagePrivate; | |
53 | ||
54 | G_DEFINE_TYPE_WITH_PRIVATE (DfuImage, dfu_image, G_TYPE_OBJECT) | |
55 | #define GET_PRIVATE(o) (dfu_image_get_instance_private (o)) | |
56 | ||
57 | /** | |
58 | * dfu_image_class_init: | |
59 | **/ | |
60 | static void | |
61 | dfu_image_class_init (DfuImageClass *klass) | |
62 | { | |
63 | GObjectClass *object_class = G_OBJECT_CLASS (klass); | |
64 | object_class->finalize = dfu_image_finalize; | |
65 | } | |
66 | ||
67 | /** | |
68 | * dfu_image_init: | |
69 | **/ | |
70 | static void | |
71 | dfu_image_init (DfuImage *image) | |
72 | { | |
73 | DfuImagePrivate *priv = GET_PRIVATE (image); | |
74 | priv->elements = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref); | |
75 | memset (priv->name, 0x00, 255); | |
76 | } | |
77 | ||
78 | /** | |
79 | * dfu_image_finalize: | |
80 | **/ | |
81 | static void | |
82 | dfu_image_finalize (GObject *object) | |
83 | { | |
84 | DfuImage *image = DFU_IMAGE (object); | |
85 | DfuImagePrivate *priv = GET_PRIVATE (image); | |
86 | ||
87 | g_ptr_array_unref (priv->elements); | |
88 | ||
89 | G_OBJECT_CLASS (dfu_image_parent_class)->finalize (object); | |
90 | } | |
91 | ||
92 | /** | |
93 | * dfu_image_new: | |
94 | * | |
95 | * Creates a new DFU image object. | |
96 | * | |
97 | * Return value: a new #DfuImage | |
98 | * | |
99 | * Since: 0.5.4 | |
100 | **/ | |
101 | DfuImage * | |
102 | dfu_image_new (void) | |
103 | { | |
104 | DfuImage *image; | |
105 | image = g_object_new (DFU_TYPE_IMAGE, NULL); | |
106 | return image; | |
107 | } | |
108 | ||
109 | /** | |
110 | * dfu_image_get_elements: | |
111 | * @image: a #DfuImage | |
112 | * | |
113 | * Gets the element data. | |
114 | * | |
115 | * Return value: (transfer none) (element-type DfuElement): element data | |
116 | * | |
117 | * Since: 0.5.4 | |
118 | **/ | |
119 | GPtrArray * | |
120 | dfu_image_get_elements (DfuImage *image) | |
121 | { | |
122 | DfuImagePrivate *priv = GET_PRIVATE (image); | |
123 | g_return_val_if_fail (DFU_IS_IMAGE (image), NULL); | |
124 | return priv->elements; | |
125 | } | |
126 | ||
127 | /** | |
128 | * dfu_image_get_element: | |
129 | * @image: a #DfuImage | |
130 | * @idx: an array index | |
131 | * | |
132 | * Gets the element. | |
133 | * | |
134 | * Return value: (transfer none): element data, or %NULL for invalid | |
135 | * | |
136 | * Since: 0.5.4 | |
137 | **/ | |
138 | DfuElement * | |
139 | dfu_image_get_element (DfuImage *image, guint8 idx) | |
140 | { | |
141 | DfuImagePrivate *priv = GET_PRIVATE (image); | |
142 | g_return_val_if_fail (DFU_IS_IMAGE (image), NULL); | |
143 | if (idx >= priv->elements->len) | |
144 | return NULL; | |
145 | return g_ptr_array_index (priv->elements, idx); | |
146 | } | |
147 | ||
148 | /** | |
149 | * dfu_image_get_alt_setting: | |
150 | * @image: a #DfuImage | |
151 | * | |
152 | * Gets the alternate setting. | |
153 | * | |
154 | * Return value: integer, or 0x00 for unset | |
155 | * | |
156 | * Since: 0.5.4 | |
157 | **/ | |
158 | guint8 | |
159 | dfu_image_get_alt_setting (DfuImage *image) | |
160 | { | |
161 | DfuImagePrivate *priv = GET_PRIVATE (image); | |
162 | g_return_val_if_fail (DFU_IS_IMAGE (image), 0xff); | |
163 | return priv->alt_setting; | |
164 | } | |
165 | ||
166 | /** | |
167 | * dfu_image_get_name: | |
168 | * @image: a #DfuImage | |
169 | * | |
170 | * Gets the target name. | |
171 | * | |
172 | * Return value: a string, or %NULL for unset | |
173 | * | |
174 | * Since: 0.5.4 | |
175 | **/ | |
176 | const gchar * | |
177 | dfu_image_get_name (DfuImage *image) | |
178 | { | |
179 | DfuImagePrivate *priv = GET_PRIVATE (image); | |
180 | g_return_val_if_fail (DFU_IS_IMAGE (image), NULL); | |
181 | return priv->name; | |
182 | } | |
183 | ||
184 | /** | |
185 | * dfu_image_get_size: | |
186 | * @image: a #DfuImage | |
187 | * | |
188 | * Gets the size of all the elements in the image. | |
189 | * | |
190 | * This only returns actual data that would be sent to the device and | |
191 | * does not include any padding. | |
192 | * | |
193 | * Return value: a integer value, or 0 if there are no elements. | |
194 | * | |
195 | * Since: 0.5.4 | |
196 | **/ | |
197 | guint32 | |
198 | dfu_image_get_size (DfuImage *image) | |
199 | { | |
200 | DfuImagePrivate *priv = GET_PRIVATE (image); | |
201 | guint32 length = 0; | |
202 | guint i; | |
203 | g_return_val_if_fail (DFU_IS_IMAGE (image), 0); | |
204 | for (i = 0; i < priv->elements->len; i++) { | |
205 | DfuElement *element = g_ptr_array_index (priv->elements, i); | |
206 | length += g_bytes_get_size (dfu_element_get_contents (element)); | |
207 | } | |
208 | return length; | |
209 | } | |
210 | ||
211 | /** | |
212 | * dfu_image_add_element: | |
213 | * @image: a #DfuImage | |
214 | * @element: a #DfuElement | |
215 | * | |
216 | * Adds an element to the image. | |
217 | * | |
218 | * Since: 0.5.4 | |
219 | **/ | |
220 | void | |
221 | dfu_image_add_element (DfuImage *image, DfuElement *element) | |
222 | { | |
223 | DfuImagePrivate *priv = GET_PRIVATE (image); | |
224 | g_return_if_fail (DFU_IS_IMAGE (image)); | |
225 | g_return_if_fail (DFU_IS_ELEMENT (element)); | |
226 | g_ptr_array_add (priv->elements, g_object_ref (element)); | |
227 | } | |
228 | ||
229 | /** | |
230 | * dfu_image_set_alt_setting: | |
231 | * @image: a #DfuImage | |
232 | * @alt_setting: vendor ID, or 0xffff for unset | |
233 | * | |
234 | * Sets the vendor ID. | |
235 | * | |
236 | * Since: 0.5.4 | |
237 | **/ | |
238 | void | |
239 | dfu_image_set_alt_setting (DfuImage *image, guint8 alt_setting) | |
240 | { | |
241 | DfuImagePrivate *priv = GET_PRIVATE (image); | |
242 | g_return_if_fail (DFU_IS_IMAGE (image)); | |
243 | priv->alt_setting = alt_setting; | |
244 | } | |
245 | ||
246 | /** | |
247 | * dfu_image_set_name: | |
248 | * @image: a #DfuImage | |
249 | * @name: a target string, or %NULL | |
250 | * | |
251 | * Sets the target name. | |
252 | * | |
253 | * Since: 0.5.4 | |
254 | **/ | |
255 | void | |
256 | dfu_image_set_name (DfuImage *image, const gchar *name) | |
257 | { | |
258 | guint16 sz; | |
259 | DfuImagePrivate *priv = GET_PRIVATE (image); | |
260 | g_return_if_fail (DFU_IS_IMAGE (image)); | |
261 | ||
262 | /* this is a hard limit in DfuSe */ | |
263 | memset (priv->name, 0x00, 0xff); | |
264 | if (name != NULL) { | |
265 | sz = MIN (strlen (name), 0xff - 1); | |
266 | memcpy (priv->name, name, sz); | |
267 | } | |
268 | } | |
269 | ||
270 | /** | |
271 | * dfu_image_to_string: | |
272 | * @image: a #DfuImage | |
273 | * | |
274 | * Returns a string representaiton of the object. | |
275 | * | |
276 | * Return value: NULL terminated string, or %NULL for invalid | |
277 | * | |
278 | * Since: 0.5.4 | |
279 | **/ | |
280 | gchar * | |
281 | dfu_image_to_string (DfuImage *image) | |
282 | { | |
283 | DfuImagePrivate *priv = GET_PRIVATE (image); | |
284 | GString *str; | |
285 | guint i; | |
286 | ||
287 | g_return_val_if_fail (DFU_IS_IMAGE (image), NULL); | |
288 | ||
289 | str = g_string_new (""); | |
290 | g_string_append_printf (str, "alt_setting: 0x%02x\n", priv->alt_setting); | |
291 | if (priv->name[0] != '\0') | |
292 | g_string_append_printf (str, "name: %s\n", priv->name); | |
293 | g_string_append_printf (str, "elements: 0x%02x\n", | |
294 | priv->elements->len); | |
295 | ||
296 | /* add elements */ | |
297 | for (i = 0; i < priv->elements->len; i++) { | |
298 | DfuElement *element = g_ptr_array_index (priv->elements, i); | |
299 | g_autofree gchar *tmp = NULL; | |
300 | tmp = dfu_element_to_string (element); | |
301 | g_string_append_printf (str, "== ELEMENT %i ==\n", i); | |
302 | g_string_append_printf (str, "%s\n", tmp); | |
303 | } | |
304 | ||
305 | g_string_truncate (str, str->len - 1); | |
306 | return g_string_free (str, FALSE); | |
307 | } | |
308 | ||
309 | /* DfuSe image header */ | |
310 | typedef struct __attribute__((packed)) { | |
311 | guint8 sig[6]; | |
312 | guint8 alt_setting; | |
313 | guint32 target_named; | |
314 | gchar target_name[255]; | |
315 | guint32 target_size; | |
316 | guint32 elements; | |
317 | } DfuSeImagePrefix; | |
318 | ||
319 | /** | |
320 | * dfu_image_from_dfuse: (skip) | |
321 | * @data: data buffer | |
322 | * @length: length of @data we can access | |
323 | * @consumed: (out): the number of bytes we consued | |
324 | * @error: a #GError, or %NULL | |
325 | * | |
326 | * Unpacks an image from DfuSe data. | |
327 | * | |
328 | * Returns: a #DfuImage, or %NULL for error | |
329 | **/ | |
330 | DfuImage * | |
331 | dfu_image_from_dfuse (const guint8 *data, | |
332 | guint32 length, | |
333 | guint32 *consumed, | |
334 | GError **error) | |
335 | { | |
336 | DfuImagePrivate *priv; | |
337 | DfuSeImagePrefix *im; | |
338 | guint32 offset = sizeof(DfuSeImagePrefix); | |
339 | guint j; | |
340 | g_autoptr(DfuImage) image = NULL; | |
341 | ||
342 | g_assert_cmpint(sizeof(DfuSeImagePrefix), ==, 274); | |
343 | ||
344 | /* check input buffer size */ | |
345 | if (length < sizeof(DfuSeImagePrefix)) { | |
346 | g_set_error (error, | |
347 | DFU_ERROR, | |
348 | DFU_ERROR_INTERNAL, | |
349 | "invalid image data size %u", | |
350 | (guint32) length); | |
351 | return NULL; | |
352 | } | |
353 | ||
354 | /* verify image signature */ | |
355 | im = (DfuSeImagePrefix *) data; | |
356 | if (memcmp (im->sig, "Target", 6) != 0) { | |
357 | g_set_error_literal (error, | |
358 | DFU_ERROR, | |
359 | DFU_ERROR_INVALID_FILE, | |
360 | "invalid DfuSe target signature"); | |
361 | return NULL; | |
362 | } | |
363 | ||
364 | /* create new image */ | |
365 | image = dfu_image_new (); | |
366 | priv = GET_PRIVATE (image); | |
367 | priv->alt_setting = im->alt_setting; | |
368 | if (im->target_named == 0x01) | |
369 | memcpy (priv->name, im->target_name, 255); | |
370 | ||
371 | /* parse elements */ | |
372 | length -= offset; | |
373 | for (j = 0; j < im->elements; j++) { | |
374 | guint32 consumed_local; | |
375 | g_autoptr(DfuElement) element = NULL; | |
376 | element = dfu_element_from_dfuse (data + offset, length, | |
377 | &consumed_local, error); | |
378 | if (element == NULL) | |
379 | return NULL; | |
380 | dfu_image_add_element (image, element); | |
381 | offset += consumed_local; | |
382 | length -= consumed_local; | |
383 | } | |
384 | ||
385 | /* return size */ | |
386 | if (consumed != NULL) | |
387 | *consumed = offset; | |
388 | ||
389 | return g_object_ref (image); | |
390 | } | |
391 | ||
392 | /** | |
393 | * dfu_image_to_dfuse: (skip) | |
394 | * @image: a #DfuImage | |
395 | * | |
396 | * Packs a DfuSe image | |
397 | * | |
398 | * Returns: (transfer full): the packed data | |
399 | **/ | |
400 | GBytes * | |
401 | dfu_image_to_dfuse (DfuImage *image) | |
402 | { | |
403 | DfuImagePrivate *priv = GET_PRIVATE (image); | |
404 | DfuElement *element; | |
405 | DfuSeImagePrefix *im; | |
406 | GBytes *bytes; | |
407 | guint32 length_total = 0; | |
408 | guint32 offset = sizeof (DfuSeImagePrefix); | |
409 | guint8 *buf; | |
410 | guint i; | |
411 | g_autoptr(GPtrArray) element_array = NULL; | |
412 | ||
413 | /* get total size */ | |
414 | element_array = g_ptr_array_new_with_free_func ((GDestroyNotify) g_bytes_unref); | |
415 | for (i = 0; i < priv->elements->len; i++) { | |
416 | element = g_ptr_array_index (priv->elements, i); | |
417 | bytes = dfu_element_to_dfuse (element); | |
418 | g_ptr_array_add (element_array, bytes); | |
419 | length_total += g_bytes_get_size (bytes); | |
420 | } | |
421 | ||
422 | /* add prefix */ | |
423 | buf = g_malloc0 (length_total + sizeof (DfuSeImagePrefix)); | |
424 | im = (DfuSeImagePrefix *) buf; | |
425 | memcpy (im->sig, "Target", 6); | |
426 | im->alt_setting = priv->alt_setting; | |
427 | if (priv->name != NULL) { | |
428 | im->target_named = 0x01; | |
429 | memcpy (im->target_name, priv->name, 255); | |
430 | } | |
431 | im->target_size = length_total; | |
432 | im->elements = priv->elements->len; | |
433 | ||
434 | /* copy data */ | |
435 | for (i = 0; i < element_array->len; i++) { | |
436 | const guint8 *data; | |
437 | gsize length; | |
438 | bytes = g_ptr_array_index (element_array, i); | |
439 | data = g_bytes_get_data (bytes, &length); | |
440 | memcpy (buf + offset, data, length); | |
441 | offset += length; | |
442 | } | |
443 | return g_bytes_new_take (buf, length_total + sizeof (DfuSeImagePrefix)); | |
444 | } |
0 | /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- | |
1 | * | |
2 | * Copyright (C) 2015 Richard Hughes <richard@hughsie.com> | |
3 | * | |
4 | * Licensed under the GNU Lesser General Public License Version 2.1 | |
5 | * | |
6 | * This library is free software; you can redistribute it and/or | |
7 | * modify it under the terms of the GNU Lesser General Public | |
8 | * License as published by the Free Software Foundation; either | |
9 | * version 2.1 of the License, or (at your option) any later version. | |
10 | * | |
11 | * This library is distributed in the hope that it will be useful, | |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
14 | * Lesser General Public License for more details. | |
15 | * | |
16 | * You should have received a copy of the GNU Lesser General Public | |
17 | * License along with this library; if not, write to the Free Software | |
18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | |
19 | */ | |
20 | ||
21 | #ifndef __DFU_IMAGE_H | |
22 | #define __DFU_IMAGE_H | |
23 | ||
24 | #include <glib-object.h> | |
25 | #include <gio/gio.h> | |
26 | ||
27 | #include "dfu-element.h" | |
28 | ||
29 | G_BEGIN_DECLS | |
30 | ||
31 | #define DFU_TYPE_IMAGE (dfu_image_get_type ()) | |
32 | G_DECLARE_DERIVABLE_TYPE (DfuImage, dfu_image, DFU, IMAGE, GObject) | |
33 | ||
34 | struct _DfuImageClass | |
35 | { | |
36 | GObjectClass parent_class; | |
37 | /*< private >*/ | |
38 | /* Padding for future expansion */ | |
39 | void (*_dfu_image_reserved1) (void); | |
40 | void (*_dfu_image_reserved2) (void); | |
41 | void (*_dfu_image_reserved3) (void); | |
42 | void (*_dfu_image_reserved4) (void); | |
43 | void (*_dfu_image_reserved5) (void); | |
44 | void (*_dfu_image_reserved6) (void); | |
45 | void (*_dfu_image_reserved7) (void); | |
46 | void (*_dfu_image_reserved8) (void); | |
47 | void (*_dfu_image_reserved9) (void); | |
48 | }; | |
49 | ||
50 | DfuImage *dfu_image_new (void); | |
51 | ||
52 | GPtrArray *dfu_image_get_elements (DfuImage *image); | |
53 | DfuElement *dfu_image_get_element (DfuImage *image, | |
54 | guint8 idx); | |
55 | guint8 dfu_image_get_alt_setting (DfuImage *image); | |
56 | const gchar *dfu_image_get_name (DfuImage *image); | |
57 | guint32 dfu_image_get_size (DfuImage *image); | |
58 | ||
59 | void dfu_image_add_element (DfuImage *image, | |
60 | DfuElement *element); | |
61 | ||
62 | void dfu_image_set_alt_setting (DfuImage *image, | |
63 | guint8 alt_setting); | |
64 | void dfu_image_set_name (DfuImage *image, | |
65 | const gchar *name); | |
66 | ||
67 | gchar *dfu_image_to_string (DfuImage *image); | |
68 | ||
69 | G_END_DECLS | |
70 | ||
71 | #endif /* __DFU_IMAGE_H */ |
0 | /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- | |
1 | * | |
2 | * Copyright (C) 2015 Richard Hughes <richard@hughsie.com> | |
3 | * | |
4 | * Licensed under the GNU Lesser General Public License Version 2.1 | |
5 | * | |
6 | * This library is free software; you can redistribute it and/or | |
7 | * modify it under the terms of the GNU Lesser General Public | |
8 | * License as published by the Free Software Foundation; either | |
9 | * version 2.1 of the License, or (at your option) any later version. | |
10 | * | |
11 | * This library is distributed in the hope that it will be useful, | |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
14 | * Lesser General Public License for more details. | |
15 | * | |
16 | * You should have received a copy of the GNU Lesser General Public | |
17 | * License along with this library; if not, write to the Free Software | |
18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | |
19 | */ | |
20 | ||
21 | #ifndef __DFU_SECTOR_PRIVATE_H | |
22 | #define __DFU_SECTOR_PRIVATE_H | |
23 | ||
24 | #include "dfu-sector.h" | |
25 | ||
26 | G_BEGIN_DECLS | |
27 | ||
28 | DfuSector *dfu_sector_new (guint32 address, | |
29 | guint32 size, | |
30 | guint32 size_left, | |
31 | guint16 zone, | |
32 | guint16 number, | |
33 | DfuSectorCap cap); | |
34 | ||
35 | G_END_DECLS | |
36 | ||
37 | #endif /* __DFU_SECTOR_PRIVATE_H */ |
0 | /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- | |
1 | * | |
2 | * Copyright (C) 2015 Richard Hughes <richard@hughsie.com> | |
3 | * | |
4 | * Licensed under the GNU Lesser General Public License Version 2.1 | |
5 | * | |
6 | * This library is free software; you can redistribute it and/or | |
7 | * modify it under the terms of the GNU Lesser General Public | |
8 | * License as published by the Free Software Foundation; either | |
9 | * version 2.1 of the License, or (at your option) any later version. | |
10 | * | |
11 | * This library is distributed in the hope that it will be useful, | |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
14 | * Lesser General Public License for more details. | |
15 | * | |
16 | * You should have received a copy of the GNU Lesser General Public | |
17 | * License along with this library; if not, write to the Free Software | |
18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | |
19 | */ | |
20 | ||
21 | /** | |
22 | * SECTION:dfu-sector | |
23 | * @short_description: Object representing a sector on a chip | |
24 | * | |
25 | * This object represents an sector of memory at a specific address on the | |
26 | * device itself. | |
27 | * | |
28 | * This allows relocatable data segments to be stored in different | |
29 | * locations on the device itself. | |
30 | * | |
31 | * You can think of these objects as flash segments on devices, where a | |
32 | * complete block can be erased and then written to. | |
33 | * | |
34 | * See also: #DfuElement | |
35 | */ | |
36 | ||
37 | #include "config.h" | |
38 | ||
39 | #include <string.h> | |
40 | #include <stdio.h> | |
41 | ||
42 | #include "dfu-common.h" | |
43 | #include "dfu-sector-private.h" | |
44 | ||
45 | /** | |
46 | * DfuSectorPrivate: | |
47 | * | |
48 | * Private #DfuSector data | |
49 | **/ | |
50 | typedef struct { | |
51 | guint32 address; | |
52 | guint32 size; | |
53 | guint32 size_left; | |
54 | guint16 zone; | |
55 | guint16 number; | |
56 | DfuSectorCap cap; | |
57 | } DfuSectorPrivate; | |
58 | ||
59 | G_DEFINE_TYPE_WITH_PRIVATE (DfuSector, dfu_sector, G_TYPE_OBJECT) | |
60 | #define GET_PRIVATE(o) (dfu_sector_get_instance_private (o)) | |
61 | ||
62 | /** | |
63 | * dfu_sector_class_init: | |
64 | **/ | |
65 | static void | |
66 | dfu_sector_class_init (DfuSectorClass *klass) | |
67 | { | |
68 | } | |
69 | ||
70 | /** | |
71 | * dfu_sector_init: | |
72 | **/ | |
73 | static void | |
74 | dfu_sector_init (DfuSector *sector) | |
75 | { | |
76 | } | |
77 | ||
78 | /** | |
79 | * dfu_sector_new: (skip) | |
80 | * address: the address for the sector | |
81 | * size: the size of this sector | |
82 | * size_left: the size of the rest of the sector | |
83 | * zone: the zone of memory the setor belongs | |
84 | * number: the sector number in the zone | |
85 | * cap: the #DfuSectorCap | |
86 | * | |
87 | * Creates a new DFU sector object. | |
88 | * | |
89 | * Return value: a new #DfuSector | |
90 | * | |
91 | * Since: 0.5.4 | |
92 | **/ | |
93 | DfuSector * | |
94 | dfu_sector_new (guint32 address, guint32 size, guint32 size_left, | |
95 | guint16 zone, guint16 number, DfuSectorCap cap) | |
96 | { | |
97 | DfuSectorPrivate *priv; | |
98 | DfuSector *sector; | |
99 | sector = g_object_new (DFU_TYPE_SECTOR, NULL); | |
100 | priv = GET_PRIVATE (sector); | |
101 | priv->address = address; | |
102 | priv->size = size; | |
103 | priv->size_left = size_left; | |
104 | priv->zone = zone; | |
105 | priv->number = number; | |
106 | priv->cap = cap; | |
107 | return sector; | |
108 | } | |
109 | ||
110 | /** | |
111 | * dfu_sector_get_address: | |
112 | * @sector: a #DfuSector | |
113 | * | |
114 | * Gets the alternate setting. | |
115 | * | |
116 | * Return value: integer, or 0x00 for unset | |
117 | * | |
118 | * Since: 0.5.4 | |
119 | **/ | |
120 | guint32 | |
121 | dfu_sector_get_address (DfuSector *sector) | |
122 | { | |
123 | DfuSectorPrivate *priv = GET_PRIVATE (sector); | |
124 | g_return_val_if_fail (DFU_IS_SECTOR (sector), 0x00); | |
125 | return priv->address; | |
126 | } | |
127 | ||
128 | /** | |
129 | * dfu_sector_get_size: | |
130 | * @sector: a #DfuSector | |
131 | * | |
132 | * Gets the alternate setting. | |
133 | * | |
134 | * Return value: integer, or 0x00 for unset | |
135 | * | |
136 | * Since: 0.5.4 | |
137 | **/ | |
138 | guint32 | |
139 | dfu_sector_get_size (DfuSector *sector) | |
140 | { | |
141 | DfuSectorPrivate *priv = GET_PRIVATE (sector); | |
142 | g_return_val_if_fail (DFU_IS_SECTOR (sector), 0x00); | |
143 | return priv->size; | |
144 | } | |
145 | ||
146 | /** | |
147 | * dfu_sector_get_size_left: | |
148 | * @sector: a #DfuSector | |
149 | * | |
150 | * Gets the alternate setting. | |
151 | * | |
152 | * Return value: integer, or 0x00 for unset | |
153 | * | |
154 | * Since: 0.5.4 | |
155 | **/ | |
156 | guint32 | |
157 | dfu_sector_get_size_left (DfuSector *sector) | |
158 | { | |
159 | DfuSectorPrivate *priv = GET_PRIVATE (sector); | |
160 | g_return_val_if_fail (DFU_IS_SECTOR (sector), 0x00); | |
161 | return priv->size_left; | |
162 | } | |
163 | ||
164 | /** | |
165 | * dfu_sector_get_id: | |
166 | * @sector: a #DfuSector | |
167 | * | |
168 | * Gets the sector ID which is a combination of the zone and sector number. | |
169 | * You can use this number to check if the segment is the 'same' as the last | |
170 | * written or read sector. | |
171 | * | |
172 | * Return value: integer ID, or 0x00 for unset | |
173 | * | |
174 | * Since: 0.5.4 | |
175 | **/ | |
176 | guint32 | |
177 | dfu_sector_get_id (DfuSector *sector) | |
178 | { | |
179 | DfuSectorPrivate *priv = GET_PRIVATE (sector); | |
180 | g_return_val_if_fail (DFU_IS_SECTOR (sector), 0x00); | |
181 | return (((guint32) priv->zone) << 16) | priv->number; | |
182 | } | |
183 | ||
184 | /** | |
185 | * dfu_sector_has_cap: | |
186 | * @sector: a #DfuSector | |
187 | * @cap: a #DfuSectorCap, e.g. %DFU_SECTOR_CAP_ERASEABLE | |
188 | * | |
189 | * Finds out if the sector has the required capability. | |
190 | * | |
191 | * Return value: %TRUE if the sector has the capabilily | |
192 | * | |
193 | * Since: 0.5.4 | |
194 | **/ | |
195 | gboolean | |
196 | dfu_sector_has_cap (DfuSector *sector, DfuSectorCap cap) | |
197 | { | |
198 | DfuSectorPrivate *priv = GET_PRIVATE (sector); | |
199 | g_return_val_if_fail (DFU_IS_SECTOR (sector), FALSE); | |
200 | return priv->cap & cap; | |
201 | } | |
202 | ||
203 | /** | |
204 | * dfu_sector_to_string: | |
205 | * @sector: a #DfuSector | |
206 | * | |
207 | * Returns a string representaiton of the object. | |
208 | * | |
209 | * Return value: NULL terminated string, or %NULL for invalid | |
210 | * | |
211 | * Since: 0.5.4 | |
212 | **/ | |
213 | gchar * | |
214 | dfu_sector_to_string (DfuSector *sector) | |
215 | { | |
216 | DfuSectorPrivate *priv = GET_PRIVATE (sector); | |
217 | GString *str; | |
218 | ||
219 | g_return_val_if_fail (DFU_IS_SECTOR (sector), NULL); | |
220 | ||
221 | str = g_string_new (""); | |
222 | g_string_append_printf (str, | |
223 | "Zone:%i, Sec#:%i, Addr:0x%08x, " | |
224 | "Size:0x%04x, Caps:0x%01x", | |
225 | priv->zone, priv->number, priv->address, | |
226 | priv->size, priv->cap); | |
227 | return g_string_free (str, FALSE); | |
228 | } |
0 | /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- | |
1 | * | |
2 | * Copyright (C) 2015 Richard Hughes <richard@hughsie.com> | |
3 | * | |
4 | * Licensed under the GNU Lesser General Public License Version 2.1 | |
5 | * | |
6 | * This library is free software; you can redistribute it and/or | |
7 | * modify it under the terms of the GNU Lesser General Public | |
8 | * License as published by the Free Software Foundation; either | |
9 | * version 2.1 of the License, or (at your option) any later version. | |
10 | * | |
11 | * This library is distributed in the hope that it will be useful, | |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
14 | * Lesser General Public License for more details. | |
15 | * | |
16 | * You should have received a copy of the GNU Lesser General Public | |
17 | * License along with this library; if not, write to the Free Software | |
18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | |
19 | */ | |
20 | ||
21 | #ifndef __DFU_SECTOR_H | |
22 | #define __DFU_SECTOR_H | |
23 | ||
24 | #include <glib-object.h> | |
25 | #include <gio/gio.h> | |
26 | ||
27 | G_BEGIN_DECLS | |
28 | ||
29 | #define DFU_TYPE_SECTOR (dfu_sector_get_type ()) | |
30 | G_DECLARE_DERIVABLE_TYPE (DfuSector, dfu_sector, DFU, SECTOR, GObject) | |
31 | ||
32 | struct _DfuSectorClass | |
33 | { | |
34 | GObjectClass parent_class; | |
35 | /*< private >*/ | |
36 | /* Padding for future expansion */ | |
37 | void (*_dfu_sector_reserved1) (void); | |
38 | void (*_dfu_sector_reserved2) (void); | |
39 | void (*_dfu_sector_reserved3) (void); | |
40 | void (*_dfu_sector_reserved4) (void); | |
41 | void (*_dfu_sector_reserved5) (void); | |
42 | void (*_dfu_sector_reserved6) (void); | |
43 | void (*_dfu_sector_reserved7) (void); | |
44 | void (*_dfu_sector_reserved8) (void); | |
45 | void (*_dfu_sector_reserved9) (void); | |
46 | }; | |
47 | ||
48 | /** | |
49 | * DfuSectorCap: | |
50 | * @DFU_SECTOR_CAP_NONE: No operations possible | |
51 | * @DFU_SECTOR_CAP_READABLE: Sector can be read | |
52 | * @DFU_SECTOR_CAP_WRITEABLE: Sector can be written | |
53 | * @DFU_SECTOR_CAP_ERASEABLE: Sector can be erased | |
54 | * | |
55 | * The flags indicating what the sector can do. | |
56 | **/ | |
57 | typedef enum { | |
58 | DFU_SECTOR_CAP_NONE = 0, | |
59 | DFU_SECTOR_CAP_READABLE = 1 << 0, | |
60 | DFU_SECTOR_CAP_WRITEABLE = 1 << 1, | |
61 | DFU_SECTOR_CAP_ERASEABLE = 1 << 2, | |
62 | /*< private >*/ | |
63 | DFU_SECTOR_CAP_LAST | |
64 | } DfuSectorCap; | |
65 | ||
66 | guint32 dfu_sector_get_id (DfuSector *sector); | |
67 | guint32 dfu_sector_get_address (DfuSector *sector); | |
68 | guint32 dfu_sector_get_size (DfuSector *sector); | |
69 | guint32 dfu_sector_get_size_left (DfuSector *sector); | |
70 | gboolean dfu_sector_has_cap (DfuSector *sector, | |
71 | DfuSectorCap cap); | |
72 | gchar *dfu_sector_to_string (DfuSector *sector); | |
73 | ||
74 | G_END_DECLS | |
75 | ||
76 | #endif /* __DFU_SECTOR_H */ |
0 | /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- | |
1 | * | |
2 | * Copyright (C) 2015 Richard Hughes <richard@hughsie.com> | |
3 | * | |
4 | * Licensed under the GNU General Public License Version 2 | |
5 | * | |
6 | * This program is free software; you can redistribute it and/or modify | |
7 | * it under the terms of the GNU General Public License as published by | |
8 | * the Free Software Foundation; either version 2 of the License, or | |
9 | * (at your option) any later version. | |
10 | * | |
11 | * This program is distributed in the hope that it will be useful, | |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
14 | * GNU General Public License for more details. | |
15 | * | |
16 | * You should have received a copy of the GNU General Public License | |
17 | * along with this program; if not, write to the Free Software | |
18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | |
19 | */ | |
20 | ||
21 | #include "config.h" | |
22 | ||
23 | #include <glib-object.h> | |
24 | #include <stdlib.h> | |
25 | ||
26 | #include "dfu-common.h" | |
27 | #include "dfu-context.h" | |
28 | #include "dfu-device.h" | |
29 | #include "dfu-error.h" | |
30 | #include "dfu-firmware.h" | |
31 | #include "dfu-sector-private.h" | |
32 | #include "dfu-target-private.h" | |
33 | ||
34 | /** | |
35 | * dfu_test_get_filename: | |
36 | **/ | |
37 | static gchar * | |
38 | dfu_test_get_filename (const gchar *filename) | |
39 | { | |
40 | gchar *tmp; | |
41 | char full_tmp[PATH_MAX]; | |
42 | g_autofree gchar *path = NULL; | |
43 | path = g_build_filename (TESTDATADIR, filename, NULL); | |
44 | tmp = realpath (path, full_tmp); | |
45 | if (tmp == NULL) | |
46 | return NULL; | |
47 | return g_strdup (full_tmp); | |
48 | } | |
49 | ||
50 | /** | |
51 | * _g_bytes_compare_verbose: | |
52 | **/ | |
53 | static gchar * | |
54 | _g_bytes_compare_verbose (GBytes *bytes1, GBytes *bytes2) | |
55 | { | |
56 | const guint8 *data1; | |
57 | const guint8 *data2; | |
58 | gsize length1; | |
59 | gsize length2; | |
60 | guint i; | |
61 | ||
62 | data1 = g_bytes_get_data (bytes1, &length1); | |
63 | data2 = g_bytes_get_data (bytes2, &length2); | |
64 | ||
65 | /* not the same length */ | |
66 | if (length1 != length2) { | |
67 | return g_strdup_printf ("got %" G_GSIZE_FORMAT " bytes, " | |
68 | "expected %" G_GSIZE_FORMAT, | |
69 | length1, length2); | |
70 | } | |
71 | ||
72 | /* return 00 01 02 03 */ | |
73 | for (i = 0; i < length1; i++) { | |
74 | if (data1[i] != data2[i]) { | |
75 | return g_strdup_printf ("got 0x%02x, expected 0x%02x @ 0x%04x", | |
76 | data1[i], data2[i], i); | |
77 | } | |
78 | } | |
79 | return NULL; | |
80 | } | |
81 | ||
82 | static void | |
83 | dfu_firmware_xdfu_func (void) | |
84 | { | |
85 | gboolean ret; | |
86 | g_autofree gchar *fn = NULL; | |
87 | g_autoptr(DfuFirmware) firmware = NULL; | |
88 | g_autoptr(GError) error = NULL; | |
89 | g_autoptr(GFile) file = NULL; | |
90 | ||
91 | fn = dfu_test_get_filename ("example.xdfu"); | |
92 | g_assert (fn != NULL); | |
93 | firmware = dfu_firmware_new (); | |
94 | file = g_file_new_for_path (fn); | |
95 | ret = dfu_firmware_parse_file (firmware, file, | |
96 | DFU_FIRMWARE_PARSE_FLAG_NONE, | |
97 | NULL, &error); | |
98 | g_assert_no_error (error); | |
99 | g_assert (ret); | |
100 | g_assert_cmpint (dfu_firmware_get_cipher_kind (firmware), ==, DFU_CIPHER_KIND_XTEA); | |
101 | } | |
102 | ||
103 | static void | |
104 | dfu_enums_func (void) | |
105 | { | |
106 | guint i; | |
107 | for (i = 0; i < DFU_STATE_LAST; i++) | |
108 | g_assert_cmpstr (dfu_state_to_string (i), !=, NULL); | |
109 | for (i = 0; i < DFU_STATUS_LAST; i++) | |
110 | g_assert_cmpstr (dfu_status_to_string (i), !=, NULL); | |
111 | } | |
112 | ||
113 | static GBytes * | |
114 | dfu_self_test_get_bytes_for_file (GFile *file, GError **error) | |
115 | { | |
116 | gchar *contents = NULL; | |
117 | gsize length = 0; | |
118 | if (!g_file_load_contents (file, NULL, &contents, &length, NULL, error)) | |
119 | return NULL; | |
120 | return g_bytes_new_take (contents, length); | |
121 | } | |
122 | ||
123 | static void | |
124 | dfu_firmware_raw_func (void) | |
125 | { | |
126 | DfuElement *element; | |
127 | DfuImage *image_tmp; | |
128 | GBytes *no_suffix_contents; | |
129 | gchar buf[256]; | |
130 | guint i; | |
131 | gboolean ret; | |
132 | g_autoptr(DfuFirmware) firmware = NULL; | |
133 | g_autoptr(GBytes) fw = NULL; | |
134 | g_autoptr(GBytes) roundtrip_orig = NULL; | |
135 | g_autoptr(GBytes) roundtrip = NULL; | |
136 | g_autoptr(GError) error = NULL; | |
137 | ||
138 | /* set up some dummy data */ | |
139 | for (i = 0; i < 256; i++) | |
140 | buf[i] = i; | |
141 | fw = g_bytes_new_static (buf, 256); | |
142 | ||
143 | /* load a non DFU firmware */ | |
144 | firmware = dfu_firmware_new (); | |
145 | ret = dfu_firmware_parse_data (firmware, fw, DFU_FIRMWARE_PARSE_FLAG_NONE, &error); | |
146 | g_assert_no_error (error); | |
147 | g_assert (ret); | |
148 | g_assert_cmpint (dfu_firmware_get_vid (firmware), ==, 0xffff); | |
149 | g_assert_cmpint (dfu_firmware_get_pid (firmware), ==, 0xffff); | |
150 | g_assert_cmpint (dfu_firmware_get_release (firmware), ==, 0xffff); | |
151 | g_assert_cmpint (dfu_firmware_get_format (firmware), ==, DFU_FIRMWARE_FORMAT_RAW); | |
152 | g_assert_cmpint (dfu_firmware_get_cipher_kind (firmware), ==, DFU_CIPHER_KIND_NONE); | |
153 | image_tmp = dfu_firmware_get_image (firmware, 0xfe); | |
154 | g_assert (image_tmp == NULL); | |
155 | image_tmp = dfu_firmware_get_image (firmware, 0); | |
156 | g_assert (image_tmp != NULL); | |
157 | g_assert_cmpint (dfu_image_get_size (image_tmp), ==, 256); | |
158 | element = dfu_image_get_element (image_tmp, 0); | |
159 | g_assert (element != NULL); | |
160 | no_suffix_contents = dfu_element_get_contents (element); | |
161 | g_assert (no_suffix_contents != NULL); | |
162 | g_assert_cmpint (g_bytes_compare (no_suffix_contents, fw), ==, 0); | |
163 | ||
164 | /* can we roundtrip without adding data */ | |
165 | roundtrip = dfu_firmware_write_data (firmware, &error); | |
166 | g_assert_no_error (error); | |
167 | g_assert (roundtrip != NULL); | |
168 | g_assert_cmpstr (_g_bytes_compare_verbose (roundtrip, fw), ==, NULL); | |
169 | } | |
170 | ||
171 | static void | |
172 | dfu_firmware_dfu_func (void) | |
173 | { | |
174 | gchar buf[256]; | |
175 | guint i; | |
176 | gboolean ret; | |
177 | g_autofree gchar *filename = NULL; | |
178 | g_autoptr(DfuFirmware) firmware = NULL; | |
179 | g_autoptr(DfuImage) image = NULL; | |
180 | g_autoptr(DfuElement) element = NULL; | |
181 | g_autoptr(GBytes) data = NULL; | |
182 | g_autoptr(GBytes) fw = NULL; | |
183 | g_autoptr(GBytes) roundtrip_orig = NULL; | |
184 | g_autoptr(GBytes) roundtrip = NULL; | |
185 | g_autoptr(GError) error = NULL; | |
186 | g_autoptr(GFile) file = NULL; | |
187 | ||
188 | /* set up some dummy data */ | |
189 | for (i = 0; i < 256; i++) | |
190 | buf[i] = i; | |
191 | fw = g_bytes_new_static (buf, 256); | |
192 | ||
193 | /* write DFU format */ | |
194 | firmware = dfu_firmware_new (); | |
195 | dfu_firmware_set_format (firmware, DFU_FIRMWARE_FORMAT_DFU_1_0); | |
196 | dfu_firmware_set_vid (firmware, 0x1234); | |
197 | dfu_firmware_set_pid (firmware, 0x5678); | |
198 | dfu_firmware_set_release (firmware, 0xfedc); | |
199 | image = dfu_image_new (); | |
200 | element = dfu_element_new (); | |
201 | dfu_element_set_contents (element, fw); | |
202 | dfu_image_add_element (image, element); | |
203 | dfu_firmware_add_image (firmware, image); | |
204 | g_assert_cmpint (dfu_firmware_get_size (firmware), ==, 256); | |
205 | data = dfu_firmware_write_data (firmware, &error); | |
206 | g_assert_no_error (error); | |
207 | g_assert (data != NULL); | |
208 | ||
209 | /* can we load it again? */ | |
210 | g_ptr_array_set_size (dfu_firmware_get_images (firmware), 0); | |
211 | ret = dfu_firmware_parse_data (firmware, data, DFU_FIRMWARE_PARSE_FLAG_NONE, &error); | |
212 | g_assert_no_error (error); | |
213 | g_assert (ret); | |
214 | g_assert_cmpint (dfu_firmware_get_vid (firmware), ==, 0x1234); | |
215 | g_assert_cmpint (dfu_firmware_get_pid (firmware), ==, 0x5678); | |
216 | g_assert_cmpint (dfu_firmware_get_release (firmware), ==, 0xfedc); | |
217 | g_assert_cmpint (dfu_firmware_get_format (firmware), ==, DFU_FIRMWARE_FORMAT_DFU_1_0); | |
218 | g_assert_cmpint (dfu_firmware_get_size (firmware), ==, 256); | |
219 | ||
220 | /* load a real firmware */ | |
221 | filename = dfu_test_get_filename ("kiibohd.dfu.bin"); | |
222 | g_assert (filename != NULL); | |
223 | file = g_file_new_for_path (filename); | |
224 | g_ptr_array_set_size (dfu_firmware_get_images (firmware), 0); | |
225 | ret = dfu_firmware_parse_file (firmware, file, | |
226 | DFU_FIRMWARE_PARSE_FLAG_NONE, | |
227 | NULL, &error); | |
228 | g_assert_no_error (error); | |
229 | g_assert (ret); | |
230 | g_assert_cmpint (dfu_firmware_get_vid (firmware), ==, 0x1c11); | |
231 | g_assert_cmpint (dfu_firmware_get_pid (firmware), ==, 0xb007); | |
232 | g_assert_cmpint (dfu_firmware_get_release (firmware), ==, 0xffff); | |
233 | g_assert_cmpint (dfu_firmware_get_format (firmware), ==, DFU_FIRMWARE_FORMAT_DFU_1_0); | |
234 | g_assert_cmpint (dfu_firmware_get_size (firmware), ==, 0x8eB4); | |
235 | g_assert_cmpint (dfu_firmware_get_cipher_kind (firmware), ==, DFU_CIPHER_KIND_NONE); | |
236 | ||
237 | /* can we roundtrip without loosing data */ | |
238 | roundtrip_orig = dfu_self_test_get_bytes_for_file (file, &error); | |
239 | g_assert_no_error (error); | |
240 | g_assert (roundtrip_orig != NULL); | |
241 | roundtrip = dfu_firmware_write_data (firmware, &error); | |
242 | g_assert_no_error (error); | |
243 | g_assert (roundtrip != NULL); | |
244 | g_assert_cmpstr (_g_bytes_compare_verbose (roundtrip, roundtrip_orig), ==, NULL); | |
245 | } | |
246 | ||
247 | static void | |
248 | dfu_firmware_dfuse_func (void) | |
249 | { | |
250 | gboolean ret; | |
251 | g_autofree gchar *filename = NULL; | |
252 | g_autoptr(DfuFirmware) firmware = NULL; | |
253 | g_autoptr(GBytes) roundtrip_orig = NULL; | |
254 | g_autoptr(GBytes) roundtrip = NULL; | |
255 | g_autoptr(GError) error = NULL; | |
256 | g_autoptr(GFile) file = NULL; | |
257 | ||
258 | /* load a DeFUse firmware */ | |
259 | filename = dfu_test_get_filename ("dev_VRBRAIN.dfu"); | |
260 | g_assert (filename != NULL); | |
261 | file = g_file_new_for_path (filename); | |
262 | firmware = dfu_firmware_new (); | |
263 | ret = dfu_firmware_parse_file (firmware, file, | |
264 | DFU_FIRMWARE_PARSE_FLAG_NONE, | |
265 | NULL, &error); | |
266 | g_assert_no_error (error); | |
267 | g_assert (ret); | |
268 | g_assert_cmpint (dfu_firmware_get_vid (firmware), ==, 0x0483); | |
269 | g_assert_cmpint (dfu_firmware_get_pid (firmware), ==, 0x0000); | |
270 | g_assert_cmpint (dfu_firmware_get_release (firmware), ==, 0x0000); | |
271 | g_assert_cmpint (dfu_firmware_get_format (firmware), ==, DFU_FIRMWARE_FORMAT_DFUSE); | |
272 | g_assert_cmpint (dfu_firmware_get_size (firmware), ==, 0x168d5); | |
273 | g_assert_cmpint (dfu_firmware_get_cipher_kind (firmware), ==, DFU_CIPHER_KIND_NONE); | |
274 | ||
275 | /* can we roundtrip without loosing data */ | |
276 | roundtrip_orig = dfu_self_test_get_bytes_for_file (file, &error); | |
277 | g_assert_no_error (error); | |
278 | g_assert (roundtrip_orig != NULL); | |
279 | roundtrip = dfu_firmware_write_data (firmware, &error); | |
280 | g_assert_no_error (error); | |
281 | g_assert (roundtrip != NULL); | |
282 | ||
283 | // g_file_set_contents ("/tmp/1.bin", | |
284 | // g_bytes_get_data (roundtrip, NULL), | |
285 | // g_bytes_get_size (roundtrip), NULL); | |
286 | ||
287 | g_assert_cmpstr (_g_bytes_compare_verbose (roundtrip, roundtrip_orig), ==, NULL); | |
288 | } | |
289 | ||
290 | static void | |
291 | dfu_firmware_metadata_func (void) | |
292 | { | |
293 | gboolean ret; | |
294 | g_autofree gchar *filename = NULL; | |
295 | g_autoptr(DfuFirmware) firmware = NULL; | |
296 | g_autoptr(GBytes) roundtrip_orig = NULL; | |
297 | g_autoptr(GBytes) roundtrip = NULL; | |
298 | g_autoptr(GError) error = NULL; | |
299 | g_autoptr(GFile) file = NULL; | |
300 | ||
301 | /* load a DFU firmware with a metadata table */ | |
302 | filename = dfu_test_get_filename ("metadata.dfu"); | |
303 | g_assert (filename != NULL); | |
304 | file = g_file_new_for_path (filename); | |
305 | firmware = dfu_firmware_new (); | |
306 | ret = dfu_firmware_parse_file (firmware, file, | |
307 | DFU_FIRMWARE_PARSE_FLAG_NONE, | |
308 | NULL, &error); | |
309 | g_assert_no_error (error); | |
310 | g_assert (ret); | |
311 | g_assert_cmpint (dfu_firmware_get_size (firmware), ==, 6); | |
312 | g_assert_cmpstr (dfu_firmware_get_metadata (firmware, "key"), ==, "value"); | |
313 | g_assert_cmpstr (dfu_firmware_get_metadata (firmware, "???"), ==, NULL); | |
314 | ||
315 | /* can we roundtrip without loosing data */ | |
316 | roundtrip_orig = dfu_self_test_get_bytes_for_file (file, &error); | |
317 | g_assert_no_error (error); | |
318 | g_assert (roundtrip_orig != NULL); | |
319 | roundtrip = dfu_firmware_write_data (firmware, &error); | |
320 | g_assert_no_error (error); | |
321 | g_assert (roundtrip != NULL); | |
322 | ||
323 | g_assert_cmpstr (_g_bytes_compare_verbose (roundtrip, roundtrip_orig), ==, NULL); | |
324 | } | |
325 | ||
326 | static void | |
327 | dfu_firmware_intel_hex_func (void) | |
328 | { | |
329 | const guint8 *data; | |
330 | gboolean ret; | |
331 | gsize len; | |
332 | g_autofree gchar *filename_hex = NULL; | |
333 | g_autofree gchar *filename_ref = NULL; | |
334 | g_autofree gchar *str = NULL; | |
335 | g_autoptr(DfuFirmware) firmware = NULL; | |
336 | g_autoptr(GBytes) data_bin2 = NULL; | |
337 | g_autoptr(GBytes) data_bin = NULL; | |
338 | g_autoptr(GBytes) data_hex = NULL; | |
339 | g_autoptr(GBytes) data_ref = NULL; | |
340 | g_autoptr(GError) error = NULL; | |
341 | g_autoptr(GFile) file_bin = NULL; | |
342 | g_autoptr(GFile) file_hex = NULL; | |
343 | ||
344 | /* load a Intel hex32 file */ | |
345 | filename_hex = dfu_test_get_filename ("firmware.hex"); | |
346 | g_assert (filename_hex != NULL); | |
347 | file_hex = g_file_new_for_path (filename_hex); | |
348 | firmware = dfu_firmware_new (); | |
349 | ret = dfu_firmware_parse_file (firmware, file_hex, | |
350 | DFU_FIRMWARE_PARSE_FLAG_NONE, | |
351 | NULL, &error); | |
352 | g_assert_no_error (error); | |
353 | g_assert (ret); | |
354 | g_assert_cmpint (dfu_firmware_get_size (firmware), ==, 136); | |
355 | dfu_firmware_set_format (firmware, DFU_FIRMWARE_FORMAT_RAW); | |
356 | data_bin = dfu_firmware_write_data (firmware, &error); | |
357 | g_assert_no_error (error); | |
358 | g_assert (data_bin != NULL); | |
359 | ||
360 | /* did we match the reference file? */ | |
361 | filename_ref = dfu_test_get_filename ("firmware.bin"); | |
362 | g_assert (filename_ref != NULL); | |
363 | file_bin = g_file_new_for_path (filename_ref); | |
364 | data_ref = dfu_self_test_get_bytes_for_file (file_bin, &error); | |
365 | g_assert_no_error (error); | |
366 | g_assert (data_ref != NULL); | |
367 | g_assert_cmpstr (_g_bytes_compare_verbose (data_bin, data_ref), ==, NULL); | |
368 | ||
369 | /* export a ihex file (which will be slightly different due to | |
370 | * non-continous regions being expanded */ | |
371 | dfu_firmware_set_format (firmware, DFU_FIRMWARE_FORMAT_INTEL_HEX); | |
372 | data_hex = dfu_firmware_write_data (firmware, &error); | |
373 | g_assert_no_error (error); | |
374 | g_assert (data_hex != NULL); | |
375 | data = g_bytes_get_data (data_hex, &len); | |
376 | str = g_strndup ((const gchar *) data, len); | |
377 | g_assert_cmpstr (str, ==, | |
378 | ":104000003DEF20F000000000FACF01F0FBCF02F0AF\n" | |
379 | ":10401000E9CF03F0EACF04F0E1CF05F0E2CF06F005\n" | |
380 | ":10402000D9CF07F0DACF08F0F3CF09F0F4CF0AF021\n" | |
381 | ":10403000F6CF0BF0F7CF0CF0F8CF0DF0F5CF0EF044\n" | |
382 | ":104040000EC0F5FF0DC0F8FF0CC0F7FF0BC0F6FF45\n" | |
383 | ":104050000AC0F4FF09C0F3FF08C0DAFF07C0D9FF24\n" | |
384 | ":1040600006C0E2FF05C0E1FF04C0EAFF03C0E9FF0A\n" | |
385 | ":1040700002C0FBFF01C0FAFF11003FEF20F00001BB\n" | |
386 | ":0840800042EF20F03DEF20F037\n" | |
387 | ":00000001FF\n"); | |
388 | ||
389 | /* do we match the binary file again */ | |
390 | dfu_firmware_set_format (firmware, DFU_FIRMWARE_FORMAT_RAW); | |
391 | data_bin2 = dfu_firmware_write_data (firmware, &error); | |
392 | g_assert_no_error (error); | |
393 | g_assert (data_bin2 != NULL); | |
394 | g_assert_cmpstr (_g_bytes_compare_verbose (data_bin, data_bin2), ==, NULL); | |
395 | } | |
396 | ||
397 | static void | |
398 | dfu_device_func (void) | |
399 | { | |
400 | GPtrArray *targets; | |
401 | gboolean ret; | |
402 | g_autoptr(DfuDevice) device = NULL; | |
403 | g_autoptr(DfuTarget) target1 = NULL; | |
404 | g_autoptr(DfuTarget) target2 = NULL; | |
405 | g_autoptr(GError) error = NULL; | |
406 | g_autoptr(GUsbContext) usb_ctx = NULL; | |
407 | g_autoptr(GUsbDevice) usb_device = NULL; | |
408 | ||
409 | /* find any DFU in appIDLE mode */ | |
410 | usb_ctx = g_usb_context_new (&error); | |
411 | g_assert_no_error (error); | |
412 | g_assert (usb_ctx != NULL); | |
413 | g_usb_context_enumerate (usb_ctx); | |
414 | usb_device = g_usb_context_find_by_vid_pid (usb_ctx, | |
415 | 0x273f, | |
416 | 0x1005, | |
417 | &error); | |
418 | if (usb_device == NULL) | |
419 | return; | |
420 | g_assert_no_error (error); | |
421 | g_assert (usb_device != NULL); | |
422 | ||
423 | /* check it's DFU-capable */ | |
424 | device = dfu_device_new (usb_device); | |
425 | g_assert (device != NULL); | |
426 | ||
427 | /* get targets */ | |
428 | targets = dfu_device_get_targets (device); | |
429 | g_assert_cmpint (targets->len, ==, 2); | |
430 | ||
431 | /* get by ID */ | |
432 | target1 = dfu_device_get_target_by_alt_setting (device, 1, &error); | |
433 | g_assert_no_error (error); | |
434 | g_assert (target1 != NULL); | |
435 | ||
436 | /* ensure open */ | |
437 | ret = dfu_device_open (device, DFU_DEVICE_OPEN_FLAG_NONE, NULL, &error); | |
438 | g_assert_no_error (error); | |
439 | g_assert (ret); | |
440 | ||
441 | /* get by name */ | |
442 | target2 = dfu_device_get_target_by_alt_name (device, "sram", &error); | |
443 | g_assert_no_error (error); | |
444 | g_assert (target2 != NULL); | |
445 | ||
446 | /* close */ | |
447 | ret = dfu_device_close (device, &error); | |
448 | g_assert_no_error (error); | |
449 | g_assert (ret); | |
450 | } | |
451 | ||
452 | static void | |
453 | dfu_colorhug_plus_func (void) | |
454 | { | |
455 | GPtrArray *elements; | |
456 | gboolean ret; | |
457 | gboolean seen_app_idle = FALSE; | |
458 | g_autoptr(DfuContext) context = NULL; | |
459 | g_autoptr(DfuDevice) device = NULL; | |
460 | g_autoptr(DfuDevice) device2 = NULL; | |
461 | g_autoptr(DfuTarget) target = NULL; | |
462 | g_autoptr(DfuImage) image = NULL; | |
463 | g_autoptr(GError) error = NULL; | |
464 | ||
465 | /* create context */ | |
466 | context = dfu_context_new (); | |
467 | ret = dfu_context_enumerate (context, &error); | |
468 | g_assert_no_error (error); | |
469 | g_assert (ret); | |
470 | ||
471 | /* push appIDLE into dfuIDLE */ | |
472 | device2 = dfu_context_get_device_by_vid_pid (context, | |
473 | 0x273f, | |
474 | 0x1002, | |
475 | NULL); | |
476 | if (device2 != NULL) { | |
477 | ret = dfu_device_open (device2, DFU_DEVICE_OPEN_FLAG_NONE, NULL, &error); | |
478 | g_assert_no_error (error); | |
479 | g_assert (ret); | |
480 | ret = dfu_device_detach (device2, NULL, &error); | |
481 | g_assert_no_error (error); | |
482 | g_assert (ret); | |
483 | ||
484 | /* wait for it to come back as 273f:1005 */ | |
485 | ret = dfu_device_wait_for_replug (device2, 5000, NULL, &error); | |
486 | g_assert_no_error (error); | |
487 | g_assert (ret); | |
488 | ||
489 | /* close it */ | |
490 | ret = dfu_device_close (device2, &error); | |
491 | g_assert_no_error (error); | |
492 | g_assert (ret); | |
493 | } | |
494 | ||
495 | /* find any DFU in dfuIDLE mode */ | |
496 | device = dfu_context_get_device_by_vid_pid (context, | |
497 | 0x273f, | |
498 | 0x1003, | |
499 | NULL); | |
500 | if (device == NULL) | |
501 | return; | |
502 | ||
503 | /* we don't know this unless we went from appIDLE -> dfuIDLE */ | |
504 | if (device2 == NULL) { | |
505 | g_assert_cmpint (dfu_device_get_runtime_vid (device), ==, 0xffff); | |
506 | g_assert_cmpint (dfu_device_get_runtime_pid (device), ==, 0xffff); | |
507 | } | |
508 | ||
509 | /* open it */ | |
510 | ret = dfu_device_open (device, DFU_DEVICE_OPEN_FLAG_NONE, NULL, &error); | |
511 | g_assert_no_error (error); | |
512 | g_assert (ret); | |
513 | ||
514 | /* is in dfuIDLE mode */ | |
515 | g_assert_cmpstr (dfu_state_to_string (dfu_device_get_state (device)), ==, "dfuIDLE"); | |
516 | ||
517 | /* lets try and flash something inappropriate */ | |
518 | if (seen_app_idle) { | |
519 | g_autoptr(DfuFirmware) firmware = NULL; | |
520 | g_autoptr(GFile) file = NULL; | |
521 | g_autofree gchar *filename = NULL; | |
522 | ||
523 | filename = dfu_test_get_filename ("kiibohd.dfu.bin"); | |
524 | g_assert (filename != NULL); | |
525 | file = g_file_new_for_path (filename); | |
526 | firmware = dfu_firmware_new (); | |
527 | ret = dfu_firmware_parse_file (firmware, file, | |
528 | DFU_FIRMWARE_PARSE_FLAG_NONE, | |
529 | NULL, &error); | |
530 | g_assert_no_error (error); | |
531 | g_assert (ret); | |
532 | ret = dfu_device_download (device, firmware, | |
533 | DFU_TARGET_TRANSFER_FLAG_DETACH | | |
534 | DFU_TARGET_TRANSFER_FLAG_WAIT_RUNTIME, | |
535 | NULL, &error); | |
536 | g_assert_error (error, | |
537 | DFU_ERROR, | |
538 | DFU_ERROR_INTERNAL); | |
539 | g_assert (ret); | |
540 | g_clear_error (&error); | |
541 | } | |
542 | ||
543 | /* get a dump of the existing firmware */ | |
544 | target = dfu_device_get_target_by_alt_setting (device, 0, &error); | |
545 | g_assert_no_error (error); | |
546 | g_assert (target != NULL); | |
547 | image = dfu_target_upload (target, DFU_TARGET_TRANSFER_FLAG_NONE, | |
548 | NULL, &error); | |
549 | g_assert_no_error (error); | |
550 | g_assert (DFU_IS_IMAGE (image)); | |
551 | elements = dfu_image_get_elements (image); | |
552 | g_assert (elements != NULL); | |
553 | g_assert_cmpint (elements->len, ==, 1); | |
554 | ||
555 | /* download a new firmware */ | |
556 | ret = dfu_target_download (target, image, | |
557 | DFU_TARGET_TRANSFER_FLAG_VERIFY | | |
558 | DFU_TARGET_TRANSFER_FLAG_ATTACH, | |
559 | NULL, &error); | |
560 | g_assert_no_error (error); | |
561 | g_assert (ret); | |
562 | ||
563 | /* wait for it to come back as 273f:1004 */ | |
564 | ret = dfu_device_wait_for_replug (device, 5000, NULL, &error); | |
565 | g_assert_no_error (error); | |
566 | g_assert (ret); | |
567 | ||
568 | /* we should know now */ | |
569 | g_assert_cmpint (dfu_device_get_runtime_vid (device), ==, 0x273f); | |
570 | g_assert_cmpint (dfu_device_get_runtime_pid (device), ==, 0x1002); | |
571 | } | |
572 | ||
573 | /** | |
574 | * dfu_target_sectors_to_string: | |
575 | **/ | |
576 | static gchar * | |
577 | dfu_target_sectors_to_string (DfuTarget *target) | |
578 | { | |
579 | DfuSector *sector; | |
580 | GPtrArray *sectors; | |
581 | GString *str; | |
582 | guint i; | |
583 | ||
584 | str = g_string_new (""); | |
585 | sectors = dfu_target_get_sectors (target); | |
586 | for (i = 0; i < sectors->len; i++) { | |
587 | g_autofree gchar *tmp = NULL; | |
588 | sector = g_ptr_array_index (sectors, i); | |
589 | tmp = dfu_sector_to_string (sector); | |
590 | g_string_append_printf (str, "%s\n", tmp); | |
591 | } | |
592 | if (str->len > 0) | |
593 | g_string_truncate (str, str->len - 1); | |
594 | return g_string_free (str, FALSE); | |
595 | } | |
596 | ||
597 | static void | |
598 | dfu_target_dfuse_func (void) | |
599 | { | |
600 | gboolean ret; | |
601 | gchar *tmp; | |
602 | g_autoptr(DfuTarget) target = NULL; | |
603 | g_autoptr(GError) error = NULL; | |
604 | ||
605 | /* NULL */ | |
606 | target = g_object_new (DFU_TYPE_TARGET, NULL); | |
607 | ret = dfu_target_parse_sectors (target, NULL, &error); | |
608 | g_assert_no_error (error); | |
609 | g_assert (ret); | |
610 | tmp = dfu_target_sectors_to_string (target); | |
611 | g_assert_cmpstr (tmp, ==, ""); | |
612 | g_free (tmp); | |
613 | ||
614 | /* no addresses */ | |
615 | ret = dfu_target_parse_sectors (target, "@Flash3", &error); | |
616 | g_assert_no_error (error); | |
617 | g_assert (ret); | |
618 | tmp = dfu_target_sectors_to_string (target); | |
619 | g_assert_cmpstr (tmp, ==, ""); | |
620 | g_free (tmp); | |
621 | ||
622 | /* one sector, no space */ | |
623 | ret = dfu_target_parse_sectors (target, "@Internal Flash /0x08000000/2*001Ka", &error); | |
624 | g_assert_no_error (error); | |
625 | g_assert (ret); | |
626 | tmp = dfu_target_sectors_to_string (target); | |
627 | g_assert_cmpstr (tmp, ==, "Zone:0, Sec#:0, Addr:0x08000000, Size:0x0400, Caps:0x1\n" | |
628 | "Zone:0, Sec#:0, Addr:0x08000400, Size:0x0400, Caps:0x1"); | |
629 | g_free (tmp); | |
630 | ||
631 | /* multiple sectors */ | |
632 | ret = dfu_target_parse_sectors (target, "@Flash1 /0x08000000/2*001 Ka,4*001 Kg", &error); | |
633 | g_assert_no_error (error); | |
634 | g_assert (ret); | |
635 | tmp = dfu_target_sectors_to_string (target); | |
636 | g_assert_cmpstr (tmp, ==, "Zone:0, Sec#:0, Addr:0x08000000, Size:0x0400, Caps:0x1\n" | |
637 | "Zone:0, Sec#:0, Addr:0x08000400, Size:0x0400, Caps:0x1\n" | |
638 | "Zone:0, Sec#:1, Addr:0x08000000, Size:0x0400, Caps:0x7\n" | |
639 | "Zone:0, Sec#:1, Addr:0x08000400, Size:0x0400, Caps:0x7\n" | |
640 | "Zone:0, Sec#:1, Addr:0x08000800, Size:0x0400, Caps:0x7\n" | |
641 | "Zone:0, Sec#:1, Addr:0x08000c00, Size:0x0400, Caps:0x7"); | |
642 | g_free (tmp); | |
643 | ||
644 | /* non-contiguous */ | |
645 | ret = dfu_target_parse_sectors (target, "@Flash2 /0xF000/4*100Ba/0xE000/3*8Kg/0x80000/2*24Kg", &error); | |
646 | g_assert_no_error (error); | |
647 | g_assert (ret); | |
648 | tmp = dfu_target_sectors_to_string (target); | |
649 | g_assert_cmpstr (tmp, ==, "Zone:0, Sec#:0, Addr:0x0000f000, Size:0x0064, Caps:0x1\n" | |
650 | "Zone:0, Sec#:0, Addr:0x0000f064, Size:0x0064, Caps:0x1\n" | |
651 | "Zone:0, Sec#:0, Addr:0x0000f0c8, Size:0x0064, Caps:0x1\n" | |
652 | "Zone:0, Sec#:0, Addr:0x0000f12c, Size:0x0064, Caps:0x1\n" | |
653 | "Zone:1, Sec#:0, Addr:0x0000e000, Size:0x2000, Caps:0x7\n" | |
654 | "Zone:1, Sec#:0, Addr:0x00010000, Size:0x2000, Caps:0x7\n" | |
655 | "Zone:1, Sec#:0, Addr:0x00012000, Size:0x2000, Caps:0x7\n" | |
656 | "Zone:2, Sec#:0, Addr:0x00080000, Size:0x6000, Caps:0x7\n" | |
657 | "Zone:2, Sec#:0, Addr:0x00086000, Size:0x6000, Caps:0x7"); | |
658 | g_free (tmp); | |
659 | ||
660 | /* invalid */ | |
661 | ret = dfu_target_parse_sectors (target, "Flash", NULL); | |
662 | g_assert (ret); | |
663 | ret = dfu_target_parse_sectors (target, "@Internal Flash /0x08000000", NULL); | |
664 | g_assert (!ret); | |
665 | ret = dfu_target_parse_sectors (target, "@Internal Flash /0x08000000/12*001a", NULL); | |
666 | g_assert (!ret); | |
667 | ||
668 | /* indicate a cipher being used */ | |
669 | g_assert_cmpint (dfu_target_get_cipher_kind (target), ==, DFU_CIPHER_KIND_NONE); | |
670 | ret = dfu_target_parse_sectors (target, "@Flash|XTEA", &error); | |
671 | g_assert_no_error (error); | |
672 | g_assert (ret); | |
673 | g_assert_cmpint (dfu_target_get_cipher_kind (target), ==, DFU_CIPHER_KIND_XTEA); | |
674 | } | |
675 | ||
676 | int | |
677 | main (int argc, char **argv) | |
678 | { | |
679 | g_test_init (&argc, &argv, NULL); | |
680 | ||
681 | /* only critical and error are fatal */ | |
682 | g_log_set_fatal_mask (NULL, G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL); | |
683 | ||
684 | /* log everything */ | |
685 | g_setenv ("G_MESSAGES_DEBUG", "all", FALSE); | |
686 | ||
687 | /* tests go here */ | |
688 | g_test_add_func ("/libdfu/enums", dfu_enums_func); | |
689 | g_test_add_func ("/libdfu/target(DfuSe}", dfu_target_dfuse_func); | |
690 | g_test_add_func ("/libdfu/firmware{raw}", dfu_firmware_raw_func); | |
691 | g_test_add_func ("/libdfu/firmware{dfu}", dfu_firmware_dfu_func); | |
692 | g_test_add_func ("/libdfu/firmware{dfuse}", dfu_firmware_dfuse_func); | |
693 | g_test_add_func ("/libdfu/firmware{xdfu}", dfu_firmware_xdfu_func); | |
694 | g_test_add_func ("/libdfu/firmware{metadata}", dfu_firmware_metadata_func); | |
695 | g_test_add_func ("/libdfu/firmware{intel-hex}", dfu_firmware_intel_hex_func); | |
696 | g_test_add_func ("/libdfu/device", dfu_device_func); | |
697 | g_test_add_func ("/libdfu/colorhug+", dfu_colorhug_plus_func); | |
698 | return g_test_run (); | |
699 | } | |
700 |
0 | /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- | |
1 | * | |
2 | * Copyright (C) 2015 Richard Hughes <richard@hughsie.com> | |
3 | * | |
4 | * Licensed under the GNU Lesser General Public License Version 2.1 | |
5 | * | |
6 | * This library is free software; you can redistribute it and/or | |
7 | * modify it under the terms of the GNU Lesser General Public | |
8 | * License as published by the Free Software Foundation; either | |
9 | * version 2.1 of the License, or (at your option) any later version. | |
10 | * | |
11 | * This library is distributed in the hope that it will be useful, | |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
14 | * Lesser General Public License for more details. | |
15 | * | |
16 | * You should have received a copy of the GNU Lesser General Public | |
17 | * License along with this library; if not, write to the Free Software | |
18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | |
19 | */ | |
20 | ||
21 | #ifndef __DFU_TARGET_PRIVATE_H | |
22 | #define __DFU_TARGET_PRIVATE_H | |
23 | ||
24 | #include <gusb.h> | |
25 | ||
26 | #include "dfu-device.h" | |
27 | #include "dfu-target.h" | |
28 | ||
29 | G_BEGIN_DECLS | |
30 | ||
31 | DfuTarget *dfu_target_new (DfuDevice *device, | |
32 | GUsbInterface *iface); | |
33 | ||
34 | GBytes *dfu_target_upload_chunk (DfuTarget *target, | |
35 | guint8 index, | |
36 | GCancellable *cancellable, | |
37 | GError **error); | |
38 | ||
39 | /* export this just for the self tests */ | |
40 | gboolean dfu_target_parse_sectors (DfuTarget *target, | |
41 | const gchar *alt_name, | |
42 | GError **error); | |
43 | ||
44 | G_END_DECLS | |
45 | ||
46 | #endif /* __DFU_TARGET_PRIVATE_H */ |
0 | /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- | |
1 | * | |
2 | * Copyright (C) 2015 Richard Hughes <richard@hughsie.com> | |
3 | * | |
4 | * Licensed under the GNU Lesser General Public License Version 2.1 | |
5 | * | |
6 | * This library is free software; you can redistribute it and/or | |
7 | * modify it under the terms of the GNU Lesser General Public | |
8 | * License as published by the Free Software Foundation; either | |
9 | * version 2.1 of the License, or (at your option) any later version. | |
10 | * | |
11 | * This library is distributed in the hope that it will be useful, | |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
14 | * Lesser General Public License for more details. | |
15 | * | |
16 | * You should have received a copy of the GNU Lesser General Public | |
17 | * License along with this library; if not, write to the Free Software | |
18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | |
19 | */ | |
20 | ||
21 | /** | |
22 | * SECTION:dfu-target | |
23 | * @short_description: Object representing a DFU-capable target | |
24 | * | |
25 | * This object allows uploading and downloading an image onto a | |
26 | * specific DFU-capable target. | |
27 | * | |
28 | * You only need to use this in preference to #DfuDevice if you only | |
29 | * want to update one target on the device. Most users will want to | |
30 | * update all the targets on the device at the same time. | |
31 | * | |
32 | * See also: #DfuDevice, #DfuImage | |
33 | */ | |
34 | ||
35 | #include "config.h" | |
36 | ||
37 | #include <string.h> | |
38 | #include <math.h> | |
39 | ||
40 | #include "dfu-common.h" | |
41 | #include "dfu-device-private.h" | |
42 | #include "dfu-error.h" | |
43 | #include "dfu-sector-private.h" | |
44 | #include "dfu-target-private.h" | |
45 | ||
46 | static void dfu_target_finalize (GObject *object); | |
47 | ||
48 | typedef enum { | |
49 | DFU_CMD_DFUSE_GET_COMMAND = 0x00, | |
50 | DFU_CMD_DFUSE_SET_ADDRESS_POINTER = 0x21, | |
51 | DFU_CMD_DFUSE_ERASE = 0x41, | |
52 | DFU_CMD_DFUSE_READ_UNPROTECT = 0x92, | |
53 | } DfuCmdDfuse; | |
54 | ||
55 | /** | |
56 | * DfuTargetPrivate: | |
57 | * | |
58 | * Private #DfuTarget data | |
59 | **/ | |
60 | typedef struct { | |
61 | DfuDevice *device; /* not refcounted */ | |
62 | DfuCipherKind cipher_kind; | |
63 | gboolean done_setup; | |
64 | guint8 alt_setting; | |
65 | guint8 alt_idx; | |
66 | gchar *alt_name; | |
67 | GPtrArray *sectors; /* of DfuSector */ | |
68 | GHashTable *sectors_erased; /* of DfuSector:1 */ | |
69 | } DfuTargetPrivate; | |
70 | ||
71 | enum { | |
72 | SIGNAL_PERCENTAGE_CHANGED, | |
73 | SIGNAL_LAST | |
74 | }; | |
75 | ||
76 | static guint signals [SIGNAL_LAST] = { 0 }; | |
77 | ||
78 | G_DEFINE_TYPE_WITH_PRIVATE (DfuTarget, dfu_target, G_TYPE_OBJECT) | |
79 | #define GET_PRIVATE(o) (dfu_target_get_instance_private (o)) | |
80 | ||
81 | /** | |
82 | * dfu_target_class_init: | |
83 | **/ | |
84 | static void | |
85 | dfu_target_class_init (DfuTargetClass *klass) | |
86 | { | |
87 | GObjectClass *object_class = G_OBJECT_CLASS (klass); | |
88 | ||
89 | /** | |
90 | * DfuTarget::percentage-changed: | |
91 | * @device: the #DfuTarget instance that emitted the signal | |
92 | * @percentage: the new percentage | |
93 | * | |
94 | * The ::percentage-changed signal is emitted when the percentage changes. | |
95 | * | |
96 | * Since: 0.5.4 | |
97 | **/ | |
98 | signals [SIGNAL_PERCENTAGE_CHANGED] = | |
99 | g_signal_new ("percentage-changed", | |
100 | G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, | |
101 | G_STRUCT_OFFSET (DfuTargetClass, percentage_changed), | |
102 | NULL, NULL, g_cclosure_marshal_VOID__UINT, | |
103 | G_TYPE_NONE, 1, G_TYPE_UINT); | |
104 | ||
105 | object_class->finalize = dfu_target_finalize; | |
106 | } | |
107 | ||
108 | /** | |
109 | * dfu_target_init: | |
110 | **/ | |
111 | static void | |
112 | dfu_target_init (DfuTarget *target) | |
113 | { | |
114 | DfuTargetPrivate *priv = GET_PRIVATE (target); | |
115 | priv->sectors = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref); | |
116 | priv->sectors_erased = g_hash_table_new (g_direct_hash, g_direct_equal); | |
117 | } | |
118 | ||
119 | /** | |
120 | * dfu_target_finalize: | |
121 | **/ | |
122 | static void | |
123 | dfu_target_finalize (GObject *object) | |
124 | { | |
125 | DfuTarget *target = DFU_TARGET (object); | |
126 | DfuTargetPrivate *priv = GET_PRIVATE (target); | |
127 | ||
128 | g_free (priv->alt_name); | |
129 | g_ptr_array_unref (priv->sectors); | |
130 | g_hash_table_unref (priv->sectors_erased); | |
131 | ||
132 | /* we no longer care */ | |
133 | if (priv->device != NULL) { | |
134 | g_object_remove_weak_pointer (G_OBJECT (priv->device), | |
135 | (gpointer *) &priv->device); | |
136 | } | |
137 | ||
138 | G_OBJECT_CLASS (dfu_target_parent_class)->finalize (object); | |
139 | } | |
140 | ||
141 | /** | |
142 | * dfu_target_sectors_to_string: | |
143 | **/ | |
144 | static gchar * | |
145 | dfu_target_sectors_to_string (DfuTarget *target) | |
146 | { | |
147 | DfuTargetPrivate *priv = GET_PRIVATE (target); | |
148 | DfuSector *sector; | |
149 | GString *str; | |
150 | guint i; | |
151 | ||
152 | str = g_string_new (""); | |
153 | for (i = 0; i < priv->sectors->len; i++) { | |
154 | g_autofree gchar *tmp = NULL; | |
155 | sector = g_ptr_array_index (priv->sectors, i); | |
156 | tmp = dfu_sector_to_string (sector); | |
157 | g_string_append_printf (str, "%s\n", tmp); | |
158 | } | |
159 | if (str->len > 0) | |
160 | g_string_truncate (str, str->len - 1); | |
161 | return g_string_free (str, FALSE); | |
162 | } | |
163 | ||
164 | /** | |
165 | * dfu_target_get_sector_for_addr: | |
166 | * | |
167 | * Returns: the sector that should be used for a specific address, or %NULL | |
168 | **/ | |
169 | static DfuSector * | |
170 | dfu_target_get_sector_for_addr (DfuTarget *target, guint32 addr) | |
171 | { | |
172 | DfuTargetPrivate *priv = GET_PRIVATE (target); | |
173 | DfuSector *sector; | |
174 | guint i; | |
175 | ||
176 | for (i = 0; i < priv->sectors->len; i++) { | |
177 | sector = g_ptr_array_index (priv->sectors, i); | |
178 | if (addr < dfu_sector_get_address (sector)) | |
179 | continue; | |
180 | if (addr > dfu_sector_get_address (sector) + | |
181 | dfu_sector_get_size (sector)) | |
182 | continue; | |
183 | return sector; | |
184 | } | |
185 | return NULL; | |
186 | } | |
187 | ||
188 | /** | |
189 | * dfu_target_parse_sector: | |
190 | * | |
191 | * Parse the DfuSe sector format according to UM0424 | |
192 | **/ | |
193 | static gboolean | |
194 | dfu_target_parse_sector (DfuTarget *target, | |
195 | const gchar *dfuse_sector_id, | |
196 | guint32 addr, | |
197 | guint zone, | |
198 | guint number, | |
199 | GError **error) | |
200 | { | |
201 | DfuTargetPrivate *priv = GET_PRIVATE (target); | |
202 | DfuSectorCap cap = DFU_SECTOR_CAP_NONE; | |
203 | gchar *tmp; | |
204 | guint32 addr_offset = 0; | |
205 | guint64 nr_sectors; | |
206 | guint64 sector_size; | |
207 | guint i; | |
208 | ||
209 | /* parse # of sectors */ | |
210 | nr_sectors = g_ascii_strtoull (dfuse_sector_id, &tmp, 10); | |
211 | if (nr_sectors > 999) { | |
212 | g_set_error (error, | |
213 | DFU_ERROR, | |
214 | DFU_ERROR_NOT_SUPPORTED, | |
215 | "Invalid number of sectors: %s", | |
216 | dfuse_sector_id); | |
217 | return FALSE; | |
218 | } | |
219 | ||
220 | /* check this is the delimiter */ | |
221 | if (tmp[0] != '*') { | |
222 | g_set_error (error, | |
223 | DFU_ERROR, | |
224 | DFU_ERROR_NOT_SUPPORTED, | |
225 | "Invalid sector ID: %s", | |
226 | dfuse_sector_id); | |
227 | return FALSE; | |
228 | } | |
229 | ||
230 | /* parse sector size */ | |
231 | sector_size = g_ascii_strtoull (tmp + 1, &tmp, 10); | |
232 | if (sector_size > 999) { | |
233 | g_set_error (error, | |
234 | DFU_ERROR, | |
235 | DFU_ERROR_NOT_SUPPORTED, | |
236 | "Invalid sector size: %s", | |
237 | dfuse_sector_id); | |
238 | return FALSE; | |
239 | } | |
240 | ||
241 | /* optional spaces */ | |
242 | while (tmp[0] == ' ') | |
243 | tmp++; | |
244 | ||
245 | /* get multiplier */ | |
246 | switch (tmp[0]) { | |
247 | case 'B': /* byte */ | |
248 | break; | |
249 | case 'K': /* Kilo */ | |
250 | sector_size *= 0x400; | |
251 | break; | |
252 | case 'M': /* Mega */ | |
253 | sector_size *= 0x100000 ; | |
254 | break; | |
255 | default: | |
256 | g_set_error (error, | |
257 | DFU_ERROR, | |
258 | DFU_ERROR_NOT_SUPPORTED, | |
259 | "Invalid sector multiplier: %s", | |
260 | tmp); | |
261 | return FALSE; | |
262 | } | |
263 | ||
264 | /* get sector type */ | |
265 | switch (tmp[1]) { | |
266 | case 'a': | |
267 | cap = DFU_SECTOR_CAP_READABLE; | |
268 | break; | |
269 | case 'b': | |
270 | cap = DFU_SECTOR_CAP_ERASEABLE; | |
271 | break; | |
272 | case 'c': | |
273 | cap = DFU_SECTOR_CAP_READABLE | | |
274 | DFU_SECTOR_CAP_ERASEABLE; | |
275 | break; | |
276 | case 'd': | |
277 | cap = DFU_SECTOR_CAP_WRITEABLE; | |
278 | break; | |
279 | case 'e': | |
280 | cap = DFU_SECTOR_CAP_READABLE | | |
281 | DFU_SECTOR_CAP_WRITEABLE; | |
282 | break; | |
283 | case 'f': | |
284 | cap = DFU_SECTOR_CAP_ERASEABLE | | |
285 | DFU_SECTOR_CAP_WRITEABLE; | |
286 | break; | |
287 | case 'g': | |
288 | cap = DFU_SECTOR_CAP_READABLE | | |
289 | DFU_SECTOR_CAP_ERASEABLE | | |
290 | DFU_SECTOR_CAP_WRITEABLE; | |
291 | break; | |
292 | default: | |
293 | g_set_error (error, | |
294 | DFU_ERROR, | |
295 | DFU_ERROR_NOT_SUPPORTED, | |
296 | "Invalid sector type: %s", | |
297 | tmp); | |
298 | return FALSE; | |
299 | } | |
300 | ||
301 | /* add all the sectors */ | |
302 | for (i = 0; i < nr_sectors; i++) { | |
303 | DfuSector *sector; | |
304 | sector = dfu_sector_new (addr + addr_offset, | |
305 | sector_size, | |
306 | (nr_sectors * sector_size)- addr_offset, | |
307 | zone, | |
308 | number, | |
309 | cap); | |
310 | g_ptr_array_add (priv->sectors, sector); | |
311 | addr_offset += dfu_sector_get_size (sector); | |
312 | } | |
313 | return TRUE; | |
314 | } | |
315 | ||
316 | /** | |
317 | * dfu_target_parse_sectors: (skip) | |
318 | * | |
319 | * Parse the DfuSe format according to UM0424 | |
320 | **/ | |
321 | gboolean | |
322 | dfu_target_parse_sectors (DfuTarget *target, const gchar *alt_name, GError **error) | |
323 | { | |
324 | DfuTargetPrivate *priv = GET_PRIVATE (target); | |
325 | guint64 addr; | |
326 | guint i; | |
327 | guint j; | |
328 | g_autofree gchar *str_debug = NULL; | |
329 | g_auto(GStrv) zones = NULL; | |
330 | ||
331 | /* not set */ | |
332 | if (alt_name == NULL) | |
333 | return TRUE; | |
334 | ||
335 | /* do we have any hint for the cipher */ | |
336 | if (g_strstr_len (alt_name, -1, "|XTEA") != NULL) | |
337 | priv->cipher_kind = DFU_CIPHER_KIND_XTEA; | |
338 | ||
339 | /* From the Neo Freerunner */ | |
340 | if (g_str_has_prefix (alt_name, "RAM 0x")) { | |
341 | DfuSector *sector; | |
342 | addr = g_ascii_strtoull (alt_name + 6, NULL, 16); | |
343 | if (addr == 0 && addr > G_MAXUINT32) | |
344 | return FALSE; | |
345 | g_debug ("RAM descripton, so parsing"); | |
346 | sector = dfu_sector_new (addr, /* addr */ | |
347 | 0x0, /* size */ | |
348 | 0x0, /* size_left */ | |
349 | 0x0, /* zone */ | |
350 | 0x0, /* number */ | |
351 | DFU_SECTOR_CAP_READABLE | | |
352 | DFU_SECTOR_CAP_WRITEABLE); | |
353 | g_ptr_array_add (priv->sectors, sector); | |
354 | } | |
355 | ||
356 | /* not a DfuSe alternative name */ | |
357 | if (alt_name[0] != '@') | |
358 | return TRUE; | |
359 | ||
360 | /* clear any existing zones */ | |
361 | g_ptr_array_set_size (priv->sectors, 0); | |
362 | ||
363 | /* parse zones */ | |
364 | zones = g_strsplit (alt_name, "/", -1); | |
365 | g_debug ("DfuSe nice alt-name: %s", g_strchomp (zones[0] + 1)); | |
366 | for (i = 1; zones[i] != NULL; i += 2) { | |
367 | g_auto(GStrv) sectors = NULL; | |
368 | ||
369 | /* parse address */ | |
370 | if (!g_str_has_prefix (zones[i], "0x")) | |
371 | return FALSE; | |
372 | addr = g_ascii_strtoull (zones[i] + 2, NULL, 16); | |
373 | if (addr > G_MAXUINT32) | |
374 | return FALSE; | |
375 | ||
376 | /* no sectors?! */ | |
377 | if (zones[i+1] == NULL) { | |
378 | g_set_error_literal (error, | |
379 | DFU_ERROR, | |
380 | DFU_ERROR_NOT_SUPPORTED, | |
381 | "No sector section"); | |
382 | return FALSE; | |
383 | } | |
384 | ||
385 | /* parse sectors */ | |
386 | sectors = g_strsplit (zones[i+1], ",", -1); | |
387 | for (j = 0; sectors[j] != NULL; j++) { | |
388 | if (!dfu_target_parse_sector (target, | |
389 | sectors[j], | |
390 | addr, | |
391 | (i - 1) / 2, j, | |
392 | error)) | |
393 | return FALSE; | |
394 | } | |
395 | } | |
396 | ||
397 | /* success */ | |
398 | str_debug = dfu_target_sectors_to_string (target); | |
399 | g_debug ("%s", str_debug); | |
400 | return TRUE; | |
401 | } | |
402 | ||
403 | /** | |
404 | * dfu_target_new: (skip) | |
405 | * @device: a #DfuDevice | |
406 | * @iface: a #GUsbInterface | |
407 | * | |
408 | * Creates a new DFU target, which represents an alt-setting on a | |
409 | * DFU-capable device. | |
410 | * | |
411 | * Return value: a #DfuTarget, or %NULL if @iface was not DFU-capable | |
412 | * | |
413 | * Since: 0.5.4 | |
414 | **/ | |
415 | DfuTarget * | |
416 | dfu_target_new (DfuDevice *device, GUsbInterface *iface) | |
417 | { | |
418 | DfuTargetPrivate *priv; | |
419 | DfuTarget *target; | |
420 | target = g_object_new (DFU_TYPE_TARGET, NULL); | |
421 | priv = GET_PRIVATE (target); | |
422 | priv->device = device; | |
423 | priv->alt_idx = g_usb_interface_get_index (iface); | |
424 | priv->alt_setting = g_usb_interface_get_alternate (iface); | |
425 | ||
426 | /* if we try to ref the target and destroy the device */ | |
427 | g_object_add_weak_pointer (G_OBJECT (priv->device), | |
428 | (gpointer *) &priv->device); | |
429 | ||
430 | return target; | |
431 | } | |
432 | ||
433 | /** | |
434 | * dfu_target_get_sectors: | |
435 | * @target: a #GUsbDevice | |
436 | * | |
437 | * Gets the sectors exported by the device. | |
438 | * | |
439 | * Return value: (transfer none) (element-type DfuSector): sectors | |
440 | * | |
441 | * Since: 0.5.4 | |
442 | **/ | |
443 | GPtrArray * | |
444 | dfu_target_get_sectors (DfuTarget *target) | |
445 | { | |
446 | DfuTargetPrivate *priv = GET_PRIVATE (target); | |
447 | g_return_val_if_fail (DFU_IS_TARGET (target), NULL); | |
448 | return priv->sectors; | |
449 | } | |
450 | ||
451 | /** | |
452 | * dfu_target_status_to_error_msg: | |
453 | * @status: a #DfuStatus, e.g. %DFU_STATUS_ERR_ERASE | |
454 | * | |
455 | * Converts an enumerated value to an error description. | |
456 | * | |
457 | * Return value: a string | |
458 | * | |
459 | * Since: 0.5.4 | |
460 | **/ | |
461 | static const gchar * | |
462 | dfu_target_status_to_error_msg (DfuStatus status) | |
463 | { | |
464 | if (status == DFU_STATUS_OK) | |
465 | return "No error condition is present"; | |
466 | if (status == DFU_STATUS_ERR_TARGET) | |
467 | return "Firmware is not for designed this device"; | |
468 | if (status == DFU_STATUS_ERR_FILE) | |
469 | return "Firmware is for this device but fails verification"; | |
470 | if (status == DFU_STATUS_ERR_WRITE) | |
471 | return "Device is unable to write memory"; | |
472 | if (status == DFU_STATUS_ERR_ERASE) | |
473 | return "Memory erase function failed"; | |
474 | if (status == DFU_STATUS_ERR_CHECK_ERASED) | |
475 | return "Memory erase check failed"; | |
476 | if (status == DFU_STATUS_ERR_PROG) | |
477 | return "Program memory function failed"; | |
478 | if (status == DFU_STATUS_ERR_VERIFY) | |
479 | return "Programmed memory failed verification"; | |
480 | if (status == DFU_STATUS_ERR_ADDRESS) | |
481 | return "Cannot program memory due to address out of range"; | |
482 | if (status == DFU_STATUS_ERR_NOTDONE) | |
483 | return "Received zero-length download but data is incomplete"; | |
484 | if (status == DFU_STATUS_ERR_FIRMWARE) | |
485 | return "Device firmware is corrupt"; | |
486 | if (status == DFU_STATUS_ERR_VENDOR) | |
487 | return "Vendor-specific error"; | |
488 | if (status == DFU_STATUS_ERR_USBR) | |
489 | return "Device detected unexpected USB reset signaling"; | |
490 | if (status == DFU_STATUS_ERR_POR) | |
491 | return "Device detected unexpected power on reset"; | |
492 | if (status == DFU_STATUS_ERR_UNKNOWN) | |
493 | return "Something unexpected went wrong"; | |
494 | if (status == DFU_STATUS_ERR_STALLDPKT) | |
495 | return "Device stalled an unexpected request"; | |
496 | return NULL; | |
497 | } | |
498 | ||
499 | /** | |
500 | * dfu_target_check_status: | |
501 | **/ | |
502 | static gboolean | |
503 | dfu_target_check_status (DfuTarget *target, | |
504 | GCancellable *cancellable, | |
505 | GError **error) | |
506 | { | |
507 | DfuTargetPrivate *priv = GET_PRIVATE (target); | |
508 | DfuStatus status; | |
509 | ||
510 | /* get the status */ | |
511 | if (!dfu_device_refresh (priv->device, cancellable, error)) | |
512 | return FALSE; | |
513 | ||
514 | /* not in an error state */ | |
515 | if (dfu_device_get_state (priv->device) != DFU_STATE_DFU_ERROR) | |
516 | return TRUE; | |
517 | ||
518 | /* DfuSe-specific long errors */ | |
519 | status = dfu_device_get_status (priv->device); | |
520 | if (dfu_device_has_dfuse_support (priv->device)) { | |
521 | if (status == DFU_STATUS_ERR_VENDOR) { | |
522 | g_set_error (error, | |
523 | DFU_ERROR, | |
524 | DFU_ERROR_NOT_SUPPORTED, | |
525 | "Read protection is active"); | |
526 | return FALSE; | |
527 | } | |
528 | if (status == DFU_STATUS_ERR_TARGET) { | |
529 | g_set_error (error, | |
530 | DFU_ERROR, | |
531 | DFU_ERROR_NOT_SUPPORTED, | |
532 | "Address is wrong or unsupported"); | |
533 | return FALSE; | |
534 | } | |
535 | } | |
536 | ||
537 | /* use a proper error description */ | |
538 | g_set_error_literal (error, | |
539 | DFU_ERROR, | |
540 | DFU_ERROR_NOT_SUPPORTED, | |
541 | dfu_target_status_to_error_msg (status)); | |
542 | return FALSE; | |
543 | } | |
544 | ||
545 | /** | |
546 | * dfu_target_use_alt_setting: | |
547 | * @target: a #DfuTarget | |
548 | * @error: a #GError, or %NULL | |
549 | * | |
550 | * Opens a DFU-capable target. | |
551 | * | |
552 | * Return value: %TRUE for success | |
553 | * | |
554 | * Since: 0.5.4 | |
555 | **/ | |
556 | static gboolean | |
557 | dfu_target_use_alt_setting (DfuTarget *target, GError **error) | |
558 | { | |
559 | DfuTargetPrivate *priv = GET_PRIVATE (target); | |
560 | GUsbDevice *dev; | |
561 | g_autoptr(GError) error_local = NULL; | |
562 | ||
563 | g_return_val_if_fail (DFU_IS_TARGET (target), FALSE); | |
564 | g_return_val_if_fail (error == NULL || *error == NULL, FALSE); | |
565 | ||
566 | /* use the correct setting */ | |
567 | dev = dfu_device_get_usb_dev (priv->device); | |
568 | if (dfu_device_get_mode (priv->device) == DFU_MODE_DFU) { | |
569 | if (!g_usb_device_set_interface_alt (dev, | |
570 | (gint) dfu_device_get_interface (priv->device), | |
571 | (gint) priv->alt_setting, | |
572 | &error_local)) { | |
573 | g_set_error (error, | |
574 | DFU_ERROR, | |
575 | DFU_ERROR_NOT_SUPPORTED, | |
576 | "cannot set alternate setting 0x%02x on interface %i: %s", | |
577 | priv->alt_setting, | |
578 | dfu_device_get_interface (priv->device), | |
579 | error_local->message); | |
580 | return FALSE; | |
581 | } | |
582 | } | |
583 | ||
584 | return TRUE; | |
585 | } | |
586 | ||
587 | /** | |
588 | * dfu_target_setup: | |
589 | * @target: a #DfuTarget | |
590 | * @error: a #GError, or %NULL | |
591 | * | |
592 | * Opens a DFU-capable target. | |
593 | * | |
594 | * Return value: %TRUE for success | |
595 | * | |
596 | * Since: 0.5.4 | |
597 | **/ | |
598 | static gboolean | |
599 | dfu_target_setup (DfuTarget *target, GError **error) | |
600 | { | |
601 | DfuTargetPrivate *priv = GET_PRIVATE (target); | |
602 | g_autoptr(GError) error_local = NULL; | |
603 | ||
604 | g_return_val_if_fail (DFU_IS_TARGET (target), FALSE); | |
605 | g_return_val_if_fail (error == NULL || *error == NULL, FALSE); | |
606 | ||
607 | /* already done */ | |
608 | if (priv->done_setup) | |
609 | return TRUE; | |
610 | ||
611 | /* get string */ | |
612 | if (priv->alt_idx != 0x00) { | |
613 | GUsbDevice *dev; | |
614 | dev = dfu_device_get_usb_dev (priv->device); | |
615 | priv->alt_name = | |
616 | g_usb_device_get_string_descriptor (dev, | |
617 | priv->alt_idx, | |
618 | NULL); | |
619 | } | |
620 | ||
621 | /* parse the DfuSe format according to UM0424 */ | |
622 | if (!dfu_target_parse_sectors (target, | |
623 | priv->alt_name, | |
624 | error)) | |
625 | return FALSE; | |
626 | ||
627 | /* add a dummy entry */ | |
628 | if (priv->sectors->len == 0) { | |
629 | DfuSector *sector; | |
630 | sector = dfu_sector_new (0x0, /* addr */ | |
631 | 0x0, /* size */ | |
632 | 0x0, /* size_left */ | |
633 | 0x0, /* zone */ | |
634 | 0x0, /* number */ | |
635 | DFU_SECTOR_CAP_READABLE | | |
636 | DFU_SECTOR_CAP_WRITEABLE); | |
637 | g_debug ("no UM0424 sector descripton in %s", priv->alt_name); | |
638 | g_ptr_array_add (priv->sectors, sector); | |
639 | } | |
640 | ||
641 | priv->done_setup = TRUE; | |
642 | return TRUE; | |
643 | } | |
644 | ||
645 | /** | |
646 | * dfu_target_download_chunk: | |
647 | **/ | |
648 | static gboolean | |
649 | dfu_target_download_chunk (DfuTarget *target, guint8 index, GBytes *bytes, | |
650 | GCancellable *cancellable, GError **error) | |
651 | { | |
652 | DfuTargetPrivate *priv = GET_PRIVATE (target); | |
653 | g_autoptr(GError) error_local = NULL; | |
654 | gsize actual_length; | |
655 | ||
656 | if (!g_usb_device_control_transfer (dfu_device_get_usb_dev (priv->device), | |
657 | G_USB_DEVICE_DIRECTION_HOST_TO_DEVICE, | |
658 | G_USB_DEVICE_REQUEST_TYPE_CLASS, | |
659 | G_USB_DEVICE_RECIPIENT_INTERFACE, | |
660 | DFU_REQUEST_DNLOAD, | |
661 | index, | |
662 | dfu_device_get_interface (priv->device), | |
663 | (guint8 *) g_bytes_get_data (bytes, NULL), | |
664 | g_bytes_get_size (bytes), | |
665 | &actual_length, | |
666 | dfu_device_get_timeout (priv->device), | |
667 | cancellable, | |
668 | &error_local)) { | |
669 | /* refresh the error code */ | |
670 | dfu_device_error_fixup (priv->device, cancellable, &error_local); | |
671 | g_set_error (error, | |
672 | DFU_ERROR, | |
673 | DFU_ERROR_NOT_SUPPORTED, | |
674 | "cannot download data: %s", | |
675 | error_local->message); | |
676 | return FALSE; | |
677 | } | |
678 | ||
679 | /* for ST devices, the action only occurs when we do GetStatus */ | |
680 | if (!dfu_target_check_status (target, cancellable, error)) | |
681 | return FALSE; | |
682 | ||
683 | g_assert (actual_length == g_bytes_get_size (bytes)); | |
684 | return TRUE; | |
685 | } | |
686 | ||
687 | /** | |
688 | * dfu_target_set_address: | |
689 | * @target: a #DfuTarget | |
690 | * @address: memory address | |
691 | * @cancellable: a #GCancellable, or %NULL | |
692 | * @error: a #GError, or %NULL | |
693 | * | |
694 | * Sets the address used for the next download or upload request. | |
695 | * | |
696 | * IMPORTANT: This only works on DfuSe-capable devices from ST. | |
697 | * | |
698 | * Return value: %TRUE for success | |
699 | * | |
700 | * Since: 0.5.4 | |
701 | **/ | |
702 | static gboolean | |
703 | dfu_target_set_address (DfuTarget *target, | |
704 | guint32 address, | |
705 | GCancellable *cancellable, | |
706 | GError **error) | |
707 | { | |
708 | DfuTargetPrivate *priv = GET_PRIVATE (target); | |
709 | GBytes *data_in; | |
710 | guint8 buf[5]; | |
711 | ||
712 | /* invalid */ | |
713 | if (!dfu_device_has_dfuse_support (priv->device)) { | |
714 | g_set_error_literal (error, | |
715 | DFU_ERROR, | |
716 | DFU_ERROR_NOT_SUPPORTED, | |
717 | "only supported for DfuSe targets"); | |
718 | return FALSE; | |
719 | } | |
720 | ||
721 | /* format buffer */ | |
722 | buf[0] = DFU_CMD_DFUSE_SET_ADDRESS_POINTER; | |
723 | memcpy (buf + 1, &address, 4); | |
724 | data_in = g_bytes_new_static (buf, sizeof(buf)); | |
725 | if (!dfu_target_download_chunk (target, 0, data_in, cancellable, error)) | |
726 | return FALSE; | |
727 | ||
728 | /* for ST devices, the action only occurs when we do GetStatus */ | |
729 | if (!dfu_target_check_status (target, cancellable, error)) | |
730 | return FALSE; | |
731 | return TRUE; | |
732 | } | |
733 | ||
734 | /** | |
735 | * dfu_target_erase_address: | |
736 | * @target: a #DfuTarget | |
737 | * @address: memory address | |
738 | * @cancellable: a #GCancellable, or %NULL | |
739 | * @error: a #GError, or %NULL | |
740 | * | |
741 | * Erases a memory sector at a given address. | |
742 | * | |
743 | * IMPORTANT: This only works on DfuSe-capable devices from ST. | |
744 | * | |
745 | * Return value: %TRUE for success | |
746 | * | |
747 | * Since: 0.5.4 | |
748 | **/ | |
749 | static gboolean | |
750 | dfu_target_erase_address (DfuTarget *target, | |
751 | guint32 address, | |
752 | GCancellable *cancellable, | |
753 | GError **error) | |
754 | { | |
755 | DfuTargetPrivate *priv = GET_PRIVATE (target); | |
756 | GBytes *data_in; | |
757 | guint8 buf[5]; | |
758 | ||
759 | /* invalid */ | |
760 | if (!dfu_device_has_dfuse_support (priv->device)) { | |
761 | g_set_error_literal (error, | |
762 | DFU_ERROR, | |
763 | DFU_ERROR_NOT_SUPPORTED, | |
764 | "only supported for DfuSe targets"); | |
765 | return FALSE; | |
766 | } | |
767 | ||
768 | /* format buffer */ | |
769 | buf[0] = DFU_CMD_DFUSE_ERASE; | |
770 | memcpy (buf + 1, &address, 4); | |
771 | data_in = g_bytes_new_static (buf, sizeof(buf)); | |
772 | if (!dfu_target_download_chunk (target, 0, data_in, cancellable, error)) | |
773 | return FALSE; | |
774 | ||
775 | /* for ST devices, the action only occurs when we do GetStatus */ | |
776 | if (!dfu_target_check_status (target, cancellable, error)) | |
777 | return FALSE; | |
778 | ||
779 | /* 2nd check required to get error code */ | |
780 | return dfu_target_check_status (target, cancellable, error); | |
781 | } | |
782 | ||
783 | #if 0 | |
784 | ||
785 | /** | |
786 | * dfu_target_mass_erase: | |
787 | * @target: a #DfuTarget | |
788 | * @cancellable: a #GCancellable, or %NULL | |
789 | * @error: a #GError, or %NULL | |
790 | * | |
791 | * Mass erases the device clearing all SRAM and EEPROM memory. | |
792 | * | |
793 | * This may not be supported on all devices, a better way of doing this action | |
794 | * is to enable read protection and then doing dfu_target_read_unprotect(). | |
795 | * | |
796 | * IMPORTANT: This only works on DfuSe-capable devices from ST. | |
797 | * | |
798 | * Return value: %TRUE for success | |
799 | * | |
800 | * Since: 0.5.4 | |
801 | **/ | |
802 | static gboolean | |
803 | dfu_target_mass_erase (DfuTarget *target, | |
804 | GCancellable *cancellable, | |
805 | GError **error) | |
806 | { | |
807 | DfuTargetPrivate *priv = GET_PRIVATE (target); | |
808 | GBytes *data_in; | |
809 | guint8 buf[1]; | |
810 | ||
811 | /* invalid */ | |
812 | if (!dfu_device_has_dfuse_support (priv->device)) { | |
813 | g_set_error_literal (error, | |
814 | DFU_ERROR, | |
815 | DFU_ERROR_NOT_SUPPORTED, | |
816 | "only supported for DfuSe targets"); | |
817 | return FALSE; | |
818 | } | |
819 | ||
820 | /* format buffer */ | |
821 | buf[0] = DFU_CMD_DFUSE_ERASE; | |
822 | data_in = g_bytes_new_static (buf, sizeof(buf)); | |
823 | if (!dfu_target_download_chunk (target, 0, data_in, cancellable, error)) | |
824 | return FALSE; | |
825 | ||
826 | /* for ST devices, the action only occurs when we do GetStatus */ | |
827 | if (!dfu_target_check_status (target, cancellable, error)) | |
828 | return FALSE; | |
829 | ||
830 | /* 2nd check required to get error code */ | |
831 | return dfu_target_check_status (target, cancellable, error); | |
832 | } | |
833 | ||
834 | /** | |
835 | * dfu_target_read_unprotect: | |
836 | * @target: a #DfuTarget | |
837 | * @cancellable: a #GCancellable, or %NULL | |
838 | * @error: a #GError, or %NULL | |
839 | * | |
840 | * Turns of read protection on the device, clearing all SRAM and EEPROM memory. | |
841 | * | |
842 | * IMPORTANT: This only works on DfuSe-capable devices from ST. | |
843 | * | |
844 | * Return value: %TRUE for success | |
845 | * | |
846 | * Since: 0.5.4 | |
847 | **/ | |
848 | static gboolean | |
849 | dfu_target_read_unprotect (DfuTarget *target, | |
850 | GCancellable *cancellable, | |
851 | GError **error) | |
852 | { | |
853 | DfuTargetPrivate *priv = GET_PRIVATE (target); | |
854 | GBytes *data_in; | |
855 | guint8 buf[5]; | |
856 | ||
857 | /* invalid */ | |
858 | if (!dfu_device_has_dfuse_support (priv->device)) { | |
859 | g_set_error_literal (error, | |
860 | DFU_ERROR, | |
861 | DFU_ERROR_NOT_SUPPORTED, | |
862 | "only supported for DfuSe targets"); | |
863 | return FALSE; | |
864 | } | |
865 | ||
866 | /* format buffer */ | |
867 | buf[0] = DFU_CMD_DFUSE_READ_UNPROTECT; | |
868 | memcpy (buf + 1, &address, 4); | |
869 | data_in = g_bytes_new_static (buf, sizeof(buf)); | |
870 | if (!dfu_target_download_chunk (target, 0, data_in, cancellable, error)) | |
871 | return FALSE; | |
872 | ||
873 | /* for ST devices, the action only occurs when we do GetStatus */ | |
874 | return dfu_target_check_status (target, cancellable, error); | |
875 | } | |
876 | ||
877 | #endif | |
878 | ||
879 | /** | |
880 | * dfu_target_upload_chunk: (skip) | |
881 | **/ | |
882 | GBytes * | |
883 | dfu_target_upload_chunk (DfuTarget *target, guint8 index, | |
884 | GCancellable *cancellable, GError **error) | |
885 | { | |
886 | DfuTargetPrivate *priv = GET_PRIVATE (target); | |
887 | g_autoptr(GError) error_local = NULL; | |
888 | guint8 *buf; | |
889 | gsize actual_length; | |
890 | guint16 transfer_size = dfu_device_get_transfer_size (priv->device); | |
891 | ||
892 | buf = g_new0 (guint8, transfer_size); | |
893 | if (!g_usb_device_control_transfer (dfu_device_get_usb_dev (priv->device), | |
894 | G_USB_DEVICE_DIRECTION_DEVICE_TO_HOST, | |
895 | G_USB_DEVICE_REQUEST_TYPE_CLASS, | |
896 | G_USB_DEVICE_RECIPIENT_INTERFACE, | |
897 | DFU_REQUEST_UPLOAD, | |
898 | index, | |
899 | dfu_device_get_interface (priv->device), | |
900 | buf, (gsize) transfer_size, | |
901 | &actual_length, | |
902 | dfu_device_get_timeout (priv->device), | |
903 | cancellable, | |
904 | &error_local)) { | |
905 | /* refresh the error code */ | |
906 | dfu_device_error_fixup (priv->device, cancellable, &error_local); | |
907 | g_set_error (error, | |
908 | DFU_ERROR, | |
909 | DFU_ERROR_NOT_SUPPORTED, | |
910 | "cannot upload data: %s", | |
911 | error_local->message); | |
912 | return NULL; | |
913 | } | |
914 | ||
915 | /* for ST devices, the action only occurs when we do GetStatus */ | |
916 | if (!dfu_device_has_quirk (priv->device, DFU_DEVICE_QUIRK_NO_GET_STATUS_UPLOAD)) { | |
917 | if (!dfu_target_check_status (target, cancellable, error)) | |
918 | return FALSE; | |
919 | } | |
920 | ||
921 | return g_bytes_new_take (buf, actual_length); | |
922 | } | |
923 | ||
924 | /** | |
925 | * dfu_target_upload_element: | |
926 | **/ | |
927 | static DfuElement * | |
928 | dfu_target_upload_element (DfuTarget *target, | |
929 | guint32 address, | |
930 | gsize expected_size, | |
931 | GCancellable *cancellable, | |
932 | GError **error) | |
933 | { | |
934 | DfuTargetPrivate *priv = GET_PRIVATE (target); | |
935 | DfuSector *sector; | |
936 | DfuElement *element = NULL; | |
937 | GBytes *chunk_tmp; | |
938 | gsize chunk_size; | |
939 | gsize offset = 0; | |
940 | gsize total_size = 0; | |
941 | guint16 transfer_size = dfu_device_get_transfer_size (priv->device); | |
942 | guint8 *buffer; | |
943 | guint32 last_sector_id = G_MAXUINT; | |
944 | guint dfuse_sector_offset = 0; | |
945 | guint i; | |
946 | guint old_percentage = G_MAXUINT; | |
947 | g_autoptr(GBytes) contents = NULL; | |
948 | g_autoptr(GPtrArray) chunks = NULL; | |
949 | ||
950 | /* ST uses wBlockNum=0 for DfuSe commands and wBlockNum=1 is reserved */ | |
951 | if (dfu_device_has_dfuse_support (priv->device)) { | |
952 | offset += address; | |
953 | dfuse_sector_offset = 2; | |
954 | } | |
955 | ||
956 | /* get all the chunks from the hardware */ | |
957 | chunks = g_ptr_array_new_with_free_func ((GDestroyNotify) g_bytes_unref); | |
958 | for (i = 0; i < 0xffff; i++) { | |
959 | ||
960 | /* for DfuSe devices we need to handle the address manually */ | |
961 | if (dfu_device_has_dfuse_support (priv->device)) { | |
962 | ||
963 | /* check the sector with this element address is suitable */ | |
964 | sector = dfu_target_get_sector_for_addr (target, offset); | |
965 | if (sector == NULL) { | |
966 | g_set_error (error, | |
967 | DFU_ERROR, | |
968 | DFU_ERROR_INVALID_DEVICE, | |
969 | "no memory sector at 0x%04x", | |
970 | (guint) offset); | |
971 | return FALSE; | |
972 | } | |
973 | if (!dfu_sector_has_cap (sector, DFU_SECTOR_CAP_READABLE)) { | |
974 | g_set_error (error, | |
975 | DFU_ERROR, | |
976 | DFU_ERROR_INVALID_DEVICE, | |
977 | "memory sector at 0x%04x is not readble", | |
978 | (guint) offset); | |
979 | return FALSE; | |
980 | } | |
981 | ||
982 | /* manually set the sector address */ | |
983 | if (dfu_sector_get_id (sector) != last_sector_id) { | |
984 | g_debug ("setting DfuSe address to 0x%04x", (guint) offset); | |
985 | if (!dfu_target_set_address (target, | |
986 | offset, | |
987 | cancellable, | |
988 | error)) | |
989 | return FALSE; | |
990 | last_sector_id = dfu_sector_get_id (sector); | |
991 | } | |
992 | } | |
993 | ||
994 | /* read chunk of data */ | |
995 | chunk_tmp = dfu_target_upload_chunk (target, | |
996 | i + dfuse_sector_offset, | |
997 | cancellable, | |
998 | error); | |
999 | if (chunk_tmp == NULL) | |
1000 | return NULL; | |
1001 | ||
1002 | /* keep a sum of all the chunks */ | |
1003 | chunk_size = g_bytes_get_size (chunk_tmp); | |
1004 | total_size += chunk_size; | |
1005 | offset += chunk_size; | |
1006 | ||
1007 | /* add to array */ | |
1008 | g_debug ("got #%04x chunk of size %" G_GSIZE_FORMAT, | |
1009 | i, chunk_size); | |
1010 | g_ptr_array_add (chunks, chunk_tmp); | |
1011 | ||
1012 | /* update UI */ | |
1013 | if (chunk_size > 0) { | |
1014 | guint percentage = (total_size * 100) / expected_size; | |
1015 | if (percentage != old_percentage) { | |
1016 | g_signal_emit (target, | |
1017 | signals[SIGNAL_PERCENTAGE_CHANGED], | |
1018 | 0, percentage); | |
1019 | } | |
1020 | } | |
1021 | ||
1022 | /* detect short write as EOF */ | |
1023 | if (chunk_size < transfer_size) | |
1024 | break; | |
1025 | } | |
1026 | ||
1027 | /* check final size */ | |
1028 | if (expected_size > 0) { | |
1029 | if (total_size != expected_size) { | |
1030 | g_set_error (error, | |
1031 | DFU_ERROR, | |
1032 | DFU_ERROR_INVALID_FILE, | |
1033 | "invalid size, got %" G_GSIZE_FORMAT ", " | |
1034 | "expected %" G_GSIZE_FORMAT , | |
1035 | total_size, expected_size); | |
1036 | return NULL; | |
1037 | } | |
1038 | } | |
1039 | ||
1040 | /* stitch them all together */ | |
1041 | offset = 0; | |
1042 | buffer = g_malloc0 (total_size); | |
1043 | for (i = 0; i < chunks->len; i++) { | |
1044 | const guint8 *chunk_data; | |
1045 | chunk_tmp = g_ptr_array_index (chunks, i); | |
1046 | chunk_data = g_bytes_get_data (chunk_tmp, &chunk_size); | |
1047 | memcpy (buffer + offset, chunk_data, chunk_size); | |
1048 | offset += chunk_size; | |
1049 | } | |
1050 | ||
1051 | /* create new image */ | |
1052 | contents = g_bytes_new_take (buffer, total_size); | |
1053 | element = dfu_element_new (); | |
1054 | dfu_element_set_contents (element, contents); | |
1055 | return element; | |
1056 | } | |
1057 | ||
1058 | /** | |
1059 | * dfu_target_upload: | |
1060 | * @target: a #DfuTarget | |
1061 | * @flags: flags to use, e.g. %DFU_TARGET_TRANSFER_FLAG_VERIFY | |
1062 | * @cancellable: a #GCancellable, or %NULL | |
1063 | * @error: a #GError, or %NULL | |
1064 | * | |
1065 | * Uploads firmware from the target to the host. | |
1066 | * | |
1067 | * Return value: (transfer full): the uploaded image, or %NULL for error | |
1068 | * | |
1069 | * Since: 0.5.4 | |
1070 | **/ | |
1071 | DfuImage * | |
1072 | dfu_target_upload (DfuTarget *target, | |
1073 | DfuTargetTransferFlags flags, | |
1074 | GCancellable *cancellable, | |
1075 | GError **error) | |
1076 | { | |
1077 | DfuTargetPrivate *priv = GET_PRIVATE (target); | |
1078 | DfuSector *sector; | |
1079 | guint i; | |
1080 | guint32 last_sector_id = G_MAXUINT; | |
1081 | g_autoptr(DfuImage) image = NULL; | |
1082 | ||
1083 | g_return_val_if_fail (DFU_IS_TARGET (target), NULL); | |
1084 | g_return_val_if_fail (error == NULL || *error == NULL, NULL); | |
1085 | ||
1086 | /* ensure populated */ | |
1087 | if (!dfu_target_setup (target, error)) | |
1088 | return NULL; | |
1089 | ||
1090 | /* can the target do this? */ | |
1091 | if (!dfu_device_can_upload (priv->device)) { | |
1092 | g_set_error_literal (error, | |
1093 | DFU_ERROR, | |
1094 | DFU_ERROR_NOT_SUPPORTED, | |
1095 | "target cannot do uploading"); | |
1096 | return NULL; | |
1097 | } | |
1098 | ||
1099 | /* use correct alt */ | |
1100 | if (!dfu_target_use_alt_setting (target, error)) | |
1101 | return NULL; | |
1102 | ||
1103 | /* no open?! */ | |
1104 | if (priv->sectors->len == 0) { | |
1105 | g_set_error_literal (error, | |
1106 | DFU_ERROR, | |
1107 | DFU_ERROR_NOT_SUPPORTED, | |
1108 | "no sectors defined for target"); | |
1109 | return NULL; | |
1110 | } | |
1111 | ||
1112 | /* create a new image */ | |
1113 | image = dfu_image_new (); | |
1114 | dfu_image_set_name (image, priv->alt_name); | |
1115 | dfu_image_set_alt_setting (image, priv->alt_setting); | |
1116 | ||
1117 | /* get all the sectors for the device */ | |
1118 | for (i = 0; i < priv->sectors->len; i++) { | |
1119 | g_autoptr(DfuElement) element = NULL; | |
1120 | ||
1121 | /* only upload to the start of any zone:sector */ | |
1122 | sector = g_ptr_array_index (priv->sectors, i); | |
1123 | if (dfu_sector_get_id (sector) == last_sector_id) | |
1124 | continue; | |
1125 | ||
1126 | /* get the first element from the hardware */ | |
1127 | g_debug ("starting upload from 0x%08x (0x%04x)", | |
1128 | dfu_sector_get_address (sector), | |
1129 | dfu_sector_get_size_left (sector)); | |
1130 | element = dfu_target_upload_element (target, | |
1131 | dfu_sector_get_address (sector), | |
1132 | dfu_sector_get_size_left (sector), | |
1133 | cancellable, | |
1134 | error); | |
1135 | if (element == NULL) | |
1136 | return NULL; | |
1137 | ||
1138 | /* this element was uploaded okay */ | |
1139 | dfu_image_add_element (image, element); | |
1140 | ||
1141 | /* ignore sectors until one of these changes */ | |
1142 | last_sector_id = dfu_sector_get_id (sector); | |
1143 | } | |
1144 | ||
1145 | /* do host reset */ | |
1146 | if ((flags & DFU_TARGET_TRANSFER_FLAG_ATTACH) > 0 || | |
1147 | (flags & DFU_TARGET_TRANSFER_FLAG_WAIT_RUNTIME) > 0) { | |
1148 | if (!dfu_device_attach (priv->device, error)) | |
1149 | return NULL; | |
1150 | } | |
1151 | ||
1152 | /* boot to runtime */ | |
1153 | if (flags & DFU_TARGET_TRANSFER_FLAG_WAIT_RUNTIME) { | |
1154 | g_debug ("booting to runtime"); | |
1155 | if (!dfu_device_wait_for_replug (priv->device, | |
1156 | DFU_DEVICE_REPLUG_TIMEOUT, | |
1157 | cancellable, | |
1158 | error)) | |
1159 | return NULL; | |
1160 | } | |
1161 | ||
1162 | /* success */ | |
1163 | return g_object_ref (image); | |
1164 | } | |
1165 | ||
1166 | /** | |
1167 | * _g_bytes_compare_verbose: | |
1168 | **/ | |
1169 | static gchar * | |
1170 | _g_bytes_compare_verbose (GBytes *bytes1, GBytes *bytes2) | |
1171 | { | |
1172 | const guint8 *data1; | |
1173 | const guint8 *data2; | |
1174 | gsize length1; | |
1175 | gsize length2; | |
1176 | guint i; | |
1177 | ||
1178 | data1 = g_bytes_get_data (bytes1, &length1); | |
1179 | data2 = g_bytes_get_data (bytes2, &length2); | |
1180 | ||
1181 | /* not the same length */ | |
1182 | if (length1 != length2) { | |
1183 | return g_strdup_printf ("got %" G_GSIZE_FORMAT " bytes, " | |
1184 | "expected %" G_GSIZE_FORMAT, | |
1185 | length1, length2); | |
1186 | } | |
1187 | ||
1188 | /* return 00 01 02 03 */ | |
1189 | for (i = 0; i < length1; i++) { | |
1190 | if (data1[i] != data2[i]) { | |
1191 | return g_strdup_printf ("got 0x%02x, expected 0x%02x @ 0x%04x", | |
1192 | data1[i], data2[i], i); | |
1193 | } | |
1194 | } | |
1195 | return NULL; | |
1196 | } | |
1197 | ||
1198 | /** | |
1199 | * dfu_target_download_element: | |
1200 | **/ | |
1201 | static gboolean | |
1202 | dfu_target_download_element (DfuTarget *target, | |
1203 | DfuElement *element, | |
1204 | DfuTargetTransferFlags flags, | |
1205 | GCancellable *cancellable, | |
1206 | GError **error) | |
1207 | { | |
1208 | DfuTargetPrivate *priv = GET_PRIVATE (target); | |
1209 | DfuSector *sector; | |
1210 | GBytes *bytes; | |
1211 | guint i; | |
1212 | guint nr_chunks; | |
1213 | guint dfuse_sector_offset = 0; | |
1214 | guint last_sector_id = G_MAXUINT; | |
1215 | guint old_percentage = G_MAXUINT; | |
1216 | guint16 transfer_size = dfu_device_get_transfer_size (priv->device); | |
1217 | g_autoptr(GError) error_local = NULL; | |
1218 | ||
1219 | /* ST uses wBlockNum=0 for DfuSe commands and wBlockNum=1 is reserved */ | |
1220 | if (dfu_device_has_dfuse_support (priv->device)) | |
1221 | dfuse_sector_offset = 2; | |
1222 | ||
1223 | /* round up as we have to transfer incomplete blocks */ | |
1224 | bytes = dfu_element_get_contents (element); | |
1225 | nr_chunks = ceil ((gdouble) g_bytes_get_size (bytes) / | |
1226 | (gdouble) transfer_size); | |
1227 | if (nr_chunks == 0) { | |
1228 | g_set_error_literal (error, | |
1229 | DFU_ERROR, | |
1230 | DFU_ERROR_INVALID_FILE, | |
1231 | "zero-length firmware"); | |
1232 | return FALSE; | |
1233 | } | |
1234 | for (i = 0; i < nr_chunks + 1; i++) { | |
1235 | gsize length; | |
1236 | gsize offset; | |
1237 | guint percentage; | |
1238 | g_autoptr(GBytes) bytes_tmp = NULL; | |
1239 | ||
1240 | /* caclulate the offset into the element data */ | |
1241 | offset = i * transfer_size; | |
1242 | ||
1243 | /* for DfuSe devices we need to handle the erase and setting | |
1244 | * the address manually */ | |
1245 | if (dfu_device_has_dfuse_support (priv->device)) { | |
1246 | ||
1247 | /* check the sector with this element address is suitable */ | |
1248 | sector = dfu_target_get_sector_for_addr (target, offset); | |
1249 | if (sector == NULL) { | |
1250 | g_set_error (error, | |
1251 | DFU_ERROR, | |
1252 | DFU_ERROR_INVALID_DEVICE, | |
1253 | "no memory sector at 0x%04x", | |
1254 | (guint) offset); | |
1255 | return FALSE; | |
1256 | } | |
1257 | if (!dfu_sector_has_cap (sector, DFU_SECTOR_CAP_WRITEABLE)) { | |
1258 | g_set_error (error, | |
1259 | DFU_ERROR, | |
1260 | DFU_ERROR_INVALID_DEVICE, | |
1261 | "memory sector at 0x%04x is not writable", | |
1262 | (guint) offset); | |
1263 | return FALSE; | |
1264 | } | |
1265 | ||
1266 | /* if it's erasable and not yet blanked */ | |
1267 | if (!dfu_sector_has_cap (sector, DFU_SECTOR_CAP_ERASEABLE) && | |
1268 | g_hash_table_lookup (priv->sectors_erased, sector) == NULL) { | |
1269 | g_debug ("erasing DfuSe address at 0x%04x", (guint) offset); | |
1270 | if (!dfu_target_erase_address (target, | |
1271 | offset, | |
1272 | cancellable, | |
1273 | error)) | |
1274 | return FALSE; | |
1275 | g_hash_table_insert (priv->sectors_erased, | |
1276 | sector, | |
1277 | GINT_TO_POINTER (1)); | |
1278 | } | |
1279 | ||
1280 | /* manually set the sector address */ | |
1281 | if (dfu_sector_get_id (sector) != last_sector_id) { | |
1282 | g_debug ("setting DfuSe address to 0x%04x", (guint) offset); | |
1283 | if (!dfu_target_set_address (target, | |
1284 | offset, | |
1285 | cancellable, | |
1286 | error)) | |
1287 | return FALSE; | |
1288 | last_sector_id = dfu_sector_get_id (sector); | |
1289 | } | |
1290 | } | |
1291 | ||
1292 | /* we have to write one final zero-sized chunk for EOF */ | |
1293 | if (i < nr_chunks) { | |
1294 | length = g_bytes_get_size (bytes) - offset; | |
1295 | if (length > transfer_size) | |
1296 | length = transfer_size; | |
1297 | bytes_tmp = g_bytes_new_from_bytes (bytes, offset, length); | |
1298 | } else { | |
1299 | bytes_tmp = g_bytes_new (NULL, 0); | |
1300 | } | |
1301 | g_debug ("writing #%04x chunk of size %" G_GSIZE_FORMAT, | |
1302 | i, g_bytes_get_size (bytes_tmp)); | |
1303 | if (!dfu_target_download_chunk (target, | |
1304 | i + dfuse_sector_offset, | |
1305 | bytes_tmp, | |
1306 | cancellable, | |
1307 | error)) | |
1308 | return FALSE; | |
1309 | ||
1310 | /* update UI */ | |
1311 | percentage = (offset * 100) / g_bytes_get_size (bytes); | |
1312 | if (percentage != old_percentage) { | |
1313 | g_signal_emit (target, | |
1314 | signals[SIGNAL_PERCENTAGE_CHANGED], | |
1315 | 0, percentage); | |
1316 | } | |
1317 | ||
1318 | /* give the target a chance to update */ | |
1319 | g_usleep (dfu_device_get_download_timeout (priv->device) * 1000); | |
1320 | ||
1321 | /* getting the status moves the state machine to DNLOAD-IDLE */ | |
1322 | if (!dfu_device_refresh (priv->device, cancellable, error)) | |
1323 | return FALSE; | |
1324 | } | |
1325 | ||
1326 | /* verify */ | |
1327 | if (flags & DFU_TARGET_TRANSFER_FLAG_VERIFY) { | |
1328 | GBytes *bytes_tmp; | |
1329 | g_autoptr(DfuElement) element_tmp = NULL; | |
1330 | element_tmp = dfu_target_upload_element (target, | |
1331 | dfu_element_get_address (element), | |
1332 | g_bytes_get_size (bytes), | |
1333 | cancellable, | |
1334 | error); | |
1335 | if (element_tmp == NULL) | |
1336 | return FALSE; | |
1337 | bytes_tmp = dfu_element_get_contents (element_tmp); | |
1338 | if (g_bytes_compare (bytes_tmp, bytes) != 0) { | |
1339 | g_autofree gchar *bytes_cmp_str = NULL; | |
1340 | bytes_cmp_str = _g_bytes_compare_verbose (bytes_tmp, bytes); | |
1341 | g_set_error (error, | |
1342 | DFU_ERROR, | |
1343 | DFU_ERROR_VERIFY_FAILED, | |
1344 | "verify failed: %s", | |
1345 | bytes_cmp_str); | |
1346 | return FALSE; | |
1347 | } | |
1348 | } | |
1349 | ||
1350 | return TRUE; | |
1351 | } | |
1352 | ||
1353 | /** | |
1354 | * dfu_target_download: | |
1355 | * @target: a #DfuTarget | |
1356 | * @image: a #DfuImage | |
1357 | * @flags: flags to use, e.g. %DFU_TARGET_TRANSFER_FLAG_VERIFY | |
1358 | * @cancellable: a #GCancellable, or %NULL | |
1359 | * @error: a #GError, or %NULL | |
1360 | * | |
1361 | * Downloads firmware from the host to the target, optionally verifying | |
1362 | * the transfer. | |
1363 | * | |
1364 | * Return value: %TRUE for success | |
1365 | * | |
1366 | * Since: 0.5.4 | |
1367 | **/ | |
1368 | gboolean | |
1369 | dfu_target_download (DfuTarget *target, DfuImage *image, | |
1370 | DfuTargetTransferFlags flags, | |
1371 | GCancellable *cancellable, | |
1372 | GError **error) | |
1373 | { | |
1374 | DfuTargetPrivate *priv = GET_PRIVATE (target); | |
1375 | DfuElement *element; | |
1376 | GPtrArray *elements; | |
1377 | gboolean ret; | |
1378 | guint i; | |
1379 | ||
1380 | g_return_val_if_fail (DFU_IS_TARGET (target), FALSE); | |
1381 | g_return_val_if_fail (DFU_IS_IMAGE (image), FALSE); | |
1382 | g_return_val_if_fail (error == NULL || *error == NULL, FALSE); | |
1383 | ||
1384 | /* ensure populated */ | |
1385 | if (!dfu_target_setup (target, error)) | |
1386 | return NULL; | |
1387 | ||
1388 | /* can the target do this? */ | |
1389 | if (!dfu_device_can_download (priv->device)) { | |
1390 | g_set_error_literal (error, | |
1391 | DFU_ERROR, | |
1392 | DFU_ERROR_NOT_SUPPORTED, | |
1393 | "target cannot do downloading"); | |
1394 | return FALSE; | |
1395 | } | |
1396 | ||
1397 | /* use correct alt */ | |
1398 | if (!dfu_target_use_alt_setting (target, error)) | |
1399 | return FALSE; | |
1400 | ||
1401 | /* mark these as all erased */ | |
1402 | if (dfu_device_has_dfuse_support (priv->device)) | |
1403 | g_hash_table_remove_all (priv->sectors_erased); | |
1404 | ||
1405 | /* download all elements in the image to the device */ | |
1406 | elements = dfu_image_get_elements (image); | |
1407 | if (elements->len == 0) { | |
1408 | g_set_error_literal (error, | |
1409 | DFU_ERROR, | |
1410 | DFU_ERROR_INVALID_FILE, | |
1411 | "no image elements"); | |
1412 | return FALSE; | |
1413 | } | |
1414 | for (i = 0; i < elements->len; i++) { | |
1415 | element = dfu_image_get_element (image, i); | |
1416 | g_debug ("downloading element at 0x%04x", | |
1417 | dfu_element_get_address (element)); | |
1418 | ret = dfu_target_download_element (target, | |
1419 | element, | |
1420 | flags, | |
1421 | cancellable, | |
1422 | error); | |
1423 | if (!ret) | |
1424 | return FALSE; | |
1425 | } | |
1426 | ||
1427 | /* attempt to switch back to runtime */ | |
1428 | if ((flags & DFU_TARGET_TRANSFER_FLAG_ATTACH) > 0 || | |
1429 | (flags & DFU_TARGET_TRANSFER_FLAG_WAIT_RUNTIME) > 0) { | |
1430 | if (!dfu_device_attach (priv->device, error)) | |
1431 | return FALSE; | |
1432 | } | |
1433 | ||
1434 | /* boot to runtime */ | |
1435 | if (flags & DFU_TARGET_TRANSFER_FLAG_WAIT_RUNTIME) { | |
1436 | g_debug ("booting to runtime to set auto-boot"); | |
1437 | if (!dfu_device_wait_for_replug (priv->device, | |
1438 | DFU_DEVICE_REPLUG_TIMEOUT, | |
1439 | cancellable, | |
1440 | error)) | |
1441 | return FALSE; | |
1442 | } | |
1443 | ||
1444 | /* success */ | |
1445 | return TRUE; | |
1446 | } | |
1447 | ||
1448 | #if 0 | |
1449 | /** | |
1450 | * dfu_target_get_commands: | |
1451 | **/ | |
1452 | static gboolean | |
1453 | dfu_target_get_commands (DfuTarget *target, | |
1454 | GCancellable *cancellable, | |
1455 | GError **error) | |
1456 | { | |
1457 | GBytes *data_in; | |
1458 | GBytes *data_out; | |
1459 | guint8 buf[1]; | |
1460 | ||
1461 | /* invalid */ | |
1462 | if (!dfu_device_has_dfuse_support (priv->device)) { | |
1463 | g_set_error_literal (error, | |
1464 | DFU_ERROR, | |
1465 | DFU_ERROR_NOT_SUPPORTED, | |
1466 | "only supported for DfuSe targets"); | |
1467 | return FALSE; | |
1468 | } | |
1469 | ||
1470 | /* format buffer */ | |
1471 | buf[0] = DFU_CMD_DFUSE_GET_COMMAND; | |
1472 | data_in = g_bytes_new_static (buf, sizeof(buf)); | |
1473 | if (!dfu_target_download_chunk (target, 0, data_in, cancellable, error)) | |
1474 | return FALSE; | |
1475 | ||
1476 | /* return results */ | |
1477 | data_out = dfu_target_upload_chunk (target, 0, cancellable, error); | |
1478 | if (data_out == NULL) | |
1479 | return FALSE; | |
1480 | ||
1481 | // N bytes, | |
1482 | // each byte is the command code | |
1483 | ||
1484 | // FIXME: parse? | |
1485 | return TRUE; | |
1486 | } | |
1487 | #endif | |
1488 | ||
1489 | /** | |
1490 | * dfu_target_get_alt_setting: | |
1491 | * @target: a #DfuTarget | |
1492 | * | |
1493 | * Gets the alternate setting to use for this interface. | |
1494 | * | |
1495 | * Return value: the alternative setting, typically zero | |
1496 | * | |
1497 | * Since: 0.5.4 | |
1498 | **/ | |
1499 | guint8 | |
1500 | dfu_target_get_alt_setting (DfuTarget *target) | |
1501 | { | |
1502 | DfuTargetPrivate *priv = GET_PRIVATE (target); | |
1503 | g_return_val_if_fail (DFU_IS_TARGET (target), 0xff); | |
1504 | return priv->alt_setting; | |
1505 | } | |
1506 | ||
1507 | /** | |
1508 | * dfu_target_get_alt_name: | |
1509 | * @target: a #DfuTarget | |
1510 | * @error: a #GError, or %NULL | |
1511 | * | |
1512 | * Gets the alternate setting name to use for this interface. | |
1513 | * | |
1514 | * Return value: the alternative setting name, typically %NULL | |
1515 | * | |
1516 | * Since: 0.5.4 | |
1517 | **/ | |
1518 | const gchar * | |
1519 | dfu_target_get_alt_name (DfuTarget *target, GError **error) | |
1520 | { | |
1521 | DfuTargetPrivate *priv = GET_PRIVATE (target); | |
1522 | g_return_val_if_fail (DFU_IS_TARGET (target), NULL); | |
1523 | ||
1524 | /* ensure populated */ | |
1525 | if (!dfu_target_setup (target, error)) | |
1526 | return NULL; | |
1527 | ||
1528 | /* nothing */ | |
1529 | if (priv->alt_name == NULL) { | |
1530 | g_set_error_literal (error, | |
1531 | DFU_ERROR, | |
1532 | DFU_ERROR_NOT_FOUND, | |
1533 | "no alt-name"); | |
1534 | return FALSE; | |
1535 | } | |
1536 | ||
1537 | return priv->alt_name; | |
1538 | } | |
1539 | ||
1540 | /** | |
1541 | * dfu_target_get_cipher_kind: | |
1542 | * @target: a #DfuTarget | |
1543 | * | |
1544 | * Gets the cipher used for data sent to this interface. | |
1545 | * | |
1546 | * Return value: the cipher, typically %DFU_CIPHER_KIND_NONE | |
1547 | * | |
1548 | * Since: 0.5.4 | |
1549 | **/ | |
1550 | DfuCipherKind | |
1551 | dfu_target_get_cipher_kind (DfuTarget *target) | |
1552 | { | |
1553 | DfuTargetPrivate *priv = GET_PRIVATE (target); | |
1554 | g_return_val_if_fail (DFU_IS_TARGET (target), 0); | |
1555 | return priv->cipher_kind; | |
1556 | } |
0 | /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- | |
1 | * | |
2 | * Copyright (C) 2015 Richard Hughes <richard@hughsie.com> | |
3 | * | |
4 | * Licensed under the GNU Lesser General Public License Version 2.1 | |
5 | * | |
6 | * This library is free software; you can redistribute it and/or | |
7 | * modify it under the terms of the GNU Lesser General Public | |
8 | * License as published by the Free Software Foundation; either | |
9 | * version 2.1 of the License, or (at your option) any later version. | |
10 | * | |
11 | * This library is distributed in the hope that it will be useful, | |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
14 | * Lesser General Public License for more details. | |
15 | * | |
16 | * You should have received a copy of the GNU Lesser General Public | |
17 | * License along with this library; if not, write to the Free Software | |
18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | |
19 | */ | |
20 | ||
21 | #ifndef __DFU_TARGET_H | |
22 | #define __DFU_TARGET_H | |
23 | ||
24 | #include <glib-object.h> | |
25 | #include <gio/gio.h> | |
26 | #include <gusb.h> | |
27 | ||
28 | #include "dfu-common.h" | |
29 | #include "dfu-image.h" | |
30 | ||
31 | G_BEGIN_DECLS | |
32 | ||
33 | #define DFU_TYPE_TARGET (dfu_target_get_type ()) | |
34 | G_DECLARE_DERIVABLE_TYPE (DfuTarget, dfu_target, DFU, TARGET, GUsbDevice) | |
35 | ||
36 | struct _DfuTargetClass | |
37 | { | |
38 | GUsbDeviceClass parent_class; | |
39 | void (*percentage_changed) (DfuTarget *target, | |
40 | guint percentage); | |
41 | /*< private >*/ | |
42 | /* Padding for future expansion */ | |
43 | void (*_dfu_target_reserved1) (void); | |
44 | void (*_dfu_target_reserved2) (void); | |
45 | void (*_dfu_target_reserved3) (void); | |
46 | void (*_dfu_target_reserved4) (void); | |
47 | void (*_dfu_target_reserved5) (void); | |
48 | void (*_dfu_target_reserved6) (void); | |
49 | void (*_dfu_target_reserved7) (void); | |
50 | void (*_dfu_target_reserved8) (void); | |
51 | void (*_dfu_target_reserved9) (void); | |
52 | }; | |
53 | ||
54 | /** | |
55 | * DfuTargetTransferFlags: | |
56 | * @DFU_TARGET_TRANSFER_FLAG_NONE: No flags set | |
57 | * @DFU_TARGET_TRANSFER_FLAG_VERIFY: Verify the download once complete | |
58 | * @DFU_TARGET_TRANSFER_FLAG_DETACH: If required, detach from runtime mode | |
59 | * @DFU_TARGET_TRANSFER_FLAG_ATTACH: Attach the device back to runtime after completion | |
60 | * @DFU_TARGET_TRANSFER_FLAG_WAIT_RUNTIME: Wait for runtime to load after completion | |
61 | * @DFU_TARGET_TRANSFER_FLAG_WILDCARD_VID: Allow downloading images with wildcard VIDs | |
62 | * @DFU_TARGET_TRANSFER_FLAG_WILDCARD_PID: Allow downloading images with wildcard PIDs | |
63 | * @DFU_TARGET_TRANSFER_FLAG_ANY_CIPHER: Allow any cipher kinds to be downloaded | |
64 | * | |
65 | * The optional flags used for transfering firmware. | |
66 | **/ | |
67 | typedef enum { | |
68 | DFU_TARGET_TRANSFER_FLAG_NONE = 0, | |
69 | DFU_TARGET_TRANSFER_FLAG_VERIFY = (1 << 0), | |
70 | DFU_TARGET_TRANSFER_FLAG_DETACH = (1 << 1), | |
71 | DFU_TARGET_TRANSFER_FLAG_ATTACH = (1 << 2), | |
72 | DFU_TARGET_TRANSFER_FLAG_WAIT_RUNTIME = (1 << 3), | |
73 | DFU_TARGET_TRANSFER_FLAG_WILDCARD_VID = (1 << 4), | |
74 | DFU_TARGET_TRANSFER_FLAG_WILDCARD_PID = (1 << 5), | |
75 | DFU_TARGET_TRANSFER_FLAG_ANY_CIPHER = (1 << 6), | |
76 | /*< private >*/ | |
77 | DFU_TARGET_TRANSFER_FLAG_LAST, | |
78 | } DfuTargetTransferFlags; | |
79 | ||
80 | GPtrArray *dfu_target_get_sectors (DfuTarget *target); | |
81 | guint8 dfu_target_get_alt_setting (DfuTarget *target); | |
82 | const gchar *dfu_target_get_alt_name (DfuTarget *target, | |
83 | GError **error); | |
84 | DfuImage *dfu_target_upload (DfuTarget *target, | |
85 | DfuTargetTransferFlags flags, | |
86 | GCancellable *cancellable, | |
87 | GError **error); | |
88 | gboolean dfu_target_download (DfuTarget *target, | |
89 | DfuImage *image, | |
90 | DfuTargetTransferFlags flags, | |
91 | GCancellable *cancellable, | |
92 | GError **error); | |
93 | DfuCipherKind dfu_target_get_cipher_kind (DfuTarget *target); | |
94 | ||
95 | G_END_DECLS | |
96 | ||
97 | #endif /* __DFU_TARGET_H */ |
0 | /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- | |
1 | * | |
2 | * Copyright (C) 2015 Richard Hughes <richard@hughsie.com> | |
3 | * | |
4 | * Licensed under the GNU General Public License Version 2 | |
5 | * | |
6 | * This program is free software; you can redistribute it and/or modify | |
7 | * it under the terms of the GNU General Public License as published by | |
8 | * the Free Software Foundation; either version 2 of the License, or | |
9 | * (at your option) any later version. | |
10 | * | |
11 | * This program is distributed in the hope that it will be useful, | |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
14 | * GNU General Public License for more details. | |
15 | * | |
16 | * You should have received a copy of the GNU General Public License | |
17 | * along with this program; if not, write to the Free Software | |
18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | |
19 | */ | |
20 | ||
21 | #include "config.h" | |
22 | ||
23 | #include <dfu.h> | |
24 | #include <libintl.h> | |
25 | #include <locale.h> | |
26 | #include <stdlib.h> | |
27 | #include <glib/gi18n.h> | |
28 | #include <glib-unix.h> | |
29 | #include <appstream-glib.h> | |
30 | ||
31 | #include "dfu-device-private.h" | |
32 | ||
33 | typedef struct { | |
34 | GCancellable *cancellable; | |
35 | GPtrArray *cmd_array; | |
36 | gboolean force; | |
37 | gchar *device_vid_pid; | |
38 | guint16 transfer_size; | |
39 | } DfuToolPrivate; | |
40 | ||
41 | /** | |
42 | * dfu_tool_print_indent: | |
43 | **/ | |
44 | static void | |
45 | dfu_tool_print_indent (const gchar *title, const gchar *message, guint indent) | |
46 | { | |
47 | guint i; | |
48 | for (i = 0; i < indent; i++) | |
49 | g_print (" "); | |
50 | g_print ("%s:", title); | |
51 | for (i = strlen (title) + indent; i < 15; i++) | |
52 | g_print (" "); | |
53 | g_print ("%s\n", message); | |
54 | } | |
55 | ||
56 | /** | |
57 | * dfu_tool_private_free: | |
58 | **/ | |
59 | static void | |
60 | dfu_tool_private_free (DfuToolPrivate *priv) | |
61 | { | |
62 | if (priv == NULL) | |
63 | return; | |
64 | g_free (priv->device_vid_pid); | |
65 | g_object_unref (priv->cancellable); | |
66 | if (priv->cmd_array != NULL) | |
67 | g_ptr_array_unref (priv->cmd_array); | |
68 | g_free (priv); | |
69 | } | |
70 | G_DEFINE_AUTOPTR_CLEANUP_FUNC(DfuToolPrivate, dfu_tool_private_free) | |
71 | ||
72 | typedef gboolean (*FuUtilPrivateCb) (DfuToolPrivate *util, | |
73 | gchar **values, | |
74 | GError **error); | |
75 | ||
76 | typedef struct { | |
77 | gchar *name; | |
78 | gchar *arguments; | |
79 | gchar *description; | |
80 | FuUtilPrivateCb callback; | |
81 | } FuUtilItem; | |
82 | ||
83 | /** | |
84 | * dfu_tool_item_free: | |
85 | **/ | |
86 | static void | |
87 | dfu_tool_item_free (FuUtilItem *item) | |
88 | { | |
89 | g_free (item->name); | |
90 | g_free (item->arguments); | |
91 | g_free (item->description); | |
92 | g_free (item); | |
93 | } | |
94 | ||
95 | /** | |
96 | * dfu_tool_sort_command_name_cb: | |
97 | **/ | |
98 | static gint | |
99 | dfu_tool_sort_command_name_cb (FuUtilItem **item1, FuUtilItem **item2) | |
100 | { | |
101 | return g_strcmp0 ((*item1)->name, (*item2)->name); | |
102 | } | |
103 | ||
104 | /** | |
105 | * dfu_tool_add: | |
106 | **/ | |
107 | static void | |
108 | dfu_tool_add (GPtrArray *array, | |
109 | const gchar *name, | |
110 | const gchar *arguments, | |
111 | const gchar *description, | |
112 | FuUtilPrivateCb callback) | |
113 | { | |
114 | guint i; | |
115 | FuUtilItem *item; | |
116 | g_auto(GStrv) names = NULL; | |
117 | ||
118 | g_return_if_fail (name != NULL); | |
119 | g_return_if_fail (description != NULL); | |
120 | g_return_if_fail (callback != NULL); | |
121 | ||
122 | /* add each one */ | |
123 | names = g_strsplit (name, ",", -1); | |
124 | for (i = 0; names[i] != NULL; i++) { | |
125 | item = g_new0 (FuUtilItem, 1); | |
126 | item->name = g_strdup (names[i]); | |
127 | if (i == 0) { | |
128 | item->description = g_strdup (description); | |
129 | } else { | |
130 | /* TRANSLATORS: this is a command alias, e.g. 'get-devices' */ | |
131 | item->description = g_strdup_printf (_("Alias to %s"), | |
132 | names[0]); | |
133 | } | |
134 | item->arguments = g_strdup (arguments); | |
135 | item->callback = callback; | |
136 | g_ptr_array_add (array, item); | |
137 | } | |
138 | } | |
139 | ||
140 | /** | |
141 | * dfu_tool_get_descriptions: | |
142 | **/ | |
143 | static gchar * | |
144 | dfu_tool_get_descriptions (GPtrArray *array) | |
145 | { | |
146 | guint i; | |
147 | guint j; | |
148 | guint len; | |
149 | const guint max_len = 31; | |
150 | FuUtilItem *item; | |
151 | GString *string; | |
152 | ||
153 | /* print each command */ | |
154 | string = g_string_new (""); | |
155 | for (i = 0; i < array->len; i++) { | |
156 | item = g_ptr_array_index (array, i); | |
157 | g_string_append (string, " "); | |
158 | g_string_append (string, item->name); | |
159 | len = strlen (item->name) + 2; | |
160 | if (item->arguments != NULL) { | |
161 | g_string_append (string, " "); | |
162 | g_string_append (string, item->arguments); | |
163 | len += strlen (item->arguments) + 1; | |
164 | } | |
165 | if (len < max_len) { | |
166 | for (j = len; j < max_len + 1; j++) | |
167 | g_string_append_c (string, ' '); | |
168 | g_string_append (string, item->description); | |
169 | g_string_append_c (string, '\n'); | |
170 | } else { | |
171 | g_string_append_c (string, '\n'); | |
172 | for (j = 0; j < max_len + 1; j++) | |
173 | g_string_append_c (string, ' '); | |
174 | g_string_append (string, item->description); | |
175 | g_string_append_c (string, '\n'); | |
176 | } | |
177 | } | |
178 | ||
179 | /* remove trailing newline */ | |
180 | if (string->len > 0) | |
181 | g_string_set_size (string, string->len - 1); | |
182 | ||
183 | return g_string_free (string, FALSE); | |
184 | } | |
185 | ||
186 | /** | |
187 | * dfu_tool_run: | |
188 | **/ | |
189 | static gboolean | |
190 | dfu_tool_run (DfuToolPrivate *priv, | |
191 | const gchar *command, | |
192 | gchar **values, | |
193 | GError **error) | |
194 | { | |
195 | guint i; | |
196 | FuUtilItem *item; | |
197 | ||
198 | /* find command */ | |
199 | for (i = 0; i < priv->cmd_array->len; i++) { | |
200 | item = g_ptr_array_index (priv->cmd_array, i); | |
201 | if (g_strcmp0 (item->name, command) == 0) | |
202 | return item->callback (priv, values, error); | |
203 | } | |
204 | ||
205 | /* not found */ | |
206 | g_set_error_literal (error, | |
207 | DFU_ERROR, | |
208 | DFU_ERROR_INTERNAL, | |
209 | /* TRANSLATORS: error message */ | |
210 | _("Command not found")); | |
211 | return FALSE; | |
212 | } | |
213 | ||
214 | /** | |
215 | * dfu_tool_get_defalt_device: | |
216 | **/ | |
217 | static DfuDevice * | |
218 | dfu_tool_get_defalt_device (DfuToolPrivate *priv, GError **error) | |
219 | { | |
220 | DfuDevice *device; | |
221 | g_autoptr(DfuContext) dfu_context = NULL; | |
222 | ||
223 | /* get all the DFU devices */ | |
224 | dfu_context = dfu_context_new (); | |
225 | dfu_context_enumerate (dfu_context, NULL); | |
226 | ||
227 | /* we specified it manually */ | |
228 | if (priv->device_vid_pid != NULL) { | |
229 | gchar *tmp; | |
230 | guint64 pid; | |
231 | guint64 vid; | |
232 | ||
233 | /* parse */ | |
234 | vid = g_ascii_strtoull (priv->device_vid_pid, &tmp, 16); | |
235 | if (vid == 0 || vid > G_MAXUINT16) { | |
236 | g_set_error_literal (error, | |
237 | DFU_ERROR, | |
238 | DFU_ERROR_INTERNAL, | |
239 | "Invalid format of VID:PID"); | |
240 | return NULL; | |
241 | } | |
242 | if (tmp[0] != ':') { | |
243 | g_set_error_literal (error, | |
244 | DFU_ERROR, | |
245 | DFU_ERROR_INTERNAL, | |
246 | "Invalid format of VID:PID"); | |
247 | return NULL; | |
248 | } | |
249 | pid = g_ascii_strtoull (tmp + 1, NULL, 16); | |
250 | if (vid == 0 || vid > G_MAXUINT16) { | |
251 | g_set_error_literal (error, | |
252 | DFU_ERROR, | |
253 | DFU_ERROR_INTERNAL, | |
254 | "Invalid format of VID:PID"); | |
255 | return NULL; | |
256 | } | |
257 | ||
258 | /* find device */ | |
259 | device = dfu_context_get_device_by_vid_pid (dfu_context, | |
260 | vid, pid, | |
261 | error); | |
262 | if (device == NULL) | |
263 | return NULL; | |
264 | } else { | |
265 | /* auto-detect first device */ | |
266 | device = dfu_context_get_device_default (dfu_context, error); | |
267 | if (device == NULL) | |
268 | return NULL; | |
269 | } | |
270 | ||
271 | /* this has to be added to the device so we can deal with detach */ | |
272 | g_object_set_data_full (G_OBJECT (device), "DfuContext", | |
273 | g_object_ref (dfu_context), | |
274 | (GDestroyNotify) g_object_unref); | |
275 | return device; | |
276 | } | |
277 | ||
278 | /** | |
279 | * dfu_tool_set_vendor: | |
280 | **/ | |
281 | static gboolean | |
282 | dfu_tool_set_vendor (DfuToolPrivate *priv, gchar **values, GError **error) | |
283 | { | |
284 | guint64 tmp; | |
285 | g_autoptr(DfuFirmware) firmware = NULL; | |
286 | g_autoptr(GFile) file = NULL; | |
287 | ||
288 | /* check args */ | |
289 | if (g_strv_length (values) < 2) { | |
290 | g_set_error_literal (error, | |
291 | DFU_ERROR, | |
292 | DFU_ERROR_INTERNAL, | |
293 | "Invalid arguments, expected FILE VID" | |
294 | " -- e.g. `firmware.dfu 273f"); | |
295 | return FALSE; | |
296 | } | |
297 | ||
298 | /* open */ | |
299 | file = g_file_new_for_path (values[0]); | |
300 | firmware = dfu_firmware_new (); | |
301 | if (!dfu_firmware_parse_file (firmware, file, | |
302 | DFU_FIRMWARE_PARSE_FLAG_NONE, | |
303 | priv->cancellable, | |
304 | error)) { | |
305 | return FALSE; | |
306 | } | |
307 | ||
308 | /* parse VID */ | |
309 | tmp = g_ascii_strtoull (values[1], NULL, 16); | |
310 | if (tmp == 0 || tmp > 0xffff) { | |
311 | g_set_error (error, | |
312 | DFU_ERROR, | |
313 | DFU_ERROR_INTERNAL, | |
314 | "Failed to parse VID '%s'", | |
315 | values[1]); | |
316 | return FALSE; | |
317 | } | |
318 | dfu_firmware_set_vid (firmware, tmp); | |
319 | ||
320 | /* write out new file */ | |
321 | return dfu_firmware_write_file (firmware, | |
322 | file, | |
323 | priv->cancellable, | |
324 | error); | |
325 | } | |
326 | ||
327 | /** | |
328 | * dfu_tool_set_product: | |
329 | **/ | |
330 | static gboolean | |
331 | dfu_tool_set_product (DfuToolPrivate *priv, gchar **values, GError **error) | |
332 | { | |
333 | guint64 tmp; | |
334 | g_autoptr(DfuFirmware) firmware = NULL; | |
335 | g_autoptr(GFile) file = NULL; | |
336 | ||
337 | /* check args */ | |
338 | if (g_strv_length (values) < 2) { | |
339 | g_set_error_literal (error, | |
340 | DFU_ERROR, | |
341 | DFU_ERROR_INTERNAL, | |
342 | "Invalid arguments, expected FILE PID" | |
343 | " -- e.g. `firmware.dfu 1004"); | |
344 | return FALSE; | |
345 | } | |
346 | ||
347 | /* open */ | |
348 | file = g_file_new_for_path (values[0]); | |
349 | firmware = dfu_firmware_new (); | |
350 | if (!dfu_firmware_parse_file (firmware, file, | |
351 | DFU_FIRMWARE_PARSE_FLAG_NONE, | |
352 | priv->cancellable, | |
353 | error)) { | |
354 | return FALSE; | |
355 | } | |
356 | ||
357 | /* parse VID */ | |
358 | tmp = g_ascii_strtoull (values[1], NULL, 16); | |
359 | if (tmp == 0 || tmp > 0xffff) { | |
360 | g_set_error (error, | |
361 | DFU_ERROR, | |
362 | DFU_ERROR_INTERNAL, | |
363 | "Failed to parse PID '%s'", values[1]); | |
364 | return FALSE; | |
365 | } | |
366 | dfu_firmware_set_pid (firmware, tmp); | |
367 | ||
368 | /* write out new file */ | |
369 | return dfu_firmware_write_file (firmware, | |
370 | file, | |
371 | priv->cancellable, | |
372 | error); | |
373 | } | |
374 | ||
375 | /** | |
376 | * dfu_tool_set_release: | |
377 | **/ | |
378 | static gboolean | |
379 | dfu_tool_set_release (DfuToolPrivate *priv, gchar **values, GError **error) | |
380 | { | |
381 | guint64 tmp; | |
382 | g_autoptr(DfuFirmware) firmware = NULL; | |
383 | g_autoptr(GFile) file = NULL; | |
384 | ||
385 | /* check args */ | |
386 | if (g_strv_length (values) < 2) { | |
387 | g_set_error_literal (error, | |
388 | DFU_ERROR, | |
389 | DFU_ERROR_INTERNAL, | |
390 | "Invalid arguments, expected FILE RELEASE" | |
391 | " -- e.g. `firmware.dfu ffff"); | |
392 | return FALSE; | |
393 | } | |
394 | ||
395 | /* open */ | |
396 | file = g_file_new_for_path (values[0]); | |
397 | firmware = dfu_firmware_new (); | |
398 | if (!dfu_firmware_parse_file (firmware, file, | |
399 | DFU_FIRMWARE_PARSE_FLAG_NONE, | |
400 | priv->cancellable, | |
401 | error)) { | |
402 | return FALSE; | |
403 | } | |
404 | ||
405 | /* parse VID */ | |
406 | tmp = g_ascii_strtoull (values[1], NULL, 16); | |
407 | if (tmp == 0 || tmp > 0xffff) { | |
408 | g_set_error (error, | |
409 | DFU_ERROR, | |
410 | DFU_ERROR_INTERNAL, | |
411 | "Failed to parse release '%s'", values[1]); | |
412 | return FALSE; | |
413 | } | |
414 | dfu_firmware_set_release (firmware, tmp); | |
415 | ||
416 | /* write out new file */ | |
417 | return dfu_firmware_write_file (firmware, | |
418 | file, | |
419 | priv->cancellable, | |
420 | error); | |
421 | } | |
422 | ||
423 | /** | |
424 | * dfu_tool_set_metadata: | |
425 | **/ | |
426 | static gboolean | |
427 | dfu_tool_set_metadata (DfuToolPrivate *priv, gchar **values, GError **error) | |
428 | { | |
429 | g_autoptr(DfuFirmware) firmware = NULL; | |
430 | g_autoptr(GFile) file = NULL; | |
431 | ||
432 | /* check args */ | |
433 | if (g_strv_length (values) < 3) { | |
434 | g_set_error_literal (error, | |
435 | DFU_ERROR, | |
436 | DFU_ERROR_INTERNAL, | |
437 | "Invalid arguments, expected FILE KEY VALUE" | |
438 | " -- e.g. `firmware.dfu Licence GPL-2.0+"); | |
439 | return FALSE; | |
440 | } | |
441 | ||
442 | /* open */ | |
443 | file = g_file_new_for_path (values[0]); | |
444 | firmware = dfu_firmware_new (); | |
445 | if (!dfu_firmware_parse_file (firmware, file, | |
446 | DFU_FIRMWARE_PARSE_FLAG_NONE, | |
447 | priv->cancellable, | |
448 | error)) { | |
449 | return FALSE; | |
450 | } | |
451 | ||
452 | /* doesn't make sense for non-DFU */ | |
453 | if (dfu_firmware_get_format (firmware) == DFU_FIRMWARE_FORMAT_RAW) { | |
454 | g_set_error (error, | |
455 | DFU_ERROR, | |
456 | DFU_ERROR_INTERNAL, | |
457 | "Only possible on DFU/DfuSe images, try convert"); | |
458 | return FALSE; | |
459 | } | |
460 | ||
461 | /* set metadata */ | |
462 | dfu_firmware_set_metadata (firmware, values[1], values[2]); | |
463 | ||
464 | /* write out new file */ | |
465 | return dfu_firmware_write_file (firmware, | |
466 | file, | |
467 | priv->cancellable, | |
468 | error); | |
469 | } | |
470 | ||
471 | /** | |
472 | * dfu_tool_set_alt_setting: | |
473 | **/ | |
474 | static gboolean | |
475 | dfu_tool_set_alt_setting (DfuToolPrivate *priv, gchar **values, GError **error) | |
476 | { | |
477 | DfuImage *image; | |
478 | guint64 tmp; | |
479 | g_autoptr(DfuFirmware) firmware = NULL; | |
480 | g_autoptr(GFile) file = NULL; | |
481 | ||
482 | /* check args */ | |
483 | if (g_strv_length (values) < 2) { | |
484 | g_set_error_literal (error, | |
485 | DFU_ERROR, | |
486 | DFU_ERROR_INTERNAL, | |
487 | "Invalid arguments, expected FILE ALT-ID" | |
488 | " -- e.g. `firmware.dfu 1"); | |
489 | return FALSE; | |
490 | } | |
491 | ||
492 | /* open */ | |
493 | file = g_file_new_for_path (values[0]); | |
494 | firmware = dfu_firmware_new (); | |
495 | if (!dfu_firmware_parse_file (firmware, file, | |
496 | DFU_FIRMWARE_PARSE_FLAG_NONE, | |
497 | priv->cancellable, | |
498 | error)) { | |
499 | return FALSE; | |
500 | } | |
501 | ||
502 | /* doesn't make sense for non-DfuSe */ | |
503 | if (dfu_firmware_get_format (firmware) != DFU_FIRMWARE_FORMAT_DFUSE) { | |
504 | g_set_error (error, | |
505 | DFU_ERROR, | |
506 | DFU_ERROR_INTERNAL, | |
507 | "Only possible on DfuSe images, try convert"); | |
508 | return FALSE; | |
509 | } | |
510 | ||
511 | /* parse VID */ | |
512 | tmp = g_ascii_strtoull (values[1], NULL, 10); | |
513 | if (tmp == 0 || tmp > 0xff) { | |
514 | g_set_error (error, | |
515 | DFU_ERROR, | |
516 | DFU_ERROR_INTERNAL, | |
517 | "Failed to parse alternative setting '%s'", | |
518 | values[1]); | |
519 | return FALSE; | |
520 | } | |
521 | image = dfu_firmware_get_image_default (firmware); | |
522 | if (image == NULL) { | |
523 | g_set_error (error, | |
524 | DFU_ERROR, | |
525 | DFU_ERROR_INTERNAL, | |
526 | "found no image '%s'", values[1]); | |
527 | return FALSE; | |
528 | } | |
529 | dfu_image_set_alt_setting (image, tmp); | |
530 | ||
531 | /* write out new file */ | |
532 | return dfu_firmware_write_file (firmware, | |
533 | file, | |
534 | priv->cancellable, | |
535 | error); | |
536 | } | |
537 | ||
538 | /** | |
539 | * dfu_tool_set_alt_setting_name: | |
540 | **/ | |
541 | static gboolean | |
542 | dfu_tool_set_alt_setting_name (DfuToolPrivate *priv, gchar **values, GError **error) | |
543 | { | |
544 | DfuImage *image; | |
545 | g_autoptr(DfuFirmware) firmware = NULL; | |
546 | g_autoptr(GFile) file = NULL; | |
547 | ||
548 | /* check args */ | |
549 | if (g_strv_length (values) < 2) { | |
550 | g_set_error_literal (error, | |
551 | DFU_ERROR, | |
552 | DFU_ERROR_INTERNAL, | |
553 | "Invalid arguments, expected FILE ALT-NAME" | |
554 | " -- e.g. `firmware.dfu ST"); | |
555 | return FALSE; | |
556 | } | |
557 | ||
558 | /* open */ | |
559 | file = g_file_new_for_path (values[0]); | |
560 | firmware = dfu_firmware_new (); | |
561 | if (!dfu_firmware_parse_file (firmware, file, | |
562 | DFU_FIRMWARE_PARSE_FLAG_NONE, | |
563 | priv->cancellable, | |
564 | error)) { | |
565 | return FALSE; | |
566 | } | |
567 | ||
568 | /* doesn't make sense for non-DfuSe */ | |
569 | if (dfu_firmware_get_format (firmware) != DFU_FIRMWARE_FORMAT_DFUSE) { | |
570 | g_set_error (error, | |
571 | DFU_ERROR, | |
572 | DFU_ERROR_INTERNAL, | |
573 | "Only possible on DfuSe images, try convert"); | |
574 | return FALSE; | |
575 | } | |
576 | ||
577 | /* parse VID */ | |
578 | image = dfu_firmware_get_image_default (firmware); | |
579 | if (image == NULL) { | |
580 | g_set_error (error, | |
581 | DFU_ERROR, | |
582 | DFU_ERROR_INTERNAL, | |
583 | "found no image '%s'", values[1]); | |
584 | return FALSE; | |
585 | } | |
586 | dfu_image_set_name (image, values[1]); | |
587 | ||
588 | /* write out new file */ | |
589 | return dfu_firmware_write_file (firmware, | |
590 | file, | |
591 | priv->cancellable, | |
592 | error); | |
593 | } | |
594 | ||
595 | /** | |
596 | * dfu_tool_merge: | |
597 | **/ | |
598 | static gboolean | |
599 | dfu_tool_merge (DfuToolPrivate *priv, gchar **values, GError **error) | |
600 | { | |
601 | guint16 pid = 0xffff; | |
602 | guint16 rel = 0xffff; | |
603 | guint16 vid = 0xffff; | |
604 | guint i; | |
605 | g_autofree gchar *str_debug = NULL; | |
606 | g_autoptr(DfuFirmware) firmware = NULL; | |
607 | g_autoptr(GFile) file = NULL; | |
608 | ||
609 | /* check args */ | |
610 | if (g_strv_length (values) < 3) { | |
611 | g_set_error_literal (error, | |
612 | DFU_ERROR, | |
613 | DFU_ERROR_INTERNAL, | |
614 | "Invalid arguments, expected " | |
615 | "FILE-OUT FILE1 FILE2 [FILE3...]" | |
616 | " -- e.g. `combined.dfu lib.dfu app.dfu`"); | |
617 | return FALSE; | |
618 | } | |
619 | ||
620 | /* parse source files */ | |
621 | firmware = dfu_firmware_new (); | |
622 | dfu_firmware_set_format (firmware, DFU_FIRMWARE_FORMAT_DFUSE); | |
623 | for (i = 1; values[i] != NULL; i++) { | |
624 | GPtrArray *images; | |
625 | guint j; | |
626 | g_autoptr(GFile) file_tmp = NULL; | |
627 | g_autoptr(DfuFirmware) firmware_tmp = NULL; | |
628 | ||
629 | /* open up source */ | |
630 | file_tmp = g_file_new_for_path (values[i]); | |
631 | firmware_tmp = dfu_firmware_new (); | |
632 | if (!dfu_firmware_parse_file (firmware_tmp, file_tmp, | |
633 | DFU_FIRMWARE_PARSE_FLAG_NONE, | |
634 | priv->cancellable, | |
635 | error)) { | |
636 | return FALSE; | |
637 | } | |
638 | ||
639 | /* check same vid:pid:rel */ | |
640 | if (vid != 0xffff && | |
641 | dfu_firmware_get_vid (firmware_tmp) != vid) { | |
642 | g_set_error (error, | |
643 | DFU_ERROR, | |
644 | DFU_ERROR_INVALID_FILE, | |
645 | "Vendor ID was already set as " | |
646 | "0x%04x, %s is 0x%04x", | |
647 | vid, values[i], | |
648 | dfu_firmware_get_vid (firmware_tmp)); | |
649 | return FALSE; | |
650 | } | |
651 | if (pid != 0xffff && | |
652 | dfu_firmware_get_pid (firmware_tmp) != pid) { | |
653 | g_set_error (error, | |
654 | DFU_ERROR, | |
655 | DFU_ERROR_INVALID_FILE, | |
656 | "Product ID was already set as " | |
657 | "0x%04x, %s is 0x%04x", | |
658 | pid, values[i], | |
659 | dfu_firmware_get_pid (firmware_tmp)); | |
660 | return FALSE; | |
661 | } | |
662 | if (rel != 0xffff && | |
663 | dfu_firmware_get_release (firmware_tmp) != rel) { | |
664 | g_set_error (error, | |
665 | DFU_ERROR, | |
666 | DFU_ERROR_INVALID_FILE, | |
667 | "Release was already set as " | |
668 | "0x%04x, %s is 0x%04x", | |
669 | rel, values[i], | |
670 | dfu_firmware_get_release (firmware_tmp)); | |
671 | return FALSE; | |
672 | } | |
673 | ||
674 | /* add all images to destination */ | |
675 | images = dfu_firmware_get_images (firmware_tmp); | |
676 | for (j = 0; j < images->len; j++) { | |
677 | DfuImage *image; | |
678 | guint alt_id; | |
679 | ||
680 | /* verify the alt-setting does not already exist */ | |
681 | image = g_ptr_array_index (images, j); | |
682 | alt_id = dfu_image_get_alt_setting (image); | |
683 | g_print ("Adding alternative setting ID of 0x%02x\n", | |
684 | alt_id); | |
685 | if (dfu_firmware_get_image (firmware, alt_id) != NULL) { | |
686 | if (!priv->force) { | |
687 | g_set_error (error, | |
688 | DFU_ERROR, | |
689 | DFU_ERROR_INVALID_FILE, | |
690 | "The alternative setting ID " | |
691 | "of 0x%02x has already been added", | |
692 | alt_id); | |
693 | return FALSE; | |
694 | } | |
695 | g_print ("WARNING: The alternative setting " | |
696 | "ID of 0x%02x has already been added\n", | |
697 | alt_id); | |
698 | } | |
699 | ||
700 | /* add to destination */ | |
701 | dfu_firmware_add_image (firmware, image); | |
702 | } | |
703 | ||
704 | /* save last IDs */ | |
705 | vid = dfu_firmware_get_vid (firmware_tmp); | |
706 | pid = dfu_firmware_get_pid (firmware_tmp); | |
707 | rel = dfu_firmware_get_release (firmware_tmp); | |
708 | } | |
709 | ||
710 | /* print the new object */ | |
711 | str_debug = dfu_firmware_to_string (firmware); | |
712 | g_print ("New merged file:\n%s\n", str_debug); | |
713 | ||
714 | /* write out new file */ | |
715 | file = g_file_new_for_path (values[0]); | |
716 | return dfu_firmware_write_file (firmware, | |
717 | file, | |
718 | priv->cancellable, | |
719 | error); | |
720 | } | |
721 | ||
722 | /** | |
723 | * dfu_tool_convert: | |
724 | **/ | |
725 | static gboolean | |
726 | dfu_tool_convert (DfuToolPrivate *priv, gchar **values, GError **error) | |
727 | { | |
728 | guint64 tmp; | |
729 | guint argc = g_strv_length (values); | |
730 | g_autofree gchar *str_debug = NULL; | |
731 | g_autoptr(DfuFirmware) firmware = NULL; | |
732 | g_autoptr(GFile) file_in = NULL; | |
733 | g_autoptr(GFile) file_out = NULL; | |
734 | ||
735 | /* check args */ | |
736 | if (argc < 3 || argc > 4) { | |
737 | g_set_error_literal (error, | |
738 | DFU_ERROR, | |
739 | DFU_ERROR_INTERNAL, | |
740 | "Invalid arguments, expected " | |
741 | "FORMAT FILE-IN FILE-OUT [SIZE]" | |
742 | " -- e.g. `dfu firmware.hex firmware.dfu 8000`"); | |
743 | return FALSE; | |
744 | } | |
745 | ||
746 | /* parse file */ | |
747 | file_in = g_file_new_for_path (values[1]); | |
748 | file_out = g_file_new_for_path (values[2]); | |
749 | firmware = dfu_firmware_new (); | |
750 | if (!dfu_firmware_parse_file (firmware, file_in, | |
751 | DFU_FIRMWARE_PARSE_FLAG_NONE, | |
752 | priv->cancellable, | |
753 | error)) { | |
754 | return FALSE; | |
755 | } | |
756 | ||
757 | /* set output format */ | |
758 | if (g_strcmp0 (values[0], "raw") == 0) { | |
759 | dfu_firmware_set_format (firmware, DFU_FIRMWARE_FORMAT_RAW); | |
760 | } else if (g_strcmp0 (values[0], "dfu") == 0) { | |
761 | dfu_firmware_set_format (firmware, DFU_FIRMWARE_FORMAT_DFU_1_0); | |
762 | } else if (g_strcmp0 (values[0], "dfuse") == 0) { | |
763 | dfu_firmware_set_format (firmware, DFU_FIRMWARE_FORMAT_DFUSE); | |
764 | } else if (g_strcmp0 (values[0], "ihex") == 0) { | |
765 | dfu_firmware_set_format (firmware, DFU_FIRMWARE_FORMAT_INTEL_HEX); | |
766 | } else { | |
767 | g_set_error (error, | |
768 | DFU_ERROR, | |
769 | DFU_ERROR_INTERNAL, | |
770 | "unknown format '%s', expected [raw|dfu|dfuse|ihex]", | |
771 | values[0]); | |
772 | return FALSE; | |
773 | } | |
774 | ||
775 | /* set target size */ | |
776 | if (argc > 3) { | |
777 | DfuImage *image; | |
778 | DfuElement *element; | |
779 | gchar *endptr; | |
780 | tmp = g_ascii_strtoull (values[3], &endptr, 16); | |
781 | if (tmp > 0xffff || endptr[0] != '\0') { | |
782 | g_set_error (error, | |
783 | DFU_ERROR, | |
784 | DFU_ERROR_INTERNAL, | |
785 | "Failed to parse target size '%s'", | |
786 | values[3]); | |
787 | return FALSE; | |
788 | } | |
789 | ||
790 | /* doesn't make sense for DfuSe */ | |
791 | if (dfu_firmware_get_format (firmware) == DFU_FIRMWARE_FORMAT_DFUSE) { | |
792 | g_set_error (error, | |
793 | DFU_ERROR, | |
794 | DFU_ERROR_INTERNAL, | |
795 | "Cannot pad DfuSe image, try DFU"); | |
796 | return FALSE; | |
797 | } | |
798 | ||
799 | /* this has to exist */ | |
800 | if (tmp > 0) { | |
801 | image = dfu_firmware_get_image_default (firmware); | |
802 | g_assert (image != NULL); | |
803 | element = dfu_image_get_element (image, 0); | |
804 | dfu_element_set_target_size (element, tmp); | |
805 | } | |
806 | } | |
807 | ||
808 | /* print the new object */ | |
809 | str_debug = dfu_firmware_to_string (firmware); | |
810 | g_debug ("DFU: %s", str_debug); | |
811 | ||
812 | /* write out new file */ | |
813 | return dfu_firmware_write_file (firmware, | |
814 | file_out, | |
815 | priv->cancellable, | |
816 | error); | |
817 | } | |
818 | ||
819 | /** | |
820 | * dfu_tool_attach: | |
821 | **/ | |
822 | static gboolean | |
823 | dfu_tool_attach (DfuToolPrivate *priv, gchar **values, GError **error) | |
824 | { | |
825 | g_autoptr(DfuDevice) device = NULL; | |
826 | ||
827 | device = dfu_tool_get_defalt_device (priv, error); | |
828 | if (device == NULL) | |
829 | return FALSE; | |
830 | if (!dfu_device_open (device, | |
831 | DFU_DEVICE_OPEN_FLAG_NONE, | |
832 | priv->cancellable, | |
833 | error)) | |
834 | return FALSE; | |
835 | if (!dfu_device_attach (device, error)) | |
836 | return FALSE; | |
837 | return TRUE; | |
838 | } | |
839 | ||
840 | typedef struct { | |
841 | guint marks_total; | |
842 | guint marks_shown; | |
843 | DfuState last_state; | |
844 | } DfuToolProgressHelper; | |
845 | ||
846 | /** | |
847 | * fu_tool_state_changed_cb: | |
848 | **/ | |
849 | static void | |
850 | fu_tool_state_changed_cb (DfuDevice *device, | |
851 | DfuState state, | |
852 | DfuToolProgressHelper *helper) | |
853 | { | |
854 | const gchar *title = NULL; | |
855 | guint i; | |
856 | ||
857 | /* changed state */ | |
858 | if (state == helper->last_state) | |
859 | return; | |
860 | ||
861 | /* state was left hanging... */ | |
862 | if (helper->marks_shown == 0) { | |
863 | switch (helper->last_state) { | |
864 | case DFU_STATE_APP_DETACH: | |
865 | case DFU_STATE_DFU_DNLOAD_IDLE: | |
866 | case DFU_STATE_DFU_MANIFEST_WAIT_RESET: | |
867 | case DFU_STATE_DFU_UPLOAD_IDLE: | |
868 | /* TRANSLATORS: when an action has completed */ | |
869 | g_print ("%s\n", _("OK")); | |
870 | break; | |
871 | default: | |
872 | g_debug ("ignore last state transition %s", | |
873 | dfu_state_to_string (helper->last_state)); | |
874 | break; | |
875 | } | |
876 | } | |
877 | ||
878 | switch (state) { | |
879 | case DFU_STATE_APP_DETACH: | |
880 | /* TRANSLATORS: when moving from runtime to DFU mode */ | |
881 | title = _("Detaching"); | |
882 | break; | |
883 | case DFU_STATE_DFU_MANIFEST_WAIT_RESET: | |
884 | /* TRANSLATORS: when moving from DFU to runtime mode */ | |
885 | title = _("Attaching"); | |
886 | break; | |
887 | case DFU_STATE_DFU_DNLOAD_IDLE: | |
888 | /* TRANSLATORS: when copying from host to device */ | |
889 | title = _("Downloading"); | |
890 | break; | |
891 | case DFU_STATE_DFU_UPLOAD_IDLE: | |
892 | /* TRANSLATORS: when copying from device to host */ | |
893 | title = _("Uploading"); | |
894 | break; | |
895 | default: | |
896 | g_debug ("ignoring %s", dfu_state_to_string (state)); | |
897 | break; | |
898 | } | |
899 | ||
900 | /* show title and then pad */ | |
901 | if (title != NULL) { | |
902 | g_print ("%s ", title); | |
903 | for (i = strlen (title); i < 15; i++) | |
904 | g_print (" "); | |
905 | g_print (": "); | |
906 | } | |
907 | ||
908 | /* reset the progress bar */ | |
909 | switch (state) { | |
910 | case DFU_STATE_APP_DETACH: | |
911 | case DFU_STATE_DFU_DNLOAD_IDLE: | |
912 | case DFU_STATE_DFU_MANIFEST_WAIT_RESET: | |
913 | case DFU_STATE_DFU_UPLOAD_IDLE: | |
914 | g_debug ("resetting progress bar"); | |
915 | helper->marks_shown = 0; | |
916 | break; | |
917 | default: | |
918 | break; | |
919 | } | |
920 | ||
921 | /* ignore if the same */ | |
922 | helper->last_state = state; | |
923 | } | |
924 | ||
925 | /** | |
926 | * fu_tool_percentage_changed_cb: | |
927 | **/ | |
928 | static void | |
929 | fu_tool_percentage_changed_cb (DfuDevice *device, | |
930 | guint percentage, | |
931 | DfuToolProgressHelper *helper) | |
932 | { | |
933 | guint marks_now; | |
934 | guint i; | |
935 | ||
936 | /* add any sections */ | |
937 | marks_now = percentage * helper->marks_total / 100; | |
938 | for (i = helper->marks_shown; i < marks_now; i++) | |
939 | g_print ("#"); | |
940 | helper->marks_shown = marks_now; | |
941 | ||
942 | /* this state done */ | |
943 | if (percentage == 100) | |
944 | g_print ("\n"); | |
945 | } | |
946 | ||
947 | /** | |
948 | * dfu_tool_read_alt: | |
949 | **/ | |
950 | static gboolean | |
951 | dfu_tool_read_alt (DfuToolPrivate *priv, gchar **values, GError **error) | |
952 | { | |
953 | DfuTargetTransferFlags flags = DFU_TARGET_TRANSFER_FLAG_NONE; | |
954 | DfuToolProgressHelper helper; | |
955 | g_autofree gchar *str_debug = NULL; | |
956 | g_autoptr(DfuDevice) device = NULL; | |
957 | g_autoptr(DfuFirmware) firmware = NULL; | |
958 | g_autoptr(DfuImage) image = NULL; | |
959 | g_autoptr(DfuTarget) target = NULL; | |
960 | g_autoptr(GFile) file = NULL; | |
961 | ||
962 | /* check args */ | |
963 | if (g_strv_length (values) < 2) { | |
964 | g_set_error_literal (error, | |
965 | DFU_ERROR, | |
966 | DFU_ERROR_INTERNAL, | |
967 | "Invalid arguments, expected " | |
968 | "FILENAME DEVICE-ALT-NAME|DEVICE-ALT-ID"); | |
969 | return FALSE; | |
970 | } | |
971 | ||
972 | /* open correct device */ | |
973 | device = dfu_tool_get_defalt_device (priv, error); | |
974 | if (device == NULL) | |
975 | return FALSE; | |
976 | if (priv->transfer_size > 0) | |
977 | dfu_device_set_transfer_size (device, priv->transfer_size); | |
978 | if (!dfu_device_open (device, | |
979 | DFU_DEVICE_OPEN_FLAG_NONE, | |
980 | priv->cancellable, | |
981 | error)) | |
982 | return FALSE; | |
983 | ||
984 | /* set up progress */ | |
985 | helper.last_state = DFU_STATE_DFU_ERROR; | |
986 | helper.marks_total = 30; | |
987 | helper.marks_shown = 0; | |
988 | g_signal_connect (device, "state-changed", | |
989 | G_CALLBACK (fu_tool_state_changed_cb), &helper); | |
990 | g_signal_connect (device, "percentage-changed", | |
991 | G_CALLBACK (fu_tool_percentage_changed_cb), &helper); | |
992 | ||
993 | /* APP -> DFU */ | |
994 | if (dfu_device_get_mode (device) == DFU_MODE_RUNTIME) { | |
995 | g_debug ("detaching"); | |
996 | if (!dfu_device_detach (device, priv->cancellable, error)) | |
997 | return FALSE; | |
998 | if (!dfu_device_wait_for_replug (device, | |
999 | DFU_DEVICE_REPLUG_TIMEOUT, | |
1000 | priv->cancellable, | |
1001 | error)) | |
1002 | return FALSE; | |
1003 | ||
1004 | /* put back in same state */ | |
1005 | flags |= DFU_TARGET_TRANSFER_FLAG_ATTACH; | |
1006 | flags |= DFU_TARGET_TRANSFER_FLAG_WAIT_RUNTIME; | |
1007 | } | |
1008 | ||
1009 | /* transfer */ | |
1010 | target = dfu_device_get_target_by_alt_name (device, | |
1011 | values[1], | |
1012 | NULL); | |
1013 | if (target == NULL) { | |
1014 | gchar *endptr; | |
1015 | guint64 tmp = g_ascii_strtoull (values[1], &endptr, 10); | |
1016 | if (tmp > 0xff || endptr[0] != '\0') { | |
1017 | g_set_error (error, | |
1018 | DFU_ERROR, | |
1019 | DFU_ERROR_INTERNAL, | |
1020 | "Failed to parse alt-setting '%s'", | |
1021 | values[1]); | |
1022 | return FALSE; | |
1023 | } | |
1024 | target = dfu_device_get_target_by_alt_setting (device, | |
1025 | tmp, | |
1026 | error); | |
1027 | if (target == NULL) | |
1028 | return FALSE; | |
1029 | } | |
1030 | ||
1031 | /* do transfer */ | |
1032 | image = dfu_target_upload (target, flags, priv->cancellable, error); | |
1033 | if (image == NULL) | |
1034 | return FALSE; | |
1035 | ||
1036 | /* create new firmware object */ | |
1037 | firmware = dfu_firmware_new (); | |
1038 | dfu_firmware_set_format (firmware, DFU_FIRMWARE_FORMAT_DFU_1_0); | |
1039 | dfu_firmware_set_vid (firmware, dfu_device_get_runtime_vid (device)); | |
1040 | dfu_firmware_set_pid (firmware, dfu_device_get_runtime_pid (device)); | |
1041 | dfu_firmware_add_image (firmware, image); | |
1042 | ||
1043 | /* save file */ | |
1044 | file = g_file_new_for_path (values[0]); | |
1045 | if (!dfu_firmware_write_file (firmware, | |
1046 | file, | |
1047 | priv->cancellable, | |
1048 | error)) | |
1049 | return FALSE; | |
1050 | ||
1051 | /* print the new object */ | |
1052 | str_debug = dfu_firmware_to_string (firmware); | |
1053 | g_debug ("DFU: %s", str_debug); | |
1054 | ||
1055 | /* success */ | |
1056 | g_print ("%u bytes successfully uploaded from device\n", | |
1057 | dfu_image_get_size (image)); | |
1058 | return TRUE; | |
1059 | } | |
1060 | ||
1061 | /** | |
1062 | * dfu_tool_read: | |
1063 | **/ | |
1064 | static gboolean | |
1065 | dfu_tool_read (DfuToolPrivate *priv, gchar **values, GError **error) | |
1066 | { | |
1067 | DfuTargetTransferFlags flags = DFU_TARGET_TRANSFER_FLAG_NONE; | |
1068 | DfuToolProgressHelper helper; | |
1069 | g_autofree gchar *str_debug = NULL; | |
1070 | g_autoptr(DfuDevice) device = NULL; | |
1071 | g_autoptr(DfuFirmware) firmware = NULL; | |
1072 | g_autoptr(DfuImage) image = NULL; | |
1073 | g_autoptr(GFile) file = NULL; | |
1074 | ||
1075 | /* check args */ | |
1076 | if (g_strv_length (values) < 1) { | |
1077 | g_set_error_literal (error, | |
1078 | DFU_ERROR, | |
1079 | DFU_ERROR_INTERNAL, | |
1080 | "Invalid arguments, expected FILENAME"); | |
1081 | return FALSE; | |
1082 | } | |
1083 | ||
1084 | /* open correct device */ | |
1085 | device = dfu_tool_get_defalt_device (priv, error); | |
1086 | if (device == NULL) | |
1087 | return FALSE; | |
1088 | if (!dfu_device_open (device, | |
1089 | DFU_DEVICE_OPEN_FLAG_NONE, | |
1090 | priv->cancellable, | |
1091 | error)) | |
1092 | return FALSE; | |
1093 | ||
1094 | /* optional reset */ | |
1095 | if (dfu_device_get_mode (device) == DFU_MODE_RUNTIME) { | |
1096 | flags |= DFU_TARGET_TRANSFER_FLAG_DETACH; | |
1097 | flags |= DFU_TARGET_TRANSFER_FLAG_ATTACH; | |
1098 | flags |= DFU_TARGET_TRANSFER_FLAG_WAIT_RUNTIME; | |
1099 | } | |
1100 | ||
1101 | /* transfer */ | |
1102 | helper.last_state = DFU_STATE_DFU_ERROR; | |
1103 | helper.marks_total = 30; | |
1104 | helper.marks_shown = 0; | |
1105 | g_signal_connect (device, "state-changed", | |
1106 | G_CALLBACK (fu_tool_state_changed_cb), &helper); | |
1107 | g_signal_connect (device, "percentage-changed", | |
1108 | G_CALLBACK (fu_tool_percentage_changed_cb), &helper); | |
1109 | firmware = dfu_device_upload (device, | |
1110 | flags, | |
1111 | priv->cancellable, | |
1112 | error); | |
1113 | if (firmware == NULL) | |
1114 | return FALSE; | |
1115 | ||
1116 | /* save file */ | |
1117 | file = g_file_new_for_path (values[0]); | |
1118 | if (!dfu_firmware_write_file (firmware, | |
1119 | file, | |
1120 | priv->cancellable, | |
1121 | error)) | |
1122 | return FALSE; | |
1123 | ||
1124 | /* print the new object */ | |
1125 | str_debug = dfu_firmware_to_string (firmware); | |
1126 | g_debug ("DFU: %s", str_debug); | |
1127 | ||
1128 | /* success */ | |
1129 | g_print ("%u bytes successfully uploaded from device\n", | |
1130 | dfu_firmware_get_size (firmware)); | |
1131 | return TRUE; | |
1132 | } | |
1133 | ||
1134 | /** | |
1135 | * dfu_tool_get_device_string: | |
1136 | **/ | |
1137 | static gchar * | |
1138 | dfu_tool_get_device_string (DfuToolPrivate *priv, DfuDevice *device) | |
1139 | { | |
1140 | gchar *dstr; | |
1141 | GUsbDevice *dev; | |
1142 | g_autoptr(GError) error = NULL; | |
1143 | ||
1144 | /* open, and get status */ | |
1145 | dev = dfu_device_get_usb_dev (device); | |
1146 | if (dev == NULL) { | |
1147 | return g_strdup_printf ("%04x:%04x [%s]", | |
1148 | dfu_device_get_runtime_vid (device), | |
1149 | dfu_device_get_runtime_pid (device), | |
1150 | "removed"); | |
1151 | } | |
1152 | if (!dfu_device_open (device, | |
1153 | DFU_DEVICE_OPEN_FLAG_NONE, | |
1154 | priv->cancellable, | |
1155 | &error)) { | |
1156 | return g_strdup_printf ("%04x:%04x [%s]", | |
1157 | g_usb_device_get_vid (dev), | |
1158 | g_usb_device_get_pid (dev), | |
1159 | error->message); | |
1160 | } | |
1161 | dstr = g_strdup_printf ("%04x:%04x [%s:%s]", | |
1162 | g_usb_device_get_vid (dev), | |
1163 | g_usb_device_get_pid (dev), | |
1164 | dfu_state_to_string (dfu_device_get_state (device)), | |
1165 | dfu_status_to_string (dfu_device_get_status (device))); | |
1166 | dfu_device_close (device, NULL); | |
1167 | return dstr; | |
1168 | } | |
1169 | ||
1170 | /** | |
1171 | * dfu_tool_device_added_cb: | |
1172 | **/ | |
1173 | static void | |
1174 | dfu_tool_device_added_cb (DfuContext *context, | |
1175 | DfuDevice *device, | |
1176 | gpointer user_data) | |
1177 | { | |
1178 | DfuToolPrivate *priv = (DfuToolPrivate *) user_data; | |
1179 | g_autofree gchar *tmp; | |
1180 | tmp = dfu_tool_get_device_string (priv, device); | |
1181 | /* TRANSLATORS: this is when a device is hotplugged */ | |
1182 | dfu_tool_print_indent (_("Added"), tmp, 0); | |
1183 | } | |
1184 | ||
1185 | /** | |
1186 | * dfu_tool_device_removed_cb: | |
1187 | **/ | |
1188 | static void | |
1189 | dfu_tool_device_removed_cb (DfuContext *context, | |
1190 | DfuDevice *device, | |
1191 | gpointer user_data) | |
1192 | { | |
1193 | DfuToolPrivate *priv = (DfuToolPrivate *) user_data; | |
1194 | g_autofree gchar *tmp; | |
1195 | tmp = dfu_tool_get_device_string (priv, device); | |
1196 | /* TRANSLATORS: this is when a device is hotplugged */ | |
1197 | dfu_tool_print_indent (_("Removed"), tmp, 0); | |
1198 | } | |
1199 | ||
1200 | /** | |
1201 | * dfu_tool_device_changed_cb: | |
1202 | **/ | |
1203 | static void | |
1204 | dfu_tool_device_changed_cb (DfuContext *context, DfuDevice *device, gpointer user_data) | |
1205 | { | |
1206 | DfuToolPrivate *priv = (DfuToolPrivate *) user_data; | |
1207 | g_autofree gchar *tmp; | |
1208 | tmp = dfu_tool_get_device_string (priv, device); | |
1209 | /* TRANSLATORS: this is when a device is hotplugged */ | |
1210 | dfu_tool_print_indent (_("Changed"), tmp, 0); | |
1211 | } | |
1212 | ||
1213 | /** | |
1214 | * dfu_tool_watch_cancelled_cb: | |
1215 | **/ | |
1216 | static void | |
1217 | dfu_tool_watch_cancelled_cb (GCancellable *cancellable, gpointer user_data) | |
1218 | { | |
1219 | GMainLoop *loop = (GMainLoop *) user_data; | |
1220 | /* TRANSLATORS: this is when a device ctrl+c's a watch */ | |
1221 | g_print ("%s\n", _("Cancelled")); | |
1222 | g_main_loop_quit (loop); | |
1223 | } | |
1224 | ||
1225 | /** | |
1226 | * dfu_tool_parse_xtea_key: | |
1227 | **/ | |
1228 | static gboolean | |
1229 | dfu_tool_parse_xtea_key (const gchar *key, guint32 *keys, GError **error) | |
1230 | { | |
1231 | guint i; | |
1232 | guint key_len; | |
1233 | g_autofree gchar *key_pad = NULL; | |
1234 | ||
1235 | /* too long */ | |
1236 | key_len = strlen (key); | |
1237 | if (key_len > 32) { | |
1238 | g_set_error (error, | |
1239 | DFU_ERROR, | |
1240 | DFU_ERROR_NOT_SUPPORTED, | |
1241 | "Key string too long at %i chars, max 16", | |
1242 | key_len); | |
1243 | return FALSE; | |
1244 | } | |
1245 | ||
1246 | /* parse 4x32b values or generate a hash */ | |
1247 | if (key_len == 32) { | |
1248 | for (i = 0; i < 4; i++) { | |
1249 | gchar buf[] = "xxxxxxxx"; | |
1250 | gchar *endptr; | |
1251 | guint64 tmp; | |
1252 | ||
1253 | /* copy to 4-char buf (with NUL) */ | |
1254 | memcpy (buf, key + i*8, 8); | |
1255 | tmp = g_ascii_strtoull (buf, &endptr, 16); | |
1256 | if (endptr && endptr[0] != '\0') { | |
1257 | g_set_error (error, | |
1258 | DFU_ERROR, | |
1259 | DFU_ERROR_NOT_SUPPORTED, | |
1260 | "Failed to parse key '%s'", key); | |
1261 | return FALSE; | |
1262 | } | |
1263 | keys[3-i] = tmp; | |
1264 | } | |
1265 | } else { | |
1266 | gsize buf_len = 16; | |
1267 | g_autoptr(GChecksum) csum = NULL; | |
1268 | csum = g_checksum_new (G_CHECKSUM_MD5); | |
1269 | g_checksum_update (csum, (const guchar *) key, key_len); | |
1270 | g_checksum_get_digest (csum, (guint8 *) keys, &buf_len); | |
1271 | g_assert (buf_len == 16); | |
1272 | } | |
1273 | ||
1274 | /* success */ | |
1275 | g_debug ("using XTEA key %04x%04x%04x%04x", | |
1276 | keys[3], keys[2], keys[1], keys[0]); | |
1277 | return TRUE; | |
1278 | } | |
1279 | ||
1280 | /** | |
1281 | * dfu_tool_get_firmware_contents_default: | |
1282 | **/ | |
1283 | static guint8 * | |
1284 | dfu_tool_get_firmware_contents_default (DfuFirmware *firmware, | |
1285 | gsize *length, | |
1286 | GError **error) | |
1287 | { | |
1288 | DfuElement *element; | |
1289 | DfuImage *image; | |
1290 | GBytes *contents; | |
1291 | ||
1292 | image = dfu_firmware_get_image_default (firmware); | |
1293 | if (image == NULL) { | |
1294 | g_set_error_literal (error, | |
1295 | DFU_ERROR, | |
1296 | DFU_ERROR_INTERNAL, | |
1297 | "No default image"); | |
1298 | return NULL; | |
1299 | } | |
1300 | element = dfu_image_get_element (image, 0); | |
1301 | if (element == NULL) { | |
1302 | g_set_error_literal (error, | |
1303 | DFU_ERROR, | |
1304 | DFU_ERROR_INTERNAL, | |
1305 | "No default element"); | |
1306 | return NULL; | |
1307 | } | |
1308 | contents = dfu_element_get_contents (element); | |
1309 | if (contents == NULL) { | |
1310 | g_set_error_literal (error, | |
1311 | DFU_ERROR, | |
1312 | DFU_ERROR_INTERNAL, | |
1313 | "No image contents"); | |
1314 | return NULL; | |
1315 | } | |
1316 | return (guint8 *) g_bytes_get_data (contents, length); | |
1317 | } | |
1318 | ||
1319 | #define XTEA_DELTA 0x9e3779b9 | |
1320 | #define XTEA_NUM_ROUNDS 32 | |
1321 | ||
1322 | /** | |
1323 | * dfu_tool_encrypt_xtea: | |
1324 | **/ | |
1325 | static void | |
1326 | dfu_tool_encrypt_xtea (const guint32 key[4], guint8 *data, guint16 length) | |
1327 | { | |
1328 | guint32 sum; | |
1329 | guint32 *tmp = (guint32 *) data; | |
1330 | guint32 v0; | |
1331 | guint32 v1; | |
1332 | guint8 i; | |
1333 | guint j; | |
1334 | ||
1335 | for (j = 0; j < length / 4; j += 2) { | |
1336 | sum = 0; | |
1337 | v0 = tmp[j]; | |
1338 | v1 = tmp[j+1]; | |
1339 | for (i = 0; i < XTEA_NUM_ROUNDS; i++) { | |
1340 | v0 += (((v1 << 4) ^ (v1 >> 5)) + v1) ^ (sum + key[sum & 3]); | |
1341 | sum += XTEA_DELTA; | |
1342 | v1 += (((v0 << 4) ^ (v0 >> 5)) + v0) ^ (sum + key[(sum >> 11) & 3]); | |
1343 | } | |
1344 | tmp[j] = v0; | |
1345 | tmp[j+1] = v1; | |
1346 | } | |
1347 | } | |
1348 | ||
1349 | /** | |
1350 | * dfu_tool_decrypt_xtea: | |
1351 | **/ | |
1352 | static void | |
1353 | dfu_tool_decrypt_xtea (const guint32 key[4], guint8 *data, guint16 length) | |
1354 | { | |
1355 | guint32 sum; | |
1356 | guint32 *tmp = (guint32 *) data; | |
1357 | guint32 v0; | |
1358 | guint32 v1; | |
1359 | guint8 i; | |
1360 | guint j; | |
1361 | ||
1362 | for (j = 0; j < length / 4; j += 2) { | |
1363 | v0 = tmp[j]; | |
1364 | v1 = tmp[j+1]; | |
1365 | sum = XTEA_DELTA * XTEA_NUM_ROUNDS; | |
1366 | for (i = 0; i < XTEA_NUM_ROUNDS; i++) { | |
1367 | v1 -= (((v0 << 4) ^ (v0 >> 5)) + v0) ^ (sum + key[(sum >> 11) & 3]); | |
1368 | sum -= XTEA_DELTA; | |
1369 | v0 -= (((v1 << 4) ^ (v1 >> 5)) + v1) ^ (sum + key[sum & 3]); | |
1370 | } | |
1371 | tmp[j] = v0; | |
1372 | tmp[j+1] = v1; | |
1373 | } | |
1374 | } | |
1375 | ||
1376 | /** | |
1377 | * dfu_tool_encrypt: | |
1378 | **/ | |
1379 | static gboolean | |
1380 | dfu_tool_encrypt (DfuToolPrivate *priv, gchar **values, GError **error) | |
1381 | { | |
1382 | gsize len; | |
1383 | guint8 *data; | |
1384 | g_autoptr(DfuFirmware) firmware = NULL; | |
1385 | g_autoptr(GFile) file_in = NULL; | |
1386 | g_autoptr(GFile) file_out = NULL; | |
1387 | ||
1388 | /* check args */ | |
1389 | if (g_strv_length (values) < 4) { | |
1390 | g_set_error_literal (error, | |
1391 | DFU_ERROR, | |
1392 | DFU_ERROR_INTERNAL, | |
1393 | "Invalid arguments, expected " | |
1394 | "FILENAME-IN FILENAME-OUT TYPE KEY" | |
1395 | " -- e.g. firmware.dfu firmware.xdfu xtea deadbeef"); | |
1396 | return FALSE; | |
1397 | } | |
1398 | ||
1399 | /* check extensions */ | |
1400 | if (!priv->force) { | |
1401 | if (!g_str_has_suffix (values[0], ".dfu")) { | |
1402 | g_set_error_literal (error, | |
1403 | DFU_ERROR, | |
1404 | DFU_ERROR_NOT_SUPPORTED, | |
1405 | "Invalid filename, expected *.dfu"); | |
1406 | return FALSE; | |
1407 | } | |
1408 | if (!g_str_has_suffix (values[1], ".xdfu")) { | |
1409 | g_set_error_literal (error, | |
1410 | DFU_ERROR, | |
1411 | DFU_ERROR_NOT_SUPPORTED, | |
1412 | "Invalid filename, expected *.xdfu"); | |
1413 | return FALSE; | |
1414 | } | |
1415 | } | |
1416 | ||
1417 | /* open */ | |
1418 | file_in = g_file_new_for_path (values[0]); | |
1419 | firmware = dfu_firmware_new (); | |
1420 | if (!dfu_firmware_parse_file (firmware, file_in, | |
1421 | DFU_FIRMWARE_PARSE_FLAG_NONE, | |
1422 | priv->cancellable, | |
1423 | error)) { | |
1424 | return FALSE; | |
1425 | } | |
1426 | ||
1427 | /* get data */ | |
1428 | data = dfu_tool_get_firmware_contents_default (firmware, &len, error); | |
1429 | if (data == NULL) | |
1430 | return FALSE; | |
1431 | ||
1432 | /* check type */ | |
1433 | if (g_strcmp0 (values[2], "xtea") == 0) { | |
1434 | guint32 key[4]; | |
1435 | if (!dfu_tool_parse_xtea_key (values[3], key, error)) | |
1436 | return FALSE; | |
1437 | dfu_tool_encrypt_xtea (key, data, len); | |
1438 | dfu_firmware_set_metadata (firmware, | |
1439 | DFU_METADATA_KEY_CIPHER_KIND, | |
1440 | "XTEA"); | |
1441 | } else { | |
1442 | g_set_error (error, | |
1443 | DFU_ERROR, | |
1444 | DFU_ERROR_INTERNAL, | |
1445 | "unknown type '%s', expected [xtea]", | |
1446 | values[2]); | |
1447 | return FALSE; | |
1448 | } | |
1449 | ||
1450 | /* write out new file */ | |
1451 | file_out = g_file_new_for_path (values[1]); | |
1452 | g_debug ("wrote %s", values[1]); | |
1453 | return dfu_firmware_write_file (firmware, | |
1454 | file_out, | |
1455 | priv->cancellable, | |
1456 | error); | |
1457 | } | |
1458 | ||
1459 | /** | |
1460 | * dfu_tool_decrypt: | |
1461 | **/ | |
1462 | static gboolean | |
1463 | dfu_tool_decrypt (DfuToolPrivate *priv, gchar **values, GError **error) | |
1464 | { | |
1465 | gsize len; | |
1466 | guint8 *data; | |
1467 | g_autoptr(DfuFirmware) firmware = NULL; | |
1468 | g_autoptr(GFile) file_in = NULL; | |
1469 | g_autoptr(GFile) file_out = NULL; | |
1470 | ||
1471 | /* check args */ | |
1472 | if (g_strv_length (values) < 4) { | |
1473 | g_set_error_literal (error, | |
1474 | DFU_ERROR, | |
1475 | DFU_ERROR_INTERNAL, | |
1476 | "Invalid arguments, expected " | |
1477 | "FILENAME-IN FILENAME-OUT TYPE KEY" | |
1478 | " -- e.g. firmware.xdfu firmware.dfu xtea deadbeef"); | |
1479 | return FALSE; | |
1480 | } | |
1481 | ||
1482 | /* check extensions */ | |
1483 | if (!priv->force) { | |
1484 | if (!g_str_has_suffix (values[0], ".xdfu")) { | |
1485 | g_set_error_literal (error, | |
1486 | DFU_ERROR, | |
1487 | DFU_ERROR_NOT_SUPPORTED, | |
1488 | "Invalid filename, expected *.xdfu"); | |
1489 | return FALSE; | |
1490 | } | |
1491 | if (!g_str_has_suffix (values[1], ".dfu")) { | |
1492 | g_set_error_literal (error, | |
1493 | DFU_ERROR, | |
1494 | DFU_ERROR_NOT_SUPPORTED, | |
1495 | "Invalid filename, expected *.dfu"); | |
1496 | return FALSE; | |
1497 | } | |
1498 | } | |
1499 | ||
1500 | /* open */ | |
1501 | file_in = g_file_new_for_path (values[0]); | |
1502 | firmware = dfu_firmware_new (); | |
1503 | if (!dfu_firmware_parse_file (firmware, file_in, | |
1504 | DFU_FIRMWARE_PARSE_FLAG_NONE, | |
1505 | priv->cancellable, | |
1506 | error)) { | |
1507 | return FALSE; | |
1508 | } | |
1509 | ||
1510 | /* get data */ | |
1511 | data = dfu_tool_get_firmware_contents_default (firmware, &len, error); | |
1512 | if (data == NULL) | |
1513 | return FALSE; | |
1514 | ||
1515 | /* check type */ | |
1516 | if (g_strcmp0 (values[2], "xtea") == 0) { | |
1517 | guint32 key[4]; | |
1518 | if (!dfu_tool_parse_xtea_key (values[3], key, error)) | |
1519 | return FALSE; | |
1520 | dfu_tool_decrypt_xtea (key, data, len); | |
1521 | dfu_firmware_remove_metadata (firmware, | |
1522 | DFU_METADATA_KEY_CIPHER_KIND); | |
1523 | } else { | |
1524 | g_set_error (error, | |
1525 | DFU_ERROR, | |
1526 | DFU_ERROR_INTERNAL, | |
1527 | "unknown type '%s', expected [xtea]", | |
1528 | values[2]); | |
1529 | return FALSE; | |
1530 | } | |
1531 | ||
1532 | /* write out new file */ | |
1533 | file_out = g_file_new_for_path (values[1]); | |
1534 | g_debug ("wrote %s", values[1]); | |
1535 | return dfu_firmware_write_file (firmware, | |
1536 | file_out, | |
1537 | priv->cancellable, | |
1538 | error); | |
1539 | } | |
1540 | ||
1541 | /** | |
1542 | * dfu_tool_watch: | |
1543 | **/ | |
1544 | static gboolean | |
1545 | dfu_tool_watch (DfuToolPrivate *priv, gchar **values, GError **error) | |
1546 | { | |
1547 | guint i; | |
1548 | DfuDevice *device; | |
1549 | g_autoptr(DfuContext) dfu_context = NULL; | |
1550 | g_autoptr(GMainLoop) loop = NULL; | |
1551 | g_autoptr(GPtrArray) devices = NULL; | |
1552 | ||
1553 | /* get all the DFU devices */ | |
1554 | dfu_context = dfu_context_new (); | |
1555 | dfu_context_enumerate (dfu_context, NULL); | |
1556 | ||
1557 | /* print what's already attached */ | |
1558 | devices = dfu_context_get_devices (dfu_context); | |
1559 | for (i = 0; i < devices->len; i++) { | |
1560 | device = g_ptr_array_index (devices, i); | |
1561 | dfu_tool_device_added_cb (dfu_context, device, priv); | |
1562 | } | |
1563 | ||
1564 | /* watch for any hotplugged device */ | |
1565 | loop = g_main_loop_new (NULL, FALSE); | |
1566 | g_signal_connect (dfu_context, "device-added", | |
1567 | G_CALLBACK (dfu_tool_device_added_cb), priv); | |
1568 | g_signal_connect (dfu_context, "device-removed", | |
1569 | G_CALLBACK (dfu_tool_device_removed_cb), priv); | |
1570 | g_signal_connect (dfu_context, "device-changed", | |
1571 | G_CALLBACK (dfu_tool_device_changed_cb), priv); | |
1572 | g_signal_connect (priv->cancellable, "cancelled", | |
1573 | G_CALLBACK (dfu_tool_watch_cancelled_cb), loop); | |
1574 | g_main_loop_run (loop); | |
1575 | return TRUE; | |
1576 | } | |
1577 | ||
1578 | /** | |
1579 | * dfu_tool_dump: | |
1580 | **/ | |
1581 | static gboolean | |
1582 | dfu_tool_dump (DfuToolPrivate *priv, gchar **values, GError **error) | |
1583 | { | |
1584 | DfuFirmwareParseFlags flags = DFU_FIRMWARE_PARSE_FLAG_NONE; | |
1585 | guint i; | |
1586 | ||
1587 | /* check args */ | |
1588 | if (g_strv_length (values) < 1) { | |
1589 | g_set_error_literal (error, | |
1590 | DFU_ERROR, | |
1591 | DFU_ERROR_INTERNAL, | |
1592 | "Invalid arguments, expected FILENAME"); | |
1593 | return FALSE; | |
1594 | } | |
1595 | ||
1596 | /* dump corrupt files */ | |
1597 | if (priv->force) { | |
1598 | flags |= DFU_FIRMWARE_PARSE_FLAG_NO_CRC_TEST; | |
1599 | flags |= DFU_FIRMWARE_PARSE_FLAG_NO_VERSION_TEST; | |
1600 | } | |
1601 | ||
1602 | /* open files */ | |
1603 | for (i = 0; values[i] != NULL; i++) { | |
1604 | g_autoptr(DfuFirmware) firmware = NULL; | |
1605 | g_autoptr(GFile) file = NULL; | |
1606 | g_autoptr(GError) error_local = NULL; | |
1607 | ||
1608 | /* dump to screen */ | |
1609 | g_print ("Loading %s:\n", values[i]); | |
1610 | firmware = dfu_firmware_new (); | |
1611 | file = g_file_new_for_path (values[i]); | |
1612 | if (!dfu_firmware_parse_file (firmware, file, flags, | |
1613 | priv->cancellable, | |
1614 | &error_local)) { | |
1615 | g_print ("Failed to load firmware: %s\n", | |
1616 | error_local->message); | |
1617 | continue; | |
1618 | } | |
1619 | g_print ("%s\n", dfu_firmware_to_string (firmware)); | |
1620 | } | |
1621 | return TRUE; | |
1622 | } | |
1623 | ||
1624 | /** | |
1625 | * dfu_tool_write_alt: | |
1626 | **/ | |
1627 | static gboolean | |
1628 | dfu_tool_write_alt (DfuToolPrivate *priv, gchar **values, GError **error) | |
1629 | { | |
1630 | DfuImage *image; | |
1631 | DfuTargetTransferFlags flags = DFU_TARGET_TRANSFER_FLAG_VERIFY; | |
1632 | DfuToolProgressHelper helper; | |
1633 | g_autofree gchar *str_debug = NULL; | |
1634 | g_autoptr(DfuDevice) device = NULL; | |
1635 | g_autoptr(DfuFirmware) firmware = NULL; | |
1636 | g_autoptr(DfuTarget) target = NULL; | |
1637 | g_autoptr(GFile) file = NULL; | |
1638 | ||
1639 | /* check args */ | |
1640 | if (g_strv_length (values) < 2) { | |
1641 | g_set_error_literal (error, | |
1642 | DFU_ERROR, | |
1643 | DFU_ERROR_INTERNAL, | |
1644 | "Invalid arguments, expected " | |
1645 | "FILENAME DEVICE-ALT-NAME|DEVICE-ALT-ID " | |
1646 | "[IMAGE-ALT-NAME|IMAGE-ALT-ID]"); | |
1647 | return FALSE; | |
1648 | } | |
1649 | ||
1650 | /* open file */ | |
1651 | firmware = dfu_firmware_new (); | |
1652 | file = g_file_new_for_path (values[0]); | |
1653 | if (!dfu_firmware_parse_file (firmware, file, | |
1654 | DFU_FIRMWARE_PARSE_FLAG_NONE, | |
1655 | priv->cancellable, error)) | |
1656 | return FALSE; | |
1657 | ||
1658 | /* open correct device */ | |
1659 | device = dfu_tool_get_defalt_device (priv, error); | |
1660 | if (device == NULL) | |
1661 | return FALSE; | |
1662 | if (priv->transfer_size > 0) | |
1663 | dfu_device_set_transfer_size (device, priv->transfer_size); | |
1664 | if (!dfu_device_open (device, | |
1665 | DFU_DEVICE_OPEN_FLAG_NONE, | |
1666 | priv->cancellable, | |
1667 | error)) | |
1668 | return FALSE; | |
1669 | ||
1670 | /* set up progress */ | |
1671 | helper.last_state = DFU_STATE_DFU_ERROR; | |
1672 | helper.marks_total = 30; | |
1673 | helper.marks_shown = 0; | |
1674 | g_signal_connect (device, "state-changed", | |
1675 | G_CALLBACK (fu_tool_state_changed_cb), &helper); | |
1676 | g_signal_connect (device, "percentage-changed", | |
1677 | G_CALLBACK (fu_tool_percentage_changed_cb), &helper); | |
1678 | ||
1679 | /* APP -> DFU */ | |
1680 | if (dfu_device_get_mode (device) == DFU_MODE_RUNTIME) { | |
1681 | g_debug ("detaching"); | |
1682 | if (!dfu_device_detach (device, priv->cancellable, error)) | |
1683 | return FALSE; | |
1684 | if (!dfu_device_wait_for_replug (device, 5000, priv->cancellable, error)) | |
1685 | return FALSE; | |
1686 | ||
1687 | /* put back in same state */ | |
1688 | flags |= DFU_TARGET_TRANSFER_FLAG_ATTACH; | |
1689 | flags |= DFU_TARGET_TRANSFER_FLAG_WAIT_RUNTIME; | |
1690 | } | |
1691 | ||
1692 | /* print the new object */ | |
1693 | str_debug = dfu_firmware_to_string (firmware); | |
1694 | g_debug ("DFU: %s", str_debug); | |
1695 | ||
1696 | /* get correct target on device */ | |
1697 | target = dfu_device_get_target_by_alt_name (device, | |
1698 | values[1], | |
1699 | NULL); | |
1700 | if (target == NULL) { | |
1701 | gchar *endptr; | |
1702 | guint64 tmp = g_ascii_strtoull (values[1], &endptr, 10); | |
1703 | if (tmp > 0xff || endptr[0] != '\0') { | |
1704 | g_set_error (error, | |
1705 | DFU_ERROR, | |
1706 | DFU_ERROR_INTERNAL, | |
1707 | "Failed to parse alt-setting '%s'", | |
1708 | values[1]); | |
1709 | return FALSE; | |
1710 | } | |
1711 | target = dfu_device_get_target_by_alt_setting (device, | |
1712 | tmp, | |
1713 | error); | |
1714 | if (target == NULL) | |
1715 | return FALSE; | |
1716 | } | |
1717 | ||
1718 | /* allow overriding the firmware alt-setting */ | |
1719 | if (g_strv_length (values) > 2) { | |
1720 | image = dfu_firmware_get_image_by_name (firmware, values[2]); | |
1721 | if (image == NULL) { | |
1722 | gchar *endptr; | |
1723 | guint64 tmp = g_ascii_strtoull (values[2], &endptr, 10); | |
1724 | if (tmp > 0xff || endptr[0] != '\0') { | |
1725 | g_set_error (error, | |
1726 | DFU_ERROR, | |
1727 | DFU_ERROR_INTERNAL, | |
1728 | "Failed to parse image alt-setting '%s'", | |
1729 | values[2]); | |
1730 | return FALSE; | |
1731 | } | |
1732 | image = dfu_firmware_get_image (firmware, tmp); | |
1733 | if (image == NULL) { | |
1734 | g_set_error (error, | |
1735 | DFU_ERROR, | |
1736 | DFU_ERROR_INVALID_FILE, | |
1737 | "could not locate image in firmware for %02x", | |
1738 | (guint) tmp); | |
1739 | return FALSE; | |
1740 | } | |
1741 | } | |
1742 | } else { | |
1743 | g_print ("WARNING: Using default firmware image\n"); | |
1744 | image = dfu_firmware_get_image_default (firmware); | |
1745 | if (image == NULL) { | |
1746 | g_set_error_literal (error, | |
1747 | DFU_ERROR, | |
1748 | DFU_ERROR_INVALID_FILE, | |
1749 | "no default image"); | |
1750 | return FALSE; | |
1751 | } | |
1752 | } | |
1753 | ||
1754 | /* allow forcing firmware kinds */ | |
1755 | if (priv->force) { | |
1756 | flags |= DFU_TARGET_TRANSFER_FLAG_ANY_CIPHER; | |
1757 | } | |
1758 | ||
1759 | /* transfer */ | |
1760 | if (!dfu_target_download (target, | |
1761 | image, | |
1762 | flags, | |
1763 | priv->cancellable, | |
1764 | error)) | |
1765 | return FALSE; | |
1766 | ||
1767 | /* success */ | |
1768 | g_print ("%u bytes successfully downloaded to device\n", | |
1769 | dfu_image_get_size (image)); | |
1770 | return TRUE; | |
1771 | } | |
1772 | ||
1773 | /** | |
1774 | * dfu_tool_write: | |
1775 | **/ | |
1776 | static gboolean | |
1777 | dfu_tool_write (DfuToolPrivate *priv, gchar **values, GError **error) | |
1778 | { | |
1779 | DfuTargetTransferFlags flags = DFU_TARGET_TRANSFER_FLAG_VERIFY; | |
1780 | DfuToolProgressHelper helper; | |
1781 | g_autofree gchar *str_debug = NULL; | |
1782 | g_autoptr(DfuDevice) device = NULL; | |
1783 | g_autoptr(DfuFirmware) firmware = NULL; | |
1784 | g_autoptr(GFile) file = NULL; | |
1785 | ||
1786 | /* check args */ | |
1787 | if (g_strv_length (values) < 1) { | |
1788 | g_set_error_literal (error, | |
1789 | DFU_ERROR, | |
1790 | DFU_ERROR_INTERNAL, | |
1791 | "Invalid arguments, expected FILENAME"); | |
1792 | return FALSE; | |
1793 | } | |
1794 | ||
1795 | /* open file */ | |
1796 | firmware = dfu_firmware_new (); | |
1797 | file = g_file_new_for_path (values[0]); | |
1798 | if (!dfu_firmware_parse_file (firmware, file, | |
1799 | DFU_FIRMWARE_PARSE_FLAG_NONE, | |
1800 | priv->cancellable, error)) | |
1801 | return FALSE; | |
1802 | ||
1803 | /* open correct device */ | |
1804 | device = dfu_tool_get_defalt_device (priv, error); | |
1805 | if (device == NULL) | |
1806 | return FALSE; | |
1807 | if (!dfu_device_open (device, | |
1808 | DFU_DEVICE_OPEN_FLAG_NONE, | |
1809 | priv->cancellable, | |
1810 | error)) | |
1811 | return FALSE; | |
1812 | ||
1813 | /* print the new object */ | |
1814 | str_debug = dfu_firmware_to_string (firmware); | |
1815 | g_debug ("DFU: %s", str_debug); | |
1816 | ||
1817 | /* put in correct mode */ | |
1818 | if (dfu_device_get_mode (device) == DFU_MODE_RUNTIME) { | |
1819 | flags |= DFU_TARGET_TRANSFER_FLAG_DETACH; | |
1820 | flags |= DFU_TARGET_TRANSFER_FLAG_ATTACH; | |
1821 | flags |= DFU_TARGET_TRANSFER_FLAG_WAIT_RUNTIME; | |
1822 | } | |
1823 | ||
1824 | /* allow wildcards */ | |
1825 | if (priv->force) { | |
1826 | flags |= DFU_TARGET_TRANSFER_FLAG_WILDCARD_VID; | |
1827 | flags |= DFU_TARGET_TRANSFER_FLAG_WILDCARD_PID; | |
1828 | flags |= DFU_TARGET_TRANSFER_FLAG_ANY_CIPHER; | |
1829 | } | |
1830 | ||
1831 | /* transfer */ | |
1832 | helper.last_state = DFU_STATE_DFU_ERROR; | |
1833 | helper.marks_total = 30; | |
1834 | helper.marks_shown = 0; | |
1835 | g_signal_connect (device, "state-changed", | |
1836 | G_CALLBACK (fu_tool_state_changed_cb), &helper); | |
1837 | g_signal_connect (device, "percentage-changed", | |
1838 | G_CALLBACK (fu_tool_percentage_changed_cb), &helper); | |
1839 | if (!dfu_device_download (device, | |
1840 | firmware, | |
1841 | flags, | |
1842 | priv->cancellable, | |
1843 | error)) | |
1844 | return FALSE; | |
1845 | ||
1846 | /* success */ | |
1847 | g_print ("%u bytes successfully downloaded to device\n", | |
1848 | dfu_firmware_get_size (firmware)); | |
1849 | return TRUE; | |
1850 | } | |
1851 | ||
1852 | /** | |
1853 | * dfu_tool_list_target: | |
1854 | **/ | |
1855 | static void | |
1856 | dfu_tool_list_target (DfuTarget *target) | |
1857 | { | |
1858 | DfuCipherKind cipher_kind; | |
1859 | GPtrArray *sectors; | |
1860 | const gchar *tmp; | |
1861 | guint i; | |
1862 | g_autofree gchar *alt_id = NULL; | |
1863 | g_autoptr(GError) error_local = NULL; | |
1864 | ||
1865 | /* TRANSLATORS: the identifier name please */ | |
1866 | alt_id = g_strdup_printf ("%i", dfu_target_get_alt_setting (target)); | |
1867 | dfu_tool_print_indent (_("ID"), alt_id, 1); | |
1868 | ||
1869 | /* this is optional */ | |
1870 | tmp = dfu_target_get_alt_name (target, NULL); | |
1871 | if (tmp != NULL) { | |
1872 | /* TRANSLATORS: interface name, e.g. "Flash" */ | |
1873 | dfu_tool_print_indent (_("Name"), tmp, 2); | |
1874 | } | |
1875 | ||
1876 | cipher_kind = dfu_target_get_cipher_kind (target); | |
1877 | /* TRANSLATORS: this is the encryption method used when writing */ | |
1878 | dfu_tool_print_indent (_("Cipher"), dfu_cipher_kind_to_string (cipher_kind), 2); | |
1879 | ||
1880 | /* print sector information */ | |
1881 | sectors = dfu_target_get_sectors (target); | |
1882 | for (i = 0; i < sectors->len; i++) { | |
1883 | DfuSector *sector; | |
1884 | g_autofree gchar *msg = NULL; | |
1885 | g_autofree gchar *title = NULL; | |
1886 | sector = g_ptr_array_index (sectors, i); | |
1887 | msg = dfu_sector_to_string (sector); | |
1888 | /* TRANSLATORS: these are areas of memory on the chip */ | |
1889 | title = g_strdup_printf ("%s 0x%02x", _("Region"), i); | |
1890 | dfu_tool_print_indent (title, msg, 2); | |
1891 | } | |
1892 | } | |
1893 | ||
1894 | /** | |
1895 | * dfu_tool_list: | |
1896 | **/ | |
1897 | static gboolean | |
1898 | dfu_tool_list (DfuToolPrivate *priv, gchar **values, GError **error) | |
1899 | { | |
1900 | guint i; | |
1901 | g_autoptr(DfuContext) dfu_context = NULL; | |
1902 | g_autoptr(GPtrArray) devices = NULL; | |
1903 | ||
1904 | /* get all the connected USB devices */ | |
1905 | dfu_context = dfu_context_new (); | |
1906 | dfu_context_enumerate (dfu_context, NULL); | |
1907 | devices = dfu_context_get_devices (dfu_context); | |
1908 | for (i = 0; i < devices->len; i++) { | |
1909 | DfuDevice *device = NULL; | |
1910 | DfuTarget *target; | |
1911 | GUsbDevice *dev; | |
1912 | GPtrArray *dfu_targets; | |
1913 | const gchar *tmp; | |
1914 | guint j; | |
1915 | g_autofree gchar *quirks = NULL; | |
1916 | g_autofree gchar *version = NULL; | |
1917 | g_autoptr(GError) error_local = NULL; | |
1918 | ||
1919 | /* device specific */ | |
1920 | device = g_ptr_array_index (devices, i); | |
1921 | dev = dfu_device_get_usb_dev (device); | |
1922 | version = as_utils_version_from_uint16 (g_usb_device_get_release (dev), | |
1923 | AS_VERSION_PARSE_FLAG_NONE); | |
1924 | g_print ("%s %04x:%04x [v%s]:\n", | |
1925 | /* TRANSLATORS: detected a DFU device */ | |
1926 | _("Found"), | |
1927 | g_usb_device_get_vid (dev), | |
1928 | g_usb_device_get_pid (dev), | |
1929 | version); | |
1930 | ||
1931 | /* open */ | |
1932 | if (!dfu_device_open (device, | |
1933 | DFU_DEVICE_OPEN_FLAG_NONE, | |
1934 | priv->cancellable, | |
1935 | &error_local)) { | |
1936 | if (g_error_matches (error_local, | |
1937 | DFU_ERROR, | |
1938 | DFU_ERROR_PERMISSION_DENIED)) { | |
1939 | /* TRANSLATORS: probably not run as root... */ | |
1940 | dfu_tool_print_indent (_("Status"), _("Unknown: permission denied"), 2); | |
1941 | } else { | |
1942 | /* TRANSLATORS: device has failed to report status */ | |
1943 | dfu_tool_print_indent (_("Status"), error_local->message, 2); | |
1944 | } | |
1945 | continue; | |
1946 | } | |
1947 | ||
1948 | tmp = dfu_mode_to_string (dfu_device_get_mode (device)); | |
1949 | /* TRANSLATORS: device mode, e.g. runtime or DFU */ | |
1950 | dfu_tool_print_indent (_("Mode"), tmp, 1); | |
1951 | ||
1952 | tmp = dfu_status_to_string (dfu_device_get_status (device)); | |
1953 | /* TRANSLATORS: device status, e.g. "OK" */ | |
1954 | dfu_tool_print_indent (_("Status"), tmp, 1); | |
1955 | ||
1956 | tmp = dfu_state_to_string (dfu_device_get_state (device)); | |
1957 | /* TRANSLATORS: device state, i.e. appIDLE */ | |
1958 | dfu_tool_print_indent (_("State"), tmp, 1); | |
1959 | ||
1960 | /* quirks are NULL if none are set */ | |
1961 | quirks = dfu_device_get_quirks_as_string (device); | |
1962 | if (quirks != NULL) { | |
1963 | /* TRANSLATORS: device quirks, i.e. things that | |
1964 | * it does that we have to work around */ | |
1965 | dfu_tool_print_indent (_("Quirks"), quirks, 1); | |
1966 | } | |
1967 | ||
1968 | /* list targets */ | |
1969 | dfu_targets = dfu_device_get_targets (device); | |
1970 | for (j = 0; j < dfu_targets->len; j++) { | |
1971 | target = g_ptr_array_index (dfu_targets, j); | |
1972 | dfu_tool_list_target (target); | |
1973 | } | |
1974 | } | |
1975 | return TRUE; | |
1976 | } | |
1977 | ||
1978 | /** | |
1979 | * dfu_tool_detach: | |
1980 | **/ | |
1981 | static gboolean | |
1982 | dfu_tool_detach (DfuToolPrivate *priv, gchar **values, GError **error) | |
1983 | { | |
1984 | g_autoptr(DfuDevice) device = NULL; | |
1985 | ||
1986 | /* open correct device */ | |
1987 | device = dfu_tool_get_defalt_device (priv, error); | |
1988 | if (device == NULL) | |
1989 | return FALSE; | |
1990 | if (priv->transfer_size > 0) | |
1991 | dfu_device_set_transfer_size (device, priv->transfer_size); | |
1992 | ||
1993 | /* detatch */ | |
1994 | if (!dfu_device_open (device, DFU_DEVICE_OPEN_FLAG_NONE, | |
1995 | priv->cancellable, error)) | |
1996 | return FALSE; | |
1997 | if (!dfu_device_detach (device, priv->cancellable, error)) | |
1998 | return FALSE; | |
1999 | return TRUE; | |
2000 | } | |
2001 | ||
2002 | /** | |
2003 | * dfu_tool_sigint_cb: | |
2004 | **/ | |
2005 | static gboolean | |
2006 | dfu_tool_sigint_cb (gpointer user_data) | |
2007 | { | |
2008 | DfuToolPrivate *priv = (DfuToolPrivate *) user_data; | |
2009 | g_debug ("Handling SIGINT"); | |
2010 | g_cancellable_cancel (priv->cancellable); | |
2011 | return FALSE; | |
2012 | } | |
2013 | ||
2014 | /** | |
2015 | * main: | |
2016 | **/ | |
2017 | int | |
2018 | main (int argc, char *argv[]) | |
2019 | { | |
2020 | gboolean ret; | |
2021 | gboolean verbose = FALSE; | |
2022 | gboolean version = FALSE; | |
2023 | g_autofree gchar *cmd_descriptions = NULL; | |
2024 | g_autoptr(DfuToolPrivate) priv = g_new0 (DfuToolPrivate, 1); | |
2025 | g_autoptr(GError) error = NULL; | |
2026 | g_autoptr(GOptionContext) context = NULL; | |
2027 | const GOptionEntry options[] = { | |
2028 | { "version", '\0', 0, G_OPTION_ARG_NONE, &version, | |
2029 | "Print the version number", NULL }, | |
2030 | { "verbose", 'v', 0, G_OPTION_ARG_NONE, &verbose, | |
2031 | "Print verbose debug statements", NULL }, | |
2032 | { "device", 'd', 0, G_OPTION_ARG_STRING, &priv->device_vid_pid, | |
2033 | "Specify Vendor/Product ID(s) of DFU device", "VID:PID" }, | |
2034 | { "transfer-size", 't', 0, G_OPTION_ARG_STRING, &priv->transfer_size, | |
2035 | "Specify the number of bytes per USB transfer", "BYTES" }, | |
2036 | { "force", '\0', 0, G_OPTION_ARG_NONE, &priv->force, | |
2037 | "Force the action ignoring all warnings", NULL }, | |
2038 | { NULL} | |
2039 | }; | |
2040 | ||
2041 | setlocale (LC_ALL, ""); | |
2042 | ||
2043 | bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR); | |
2044 | bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8"); | |
2045 | textdomain (GETTEXT_PACKAGE); | |
2046 | ||
2047 | /* add commands */ | |
2048 | priv->cmd_array = g_ptr_array_new_with_free_func ((GDestroyNotify) dfu_tool_item_free); | |
2049 | dfu_tool_add (priv->cmd_array, | |
2050 | "convert", | |
2051 | NULL, | |
2052 | /* TRANSLATORS: command description */ | |
2053 | _("Convert firmware to DFU format"), | |
2054 | dfu_tool_convert); | |
2055 | dfu_tool_add (priv->cmd_array, | |
2056 | "merge", | |
2057 | NULL, | |
2058 | /* TRANSLATORS: command description */ | |
2059 | _("Merge multiple firmware files into one"), | |
2060 | dfu_tool_merge); | |
2061 | dfu_tool_add (priv->cmd_array, | |
2062 | "set-vendor", | |
2063 | NULL, | |
2064 | /* TRANSLATORS: command description */ | |
2065 | _("Set vendor ID on firmware file"), | |
2066 | dfu_tool_set_vendor); | |
2067 | dfu_tool_add (priv->cmd_array, | |
2068 | "set-product", | |
2069 | NULL, | |
2070 | /* TRANSLATORS: command description */ | |
2071 | _("Set product ID on firmware file"), | |
2072 | dfu_tool_set_product); | |
2073 | dfu_tool_add (priv->cmd_array, | |
2074 | "set-release", | |
2075 | NULL, | |
2076 | /* TRANSLATORS: command description */ | |
2077 | _("Set release version on firmware file"), | |
2078 | dfu_tool_set_release); | |
2079 | dfu_tool_add (priv->cmd_array, | |
2080 | "set-alt-setting", | |
2081 | NULL, | |
2082 | /* TRANSLATORS: command description */ | |
2083 | _("Set alternative number on firmware file"), | |
2084 | dfu_tool_set_alt_setting); | |
2085 | dfu_tool_add (priv->cmd_array, | |
2086 | "set-alt-setting-name", | |
2087 | NULL, | |
2088 | /* TRANSLATORS: command description */ | |
2089 | _("Set alternative name on firmware file"), | |
2090 | dfu_tool_set_alt_setting_name); | |
2091 | dfu_tool_add (priv->cmd_array, | |
2092 | "attach", | |
2093 | NULL, | |
2094 | /* TRANSLATORS: command description */ | |
2095 | _("Attach DFU capable device back to runtime"), | |
2096 | dfu_tool_attach); | |
2097 | dfu_tool_add (priv->cmd_array, | |
2098 | "read", | |
2099 | NULL, | |
2100 | /* TRANSLATORS: command description */ | |
2101 | _("Read firmware from device into a file"), | |
2102 | dfu_tool_read); | |
2103 | dfu_tool_add (priv->cmd_array, | |
2104 | "read-alt", | |
2105 | NULL, | |
2106 | /* TRANSLATORS: command description */ | |
2107 | _("Read firmware from one partition into a file"), | |
2108 | dfu_tool_read_alt); | |
2109 | dfu_tool_add (priv->cmd_array, | |
2110 | "write", | |
2111 | NULL, | |
2112 | /* TRANSLATORS: command description */ | |
2113 | _("Write firmware from file into device"), | |
2114 | dfu_tool_write); | |
2115 | dfu_tool_add (priv->cmd_array, | |
2116 | "write-alt", | |
2117 | NULL, | |
2118 | /* TRANSLATORS: command description */ | |
2119 | _("Write firmware from file into one partition"), | |
2120 | dfu_tool_write_alt); | |
2121 | dfu_tool_add (priv->cmd_array, | |
2122 | "list", | |
2123 | NULL, | |
2124 | /* TRANSLATORS: command description */ | |
2125 | _("List currently attached DFU capable devices"), | |
2126 | dfu_tool_list); | |
2127 | dfu_tool_add (priv->cmd_array, | |
2128 | "detach", | |
2129 | NULL, | |
2130 | /* TRANSLATORS: command description */ | |
2131 | _("Detach currently attached DFU capable device"), | |
2132 | dfu_tool_detach); | |
2133 | dfu_tool_add (priv->cmd_array, | |
2134 | "dump", | |
2135 | NULL, | |
2136 | /* TRANSLATORS: command description */ | |
2137 | _("Dump details about a firmware file"), | |
2138 | dfu_tool_dump); | |
2139 | dfu_tool_add (priv->cmd_array, | |
2140 | "watch", | |
2141 | NULL, | |
2142 | /* TRANSLATORS: command description */ | |
2143 | _("Watch DFU devices being hotplugged"), | |
2144 | dfu_tool_watch); | |
2145 | dfu_tool_add (priv->cmd_array, | |
2146 | "encrypt", | |
2147 | NULL, | |
2148 | /* TRANSLATORS: command description */ | |
2149 | _("Encrypt firmware data"), | |
2150 | dfu_tool_encrypt); | |
2151 | dfu_tool_add (priv->cmd_array, | |
2152 | "decrypt", | |
2153 | NULL, | |
2154 | /* TRANSLATORS: command description */ | |
2155 | _("Decrypt firmware data"), | |
2156 | dfu_tool_decrypt); | |
2157 | dfu_tool_add (priv->cmd_array, | |
2158 | "set-metadata", | |
2159 | NULL, | |
2160 | /* TRANSLATORS: command description */ | |
2161 | _("Sets metadata on a firmware file"), | |
2162 | dfu_tool_set_metadata); | |
2163 | ||
2164 | /* do stuff on ctrl+c */ | |
2165 | priv->cancellable = g_cancellable_new (); | |
2166 | g_unix_signal_add_full (G_PRIORITY_DEFAULT, | |
2167 | SIGINT, | |
2168 | dfu_tool_sigint_cb, | |
2169 | priv, | |
2170 | NULL); | |
2171 | ||
2172 | /* sort by command name */ | |
2173 | g_ptr_array_sort (priv->cmd_array, | |
2174 | (GCompareFunc) dfu_tool_sort_command_name_cb); | |
2175 | ||
2176 | /* get a list of the commands */ | |
2177 | context = g_option_context_new (NULL); | |
2178 | cmd_descriptions = dfu_tool_get_descriptions (priv->cmd_array); | |
2179 | g_option_context_set_summary (context, cmd_descriptions); | |
2180 | ||
2181 | /* TRANSLATORS: DFU stands for device firmware update */ | |
2182 | g_set_application_name (_("DFU Utility")); | |
2183 | g_option_context_add_main_entries (context, options, NULL); | |
2184 | ret = g_option_context_parse (context, &argc, &argv, &error); | |
2185 | if (!ret) { | |
2186 | /* TRANSLATORS: the user didn't read the man page */ | |
2187 | g_print ("%s: %s\n", _("Failed to parse arguments"), error->message); | |
2188 | return EXIT_FAILURE; | |
2189 | } | |
2190 | ||
2191 | /* set verbose? */ | |
2192 | if (verbose) | |
2193 | g_setenv ("G_MESSAGES_DEBUG", "all", FALSE); | |
2194 | ||
2195 | /* version */ | |
2196 | if (version) { | |
2197 | g_print ("%s %s\n", PACKAGE_NAME, PACKAGE_VERSION); | |
2198 | return EXIT_SUCCESS; | |
2199 | } | |
2200 | ||
2201 | /* run the specified command */ | |
2202 | ret = dfu_tool_run (priv, argv[1], (gchar**) &argv[2], &error); | |
2203 | if (!ret) { | |
2204 | if (g_error_matches (error, DFU_ERROR, DFU_ERROR_INTERNAL)) { | |
2205 | g_autofree gchar *tmp = NULL; | |
2206 | tmp = g_option_context_get_help (context, TRUE, NULL); | |
2207 | g_print ("%s\n\n%s", error->message, tmp); | |
2208 | } else { | |
2209 | g_print ("%s\n", error->message); | |
2210 | } | |
2211 | return EXIT_FAILURE; | |
2212 | } | |
2213 | ||
2214 | /* success/ */ | |
2215 | return EXIT_SUCCESS; | |
2216 | } |
0 | /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- | |
1 | * | |
2 | * Copyright (C) 2015 Richard Hughes <richard@hughsie.com> | |
3 | * | |
4 | * Licensed under the GNU Lesser General Public License Version 2.1 | |
5 | * | |
6 | * This library is free software; you can redistribute it and/or | |
7 | * modify it under the terms of the GNU Lesser General Public | |
8 | * License as published by the Free Software Foundation; either | |
9 | * version 2.1 of the License, or (at your option) any later version. | |
10 | * | |
11 | * This library is distributed in the hope that it will be useful, | |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
14 | * Lesser General Public License for more details. | |
15 | * | |
16 | * You should have received a copy of the GNU Lesser General Public | |
17 | * License along with this library; if not, write to the Free Software | |
18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | |
19 | */ | |
20 | ||
21 | /** | |
22 | * SECTION:dfu | |
23 | * @short_description: Helper objects for interacting with DFU devices. | |
24 | */ | |
25 | ||
26 | #ifndef __DFU_H__ | |
27 | #define __DFU_H__ | |
28 | ||
29 | #define __DFU_H_INSIDE__ | |
30 | ||
31 | #include <libdfu/dfu-common.h> | |
32 | #include <libdfu/dfu-context.h> | |
33 | #include <libdfu/dfu-device.h> | |
34 | #include <libdfu/dfu-element.h> | |
35 | #include <libdfu/dfu-error.h> | |
36 | #include <libdfu/dfu-firmware.h> | |
37 | #include <libdfu/dfu-image.h> | |
38 | #include <libdfu/dfu-sector.h> | |
39 | #include <libdfu/dfu-target.h> | |
40 | ||
41 | #undef __DFU_H_INSIDE__ | |
42 | ||
43 | #endif /* __DFU_H__ */ | |
44 |
0 | prefix=@prefix@ | |
1 | exec_prefix=@exec_prefix@ | |
2 | libdir=@libdir@ | |
3 | includedir=@includedir@ | |
4 | ||
5 | Name: dfu | |
6 | Description: libdfu is a library for reading and writing USB device firmware | |
7 | Version: @VERSION@ | |
8 | Requires: glib-2.0, gobject-2.0, gio-2.0 | |
9 | Libs: -L${libdir} -ldfu | |
10 | Cflags: -I${includedir}/fwupd-1 |
0 | #!/usr/bin/python | |
1 | ||
2 | # Copyright (C) 2015 Richard Hughes <richard@hughsie.com> | |
3 | # Licensed under the GNU General Public License Version 2 | |
4 | ||
5 | import gi | |
6 | gi.require_version('Dfu', '1.0') | |
7 | ||
8 | from gi.repository import Dfu | |
9 | from gi.repository import Gio | |
10 | ||
11 | cancellable = Gio.Cancellable.new() | |
12 | ctx = Dfu.Context.new() | |
13 | ctx.enumerate() | |
14 | ||
15 | for dev in ctx.get_devices(): | |
16 | ||
17 | # print details about the device | |
18 | dev.open(Dfu.DeviceOpenFlags.NONE, cancellable) | |
19 | print "getting firmware from %s:%s" % (dev.get_state(), dev.get_status()) | |
20 | ||
21 | # transfer firmware from device to host and show summary | |
22 | fw = dev.upload(Dfu.TargetTransferFlags.DETACH, cancellable) | |
23 | print fw.to_string() |
Binary diff not shown
Binary diff not shown
0 | :044000003DEF20F080 | |
1 | :10400800FACF01F0FBCF02F0E9CF03F0EACF04F0DA | |
2 | :10401800E1CF05F0E2CF06F0D9CF07F0DACF08F00C | |
3 | :10402800F3CF09F0F4CF0AF0F6CF0BF0F7CF0CF08E | |
4 | :10403800F8CF0DF0F5CF0EF00EC0F5FF0DC0F8FF6C | |
5 | :104048000CC0F7FF0BC0F6FF0AC0F4FF09C0F3FF6E | |
6 | :1040580008C0DAFF07C0D9FF06C0E2FF05C0E1FFCC | |
7 | :1040680004C0EAFF03C0E9FF02C0FBFF01C0FAFF7A | |
8 | :1040780011003FEF20F0000142EF20F03DEF20F06B | |
9 | :00000001FF |
Binary diff not shown
Binary diff not shown
0 | CC=afl-gcc ./configure --disable-shared | |
1 | AFL_HARDEN=1 make | |
2 | afl-fuzz -m 300 -i fuzzing -o findings ./dfu-tool dump @@ |
0 | 0 | policy/org.freedesktop.fwupd.policy.in |
1 | libdfu/dfu-tool.c | |
1 | 2 | src/fu-debug.c |
2 | 3 | src/fu-main.c |
3 | 4 | src/fu-util.c |
7 | 7 | msgstr "" |
8 | 8 | "Project-Id-Version: fwupd\n" |
9 | 9 | "Report-Msgid-Bugs-To: \n" |
10 | "POT-Creation-Date: 2015-09-10 09:28+0100\n" | |
11 | "PO-Revision-Date: 2015-09-10 08:28+0000\n" | |
10 | "POT-Creation-Date: 2015-12-07 12:22+0000\n" | |
11 | "PO-Revision-Date: 2015-12-07 12:22+0000\n" | |
12 | 12 | "Last-Translator: Richard Hughes <richard@hughsie.com>\n" |
13 | 13 | "Language-Team: German (http://www.transifex.com/hughsie/fwupd/language/de/)\n" |
14 | 14 | "MIME-Version: 1.0\n" |
50 | 50 | "Authentication is required to downgrade the firmware on a removable device" |
51 | 51 | msgstr "" |
52 | 52 | |
53 | #. TRANSLATORS: turn on all debugging | |
54 | msgid "Show debugging information for all files" | |
55 | msgstr "Debuginformationen für alle Dateien anzeigen" | |
56 | ||
57 | #. TRANSLATORS: for the --verbose arg | |
58 | msgid "Debugging Options" | |
59 | msgstr "Debug Optionen" | |
60 | ||
61 | #. TRANSLATORS: for the --verbose arg | |
62 | msgid "Show debugging options" | |
63 | msgstr "Debug Optionen anzeigen" | |
64 | ||
65 | #. TRANSLATORS: exit after we've started up, used for user profiling | |
66 | msgid "Exit after a small delay" | |
67 | msgstr "Verlassen nach einer kurzen Verzögerung" | |
68 | ||
69 | #. TRANSLATORS: exit straight away, used for automatic profiling | |
70 | msgid "Exit after the engine has loaded" | |
71 | msgstr "" | |
72 | ||
73 | #. TRANSLATORS: program name | |
74 | msgid "Firmware Update Daemon" | |
75 | msgstr "" | |
76 | ||
77 | #. TRANSLATORS: program summary | |
78 | msgid "Firmware Update D-Bus Service" | |
79 | msgstr "" | |
80 | ||
81 | 53 | #. TRANSLATORS: this is a command alias, e.g. 'get-devices' |
82 | 54 | #, c-format |
83 | 55 | msgid "Alias to %s" |
85 | 57 | |
86 | 58 | #. TRANSLATORS: error message |
87 | 59 | msgid "Command not found" |
60 | msgstr "" | |
61 | ||
62 | #. TRANSLATORS: when an action has completed | |
63 | msgid "OK" | |
64 | msgstr "" | |
65 | ||
66 | #. TRANSLATORS: when moving from runtime to DFU mode | |
67 | msgid "Detaching" | |
68 | msgstr "" | |
69 | ||
70 | #. TRANSLATORS: when moving from DFU to runtime mode | |
71 | msgid "Attaching" | |
72 | msgstr "" | |
73 | ||
74 | #. TRANSLATORS: when copying from host to device | |
75 | msgid "Downloading" | |
76 | msgstr "" | |
77 | ||
78 | #. TRANSLATORS: when copying from device to host | |
79 | msgid "Uploading" | |
80 | msgstr "" | |
81 | ||
82 | #. TRANSLATORS: this is when a device is hotplugged | |
83 | msgid "Added" | |
84 | msgstr "" | |
85 | ||
86 | #. TRANSLATORS: this is when a device is hotplugged | |
87 | msgid "Removed" | |
88 | msgstr "" | |
89 | ||
90 | #. TRANSLATORS: this is when a device is hotplugged | |
91 | msgid "Changed" | |
92 | msgstr "" | |
93 | ||
94 | #. TRANSLATORS: this is when a device ctrl+c's a watch | |
95 | msgid "Cancelled" | |
96 | msgstr "" | |
97 | ||
98 | #. TRANSLATORS: Appstream ID for the hardware type | |
99 | msgid "ID" | |
100 | msgstr "" | |
101 | ||
102 | #. TRANSLATORS: interface name, e.g. "Flash" | |
103 | msgid "Name" | |
104 | msgstr "" | |
105 | ||
106 | #. TRANSLATORS: this is the encryption method used when writing | |
107 | msgid "Cipher" | |
108 | msgstr "" | |
109 | ||
110 | #. TRANSLATORS: these are areas of memory on the chip | |
111 | msgid "Region" | |
112 | msgstr "" | |
113 | ||
114 | #. TRANSLATORS: detected a DFU device | |
115 | msgid "Found" | |
116 | msgstr "" | |
117 | ||
118 | #. TRANSLATORS: probably not run as root... | |
119 | #. TRANSLATORS: device has failed to report status | |
120 | #. TRANSLATORS: device status, e.g. "OK" | |
121 | msgid "Status" | |
122 | msgstr "" | |
123 | ||
124 | msgid "Unknown: permission denied" | |
125 | msgstr "" | |
126 | ||
127 | #. TRANSLATORS: device mode, e.g. runtime or DFU | |
128 | msgid "Mode" | |
129 | msgstr "" | |
130 | ||
131 | #. TRANSLATORS: device state, i.e. appIDLE | |
132 | msgid "State" | |
133 | msgstr "" | |
134 | ||
135 | #. TRANSLATORS: device quirks, i.e. things that | |
136 | #. * it does that we have to work around | |
137 | msgid "Quirks" | |
138 | msgstr "" | |
139 | ||
140 | #. TRANSLATORS: command description | |
141 | msgid "Convert firmware to DFU format" | |
142 | msgstr "" | |
143 | ||
144 | #. TRANSLATORS: command description | |
145 | msgid "Merge multiple firmware files into one" | |
146 | msgstr "" | |
147 | ||
148 | #. TRANSLATORS: command description | |
149 | msgid "Set vendor ID on firmware file" | |
150 | msgstr "" | |
151 | ||
152 | #. TRANSLATORS: command description | |
153 | msgid "Set product ID on firmware file" | |
154 | msgstr "" | |
155 | ||
156 | #. TRANSLATORS: command description | |
157 | msgid "Set release version on firmware file" | |
158 | msgstr "" | |
159 | ||
160 | #. TRANSLATORS: command description | |
161 | msgid "Set alternative number on firmware file" | |
162 | msgstr "" | |
163 | ||
164 | #. TRANSLATORS: command description | |
165 | msgid "Set alternative name on firmware file" | |
166 | msgstr "" | |
167 | ||
168 | #. TRANSLATORS: command description | |
169 | msgid "Attach DFU capable device back to runtime" | |
170 | msgstr "" | |
171 | ||
172 | #. TRANSLATORS: command description | |
173 | msgid "Read firmware from device into a file" | |
174 | msgstr "" | |
175 | ||
176 | #. TRANSLATORS: command description | |
177 | msgid "Read firmware from one partition into a file" | |
178 | msgstr "" | |
179 | ||
180 | #. TRANSLATORS: command description | |
181 | msgid "Write firmware from file into device" | |
182 | msgstr "" | |
183 | ||
184 | #. TRANSLATORS: command description | |
185 | msgid "Write firmware from file into one partition" | |
186 | msgstr "" | |
187 | ||
188 | #. TRANSLATORS: command description | |
189 | msgid "List currently attached DFU capable devices" | |
190 | msgstr "" | |
191 | ||
192 | #. TRANSLATORS: command description | |
193 | msgid "Detach currently attached DFU capable device" | |
194 | msgstr "" | |
195 | ||
196 | #. TRANSLATORS: command description | |
197 | msgid "Dump details about a firmware file" | |
198 | msgstr "" | |
199 | ||
200 | #. TRANSLATORS: command description | |
201 | msgid "Watch DFU devices being hotplugged" | |
202 | msgstr "" | |
203 | ||
204 | #. TRANSLATORS: command description | |
205 | msgid "Encrypt firmware data" | |
206 | msgstr "" | |
207 | ||
208 | #. TRANSLATORS: command description | |
209 | msgid "Decrypt firmware data" | |
210 | msgstr "" | |
211 | ||
212 | #. TRANSLATORS: command description | |
213 | msgid "Sets metadata on a firmware file" | |
214 | msgstr "" | |
215 | ||
216 | #. TRANSLATORS: DFU stands for device firmware update | |
217 | msgid "DFU Utility" | |
218 | msgstr "" | |
219 | ||
220 | #. TRANSLATORS: the user didn't read the man page | |
221 | msgid "Failed to parse arguments" | |
222 | msgstr "" | |
223 | ||
224 | #. TRANSLATORS: turn on all debugging | |
225 | msgid "Show debugging information for all files" | |
226 | msgstr "Debuginformationen für alle Dateien anzeigen" | |
227 | ||
228 | #. TRANSLATORS: for the --verbose arg | |
229 | msgid "Debugging Options" | |
230 | msgstr "Debug Optionen" | |
231 | ||
232 | #. TRANSLATORS: for the --verbose arg | |
233 | msgid "Show debugging options" | |
234 | msgstr "Debug Optionen anzeigen" | |
235 | ||
236 | #. TRANSLATORS: exit after we've started up, used for user profiling | |
237 | msgid "Exit after a small delay" | |
238 | msgstr "Verlassen nach einer kurzen Verzögerung" | |
239 | ||
240 | #. TRANSLATORS: exit straight away, used for automatic profiling | |
241 | msgid "Exit after the engine has loaded" | |
242 | msgstr "" | |
243 | ||
244 | #. TRANSLATORS: program name | |
245 | msgid "Firmware Update Daemon" | |
246 | msgstr "" | |
247 | ||
248 | #. TRANSLATORS: program summary | |
249 | msgid "Firmware Update D-Bus Service" | |
88 | 250 | msgstr "" |
89 | 251 | |
90 | 252 | #. TRANSLATORS: daemon is inactive |
148 | 310 | msgid "Updating %s from %s to %s... " |
149 | 311 | msgstr "" |
150 | 312 | |
151 | msgid "OK" | |
152 | msgstr "" | |
153 | ||
154 | 313 | #. TRANSLATORS: first replacement is device name |
155 | 314 | #, c-format |
156 | 315 | msgid "%s has firmware updates:" |
157 | 316 | msgstr "" |
158 | 317 | |
318 | #. TRANSLATORS: a GUID for the hardware | |
319 | msgid "GUID" | |
320 | msgstr "" | |
321 | ||
159 | 322 | #. TRANSLATORS: section header for firmware version |
160 | 323 | msgid "Version" |
161 | 324 | msgstr "" |
240 | 403 | msgid "Firmware Utility" |
241 | 404 | msgstr "" |
242 | 405 | |
243 | #. TRANSLATORS: the user didn't read the man page | |
244 | msgid "Failed to parse arguments" | |
245 | msgstr "" | |
246 | ||
247 | 406 | #. TRANSLATORS: the user is in a bad place |
248 | 407 | msgid "Failed to connect to D-Bus" |
249 | 408 | msgstr "" |
3 | 3 | # |
4 | 4 | # Translators: |
5 | 5 | # Richard Hughes <richard@hughsie.com>, 2015 |
6 | # Robert Readman <robert_readman@hotmail.com>, 2013 | |
7 | 6 | msgid "" |
8 | 7 | msgstr "" |
9 | 8 | "Project-Id-Version: fwupd\n" |
10 | 9 | "Report-Msgid-Bugs-To: \n" |
11 | "POT-Creation-Date: 2014-11-10 11:04+0000\n" | |
12 | "PO-Revision-Date: 2014-11-10 11:04+0000\n" | |
10 | "POT-Creation-Date: 2015-12-07 12:22+0000\n" | |
11 | "PO-Revision-Date: 2015-12-07 12:27+0000\n" | |
13 | 12 | "Last-Translator: Richard Hughes <richard@hughsie.com>\n" |
14 | "Language-Team: English (United Kingdom) (http://www.transifex.com/projects/p/colord/language/en_GB/)\n" | |
13 | "Language-Team: English (United Kingdom) (http://www.transifex.com/hughsie/fwupd/language/en_GB/)\n" | |
15 | 14 | "MIME-Version: 1.0\n" |
16 | 15 | "Content-Type: text/plain; charset=UTF-8\n" |
17 | 16 | "Content-Transfer-Encoding: 8bit\n" |
18 | 17 | "Language: en_GB\n" |
19 | 18 | "Plural-Forms: nplurals=2; plural=(n != 1);\n" |
20 | 19 | |
21 | #. TRANSLATORS: this is a command alias | |
20 | msgid "Install signed system firmware" | |
21 | msgstr "Install signed system firmware" | |
22 | ||
23 | #. TRANSLATORS: this is the PolicyKit modal dialog | |
24 | msgid "Authentication is required to update the firmware on this machine" | |
25 | msgstr "Authentication is required to update the firmware on this machine" | |
26 | ||
27 | msgid "Install unsigned system firmware" | |
28 | msgstr "Install unsigned system firmware" | |
29 | ||
30 | msgid "Install old version of system firmware" | |
31 | msgstr "Install old version of system firmware" | |
32 | ||
33 | #. TRANSLATORS: this is the PolicyKit modal dialog | |
34 | msgid "Authentication is required to downgrade the firmware on this machine" | |
35 | msgstr "Authentication is required to downgrade the firmware on this machine" | |
36 | ||
37 | msgid "Install signed device firmware" | |
38 | msgstr "Install signed device firmware" | |
39 | ||
40 | #. TRANSLATORS: this is the PolicyKit modal dialog | |
41 | msgid "" | |
42 | "Authentication is required to update the firmware on a removable device" | |
43 | msgstr "Authentication is required to update the firmware on a removable device" | |
44 | ||
45 | msgid "Install unsigned device firmware" | |
46 | msgstr "Install unsigned device firmware" | |
47 | ||
48 | #. TRANSLATORS: this is the PolicyKit modal dialog | |
49 | msgid "" | |
50 | "Authentication is required to downgrade the firmware on a removable device" | |
51 | msgstr "Authentication is required to downgrade the firmware on a removable device" | |
52 | ||
53 | #. TRANSLATORS: this is a command alias, e.g. 'get-devices' | |
22 | 54 | #, c-format |
23 | 55 | msgid "Alias to %s" |
24 | 56 | msgstr "Alias to %s" |
57 | ||
58 | #. TRANSLATORS: error message | |
59 | msgid "Command not found" | |
60 | msgstr "Command not found" | |
61 | ||
62 | #. TRANSLATORS: when an action has completed | |
63 | msgid "OK" | |
64 | msgstr "OK" | |
65 | ||
66 | #. TRANSLATORS: when moving from runtime to DFU mode | |
67 | msgid "Detaching" | |
68 | msgstr "Detaching" | |
69 | ||
70 | #. TRANSLATORS: when moving from DFU to runtime mode | |
71 | msgid "Attaching" | |
72 | msgstr "Attaching" | |
73 | ||
74 | #. TRANSLATORS: when copying from host to device | |
75 | msgid "Downloading" | |
76 | msgstr "Downloading" | |
77 | ||
78 | #. TRANSLATORS: when copying from device to host | |
79 | msgid "Uploading" | |
80 | msgstr "Uploading" | |
81 | ||
82 | #. TRANSLATORS: this is when a device is hotplugged | |
83 | msgid "Added" | |
84 | msgstr "Added" | |
85 | ||
86 | #. TRANSLATORS: this is when a device is hotplugged | |
87 | msgid "Removed" | |
88 | msgstr "Removed" | |
89 | ||
90 | #. TRANSLATORS: this is when a device is hotplugged | |
91 | msgid "Changed" | |
92 | msgstr "Changed" | |
93 | ||
94 | #. TRANSLATORS: this is when a device ctrl+c's a watch | |
95 | msgid "Cancelled" | |
96 | msgstr "Cancelled" | |
97 | ||
98 | #. TRANSLATORS: Appstream ID for the hardware type | |
99 | msgid "ID" | |
100 | msgstr "ID" | |
101 | ||
102 | #. TRANSLATORS: interface name, e.g. "Flash" | |
103 | msgid "Name" | |
104 | msgstr "Name" | |
105 | ||
106 | #. TRANSLATORS: this is the encryption method used when writing | |
107 | msgid "Cipher" | |
108 | msgstr "Cipher" | |
109 | ||
110 | #. TRANSLATORS: these are areas of memory on the chip | |
111 | msgid "Region" | |
112 | msgstr "Region" | |
113 | ||
114 | #. TRANSLATORS: detected a DFU device | |
115 | msgid "Found" | |
116 | msgstr "Found" | |
117 | ||
118 | #. TRANSLATORS: probably not run as root... | |
119 | #. TRANSLATORS: device has failed to report status | |
120 | #. TRANSLATORS: device status, e.g. "OK" | |
121 | msgid "Status" | |
122 | msgstr "Status" | |
123 | ||
124 | msgid "Unknown: permission denied" | |
125 | msgstr "Unknown: permission denied" | |
126 | ||
127 | #. TRANSLATORS: device mode, e.g. runtime or DFU | |
128 | msgid "Mode" | |
129 | msgstr "Mode" | |
130 | ||
131 | #. TRANSLATORS: device state, i.e. appIDLE | |
132 | msgid "State" | |
133 | msgstr "State" | |
134 | ||
135 | #. TRANSLATORS: device quirks, i.e. things that | |
136 | #. * it does that we have to work around | |
137 | msgid "Quirks" | |
138 | msgstr "Quirks" | |
139 | ||
140 | #. TRANSLATORS: command description | |
141 | msgid "Convert firmware to DFU format" | |
142 | msgstr "Convert firmware to DFU format" | |
143 | ||
144 | #. TRANSLATORS: command description | |
145 | msgid "Merge multiple firmware files into one" | |
146 | msgstr "Merge multiple firmware files into one" | |
147 | ||
148 | #. TRANSLATORS: command description | |
149 | msgid "Set vendor ID on firmware file" | |
150 | msgstr "Set vendor ID on firmware file" | |
151 | ||
152 | #. TRANSLATORS: command description | |
153 | msgid "Set product ID on firmware file" | |
154 | msgstr "Set product ID on firmware file" | |
155 | ||
156 | #. TRANSLATORS: command description | |
157 | msgid "Set release version on firmware file" | |
158 | msgstr "Set release version on firmware file" | |
159 | ||
160 | #. TRANSLATORS: command description | |
161 | msgid "Set alternative number on firmware file" | |
162 | msgstr "Set alternative number on firmware file" | |
163 | ||
164 | #. TRANSLATORS: command description | |
165 | msgid "Set alternative name on firmware file" | |
166 | msgstr "Set alternative name on firmware file" | |
167 | ||
168 | #. TRANSLATORS: command description | |
169 | msgid "Attach DFU capable device back to runtime" | |
170 | msgstr "Attach DFU capable device back to runtime" | |
171 | ||
172 | #. TRANSLATORS: command description | |
173 | msgid "Read firmware from device into a file" | |
174 | msgstr "Read firmware from device into a file" | |
175 | ||
176 | #. TRANSLATORS: command description | |
177 | msgid "Read firmware from one partition into a file" | |
178 | msgstr "Read firmware from one partition into a file" | |
179 | ||
180 | #. TRANSLATORS: command description | |
181 | msgid "Write firmware from file into device" | |
182 | msgstr "Write firmware from file into device" | |
183 | ||
184 | #. TRANSLATORS: command description | |
185 | msgid "Write firmware from file into one partition" | |
186 | msgstr "Write firmware from file into one partition" | |
187 | ||
188 | #. TRANSLATORS: command description | |
189 | msgid "List currently attached DFU capable devices" | |
190 | msgstr "List currently attached DFU capable devices" | |
191 | ||
192 | #. TRANSLATORS: command description | |
193 | msgid "Detach currently attached DFU capable device" | |
194 | msgstr "Detach currently attached DFU capable device" | |
195 | ||
196 | #. TRANSLATORS: command description | |
197 | msgid "Dump details about a firmware file" | |
198 | msgstr "Dump details about a firmware file" | |
199 | ||
200 | #. TRANSLATORS: command description | |
201 | msgid "Watch DFU devices being hotplugged" | |
202 | msgstr "Watch DFU devices being hotplugged" | |
203 | ||
204 | #. TRANSLATORS: command description | |
205 | msgid "Encrypt firmware data" | |
206 | msgstr "Encrypt firmware data" | |
207 | ||
208 | #. TRANSLATORS: command description | |
209 | msgid "Decrypt firmware data" | |
210 | msgstr "Decrypt firmware data" | |
211 | ||
212 | #. TRANSLATORS: command description | |
213 | msgid "Sets metadata on a firmware file" | |
214 | msgstr "Sets metadata on a firmware file" | |
215 | ||
216 | #. TRANSLATORS: DFU stands for device firmware update | |
217 | msgid "DFU Utility" | |
218 | msgstr "DFU Utility" | |
219 | ||
220 | #. TRANSLATORS: the user didn't read the man page | |
221 | msgid "Failed to parse arguments" | |
222 | msgstr "Failed to parse arguments" | |
223 | ||
224 | #. TRANSLATORS: turn on all debugging | |
225 | msgid "Show debugging information for all files" | |
226 | msgstr "Show debugging information for all files" | |
227 | ||
228 | #. TRANSLATORS: for the --verbose arg | |
229 | msgid "Debugging Options" | |
230 | msgstr "Debugging Options" | |
231 | ||
232 | #. TRANSLATORS: for the --verbose arg | |
233 | msgid "Show debugging options" | |
234 | msgstr "Show debugging options" | |
235 | ||
236 | #. TRANSLATORS: exit after we've started up, used for user profiling | |
237 | msgid "Exit after a small delay" | |
238 | msgstr "Exit after a small delay" | |
239 | ||
240 | #. TRANSLATORS: exit straight away, used for automatic profiling | |
241 | msgid "Exit after the engine has loaded" | |
242 | msgstr "Exit after the engine has loaded" | |
243 | ||
244 | #. TRANSLATORS: program name | |
245 | msgid "Firmware Update Daemon" | |
246 | msgstr "Firmware Update Daemon" | |
247 | ||
248 | #. TRANSLATORS: program summary | |
249 | msgid "Firmware Update D-Bus Service" | |
250 | msgstr "Firmware Update D-Bus Service" | |
251 | ||
252 | #. TRANSLATORS: daemon is inactive | |
253 | msgid "Idle" | |
254 | msgstr "Idle" | |
255 | ||
256 | #. TRANSLATORS: decompressing the firmware file | |
257 | msgid "Decompressing firmware" | |
258 | msgstr "Decompressing firmware" | |
259 | ||
260 | #. TRANSLATORS: parsing the firmware information | |
261 | msgid "Loading firmware" | |
262 | msgstr "Loading firmware" | |
263 | ||
264 | #. TRANSLATORS: restarting the device to pick up new F/W | |
265 | msgid "Restarting device" | |
266 | msgstr "Restarting device" | |
267 | ||
268 | #. TRANSLATORS: writing to the flash chips | |
269 | msgid "Writing firmware to device" | |
270 | msgstr "Writing firmware to device" | |
271 | ||
272 | #. TRANSLATORS: verifying we wrote the firmware correctly | |
273 | msgid "Verifying firmware from device" | |
274 | msgstr "Verifying firmware from device" | |
275 | ||
276 | #. TRANSLATORS: scheduing an update to be done on the next boot | |
277 | msgid "Scheduling upgrade" | |
278 | msgstr "Scheduling upgrade" | |
279 | ||
280 | #. TRANSLATORS: nothing attached that can be upgraded | |
281 | msgid "No hardware detected with firmware update capability" | |
282 | msgstr "No hardware detected with firmware update capability" | |
283 | ||
284 | #. TRANSLATORS: update completed, no errors | |
285 | msgid "Done!" | |
286 | msgstr "Done!" | |
287 | ||
288 | #. TRANSLATOR: the provider only supports offline | |
289 | msgid "Retrying as an offline update" | |
290 | msgstr "Retrying as an offline update" | |
291 | ||
292 | #. TRANSLATORS: the first replacement is a display name | |
293 | #. * e.g. "ColorHugALS" and the second is a version number | |
294 | #. * e.g. "1.2.3" | |
295 | #, c-format | |
296 | msgid "Reinstalling %s with %s... " | |
297 | msgstr "Reinstalling %s with %s... " | |
298 | ||
299 | #. TRANSLATORS: the first replacement is a display name | |
300 | #. * e.g. "ColorHugALS" and the second and third are | |
301 | #. * version numbers e.g. "1.2.3" | |
302 | #, c-format | |
303 | msgid "Downgrading %s from %s to %s... " | |
304 | msgstr "Downgrading %s from %s to %s... " | |
305 | ||
306 | #. TRANSLATORS: the first replacement is a display name | |
307 | #. * e.g. "ColorHugALS" and the second and third are | |
308 | #. * version numbers e.g. "1.2.3" | |
309 | #, c-format | |
310 | msgid "Updating %s from %s to %s... " | |
311 | msgstr "Updating %s from %s to %s... " | |
312 | ||
313 | #. TRANSLATORS: first replacement is device name | |
314 | #, c-format | |
315 | msgid "%s has firmware updates:" | |
316 | msgstr "%s has firmware updates:" | |
317 | ||
318 | #. TRANSLATORS: a GUID for the hardware | |
319 | msgid "GUID" | |
320 | msgstr "GUID" | |
321 | ||
322 | #. TRANSLATORS: section header for firmware version | |
323 | msgid "Version" | |
324 | msgstr "Version" | |
325 | ||
326 | #. TRANSLATORS: section header for firmware SHA1 | |
327 | msgid "Checksum" | |
328 | msgstr "Checksum" | |
329 | ||
330 | #. TRANSLATORS: section header for firmware remote http:// | |
331 | msgid "Location" | |
332 | msgstr "Location" | |
333 | ||
334 | #. TRANSLATORS: section header for long firmware desc | |
335 | msgid "Description" | |
336 | msgstr "Description" | |
337 | ||
338 | #. TRANSLATORS: command line option | |
339 | msgid "Show extra debugging information" | |
340 | msgstr "Show extra debugging information" | |
341 | ||
342 | #. TRANSLATORS: command line option | |
343 | msgid "Perform the installation offline where possible" | |
344 | msgstr "Perform the installation offline where possible" | |
345 | ||
346 | #. TRANSLATORS: command line option | |
347 | msgid "Allow re-installing existing firmware versions" | |
348 | msgstr "Allow re-installing existing firmware versions" | |
349 | ||
350 | #. TRANSLATORS: command line option | |
351 | msgid "Allow downgrading firmware versions" | |
352 | msgstr "Allow downgrading firmware versions" | |
353 | ||
354 | #. TRANSLATORS: command description | |
355 | msgid "Get all devices that support firmware updates" | |
356 | msgstr "Get all devices that support firmware updates" | |
357 | ||
358 | #. TRANSLATORS: command description | |
359 | msgid "Install prepared updates now" | |
360 | msgstr "Install prepared updates now" | |
361 | ||
362 | #. TRANSLATORS: command description | |
363 | msgid "Install a firmware file on this hardware" | |
364 | msgstr "Install a firmware file on this hardware" | |
365 | ||
366 | #. TRANSLATORS: command description | |
367 | msgid "Gets details about a firmware file" | |
368 | msgstr "Gets details about a firmware file" | |
369 | ||
370 | #. TRANSLATORS: command description | |
371 | msgid "Gets the list of updates for connected hardware" | |
372 | msgstr "Gets the list of updates for connected hardware" | |
373 | ||
374 | #. TRANSLATORS: command description | |
375 | msgid "Updates all firmware to latest versions available" | |
376 | msgstr "Updates all firmware to latest versions available" | |
377 | ||
378 | #. TRANSLATORS: command description | |
379 | msgid "Gets the cryptographic hash of the dumped firmware" | |
380 | msgstr "Gets the cryptographic hash of the dumped firmware" | |
381 | ||
382 | #. TRANSLATORS: command description | |
383 | msgid "Clears the results from the last update" | |
384 | msgstr "Clears the results from the last update" | |
385 | ||
386 | #. TRANSLATORS: command description | |
387 | msgid "Gets the results from the last update" | |
388 | msgstr "Gets the results from the last update" | |
389 | ||
390 | #. TRANSLATORS: command description | |
391 | msgid "Refresh metadata from remote server" | |
392 | msgstr "Refresh metadata from remote server" | |
393 | ||
394 | #. TRANSLATORS: command description | |
395 | msgid "Dump the ROM checksum" | |
396 | msgstr "Dump the ROM checksum" | |
397 | ||
398 | #. TRANSLATORS: command description | |
399 | msgid "Update the stored metadata with current ROM contents" | |
400 | msgstr "Update the stored metadata with current ROM contents" | |
401 | ||
402 | #. TRANSLATORS: program name | |
403 | msgid "Firmware Utility" | |
404 | msgstr "Firmware Utility" | |
405 | ||
406 | #. TRANSLATORS: the user is in a bad place | |
407 | msgid "Failed to connect to D-Bus" | |
408 | msgstr "Failed to connect to D-Bus" | |
409 | ||
410 | #. TRANSLATORS: we can't connect to the daemon | |
411 | msgid "Failed to connect to fwupd" | |
412 | msgstr "Failed to connect to fwupd" |
7 | 7 | msgstr "" |
8 | 8 | "Project-Id-Version: fwupd\n" |
9 | 9 | "Report-Msgid-Bugs-To: \n" |
10 | "POT-Creation-Date: 2015-09-10 09:28+0100\n" | |
11 | "PO-Revision-Date: 2015-09-10 08:28+0000\n" | |
10 | "POT-Creation-Date: 2015-12-07 12:22+0000\n" | |
11 | "PO-Revision-Date: 2015-12-07 12:22+0000\n" | |
12 | 12 | "Last-Translator: Richard Hughes <richard@hughsie.com>\n" |
13 | 13 | "Language-Team: French (http://www.transifex.com/hughsie/fwupd/language/fr/)\n" |
14 | 14 | "MIME-Version: 1.0\n" |
50 | 50 | "Authentication is required to downgrade the firmware on a removable device" |
51 | 51 | msgstr "" |
52 | 52 | |
53 | #. TRANSLATORS: turn on all debugging | |
54 | msgid "Show debugging information for all files" | |
55 | msgstr "Montrer les informations de débogage pour tous les fichiers" | |
56 | ||
57 | #. TRANSLATORS: for the --verbose arg | |
58 | msgid "Debugging Options" | |
59 | msgstr "Options de débogage" | |
60 | ||
61 | #. TRANSLATORS: for the --verbose arg | |
62 | msgid "Show debugging options" | |
63 | msgstr "Montrer les options de débogage" | |
64 | ||
65 | #. TRANSLATORS: exit after we've started up, used for user profiling | |
66 | msgid "Exit after a small delay" | |
67 | msgstr "Quitter après un bref délai" | |
68 | ||
69 | #. TRANSLATORS: exit straight away, used for automatic profiling | |
70 | msgid "Exit after the engine has loaded" | |
71 | msgstr "Quitter après le chargement du moteur" | |
72 | ||
73 | #. TRANSLATORS: program name | |
74 | msgid "Firmware Update Daemon" | |
75 | msgstr "" | |
76 | ||
77 | #. TRANSLATORS: program summary | |
78 | msgid "Firmware Update D-Bus Service" | |
79 | msgstr "Service D-Bus de mise à jour des micrologiciels" | |
80 | ||
81 | 53 | #. TRANSLATORS: this is a command alias, e.g. 'get-devices' |
82 | 54 | #, c-format |
83 | 55 | msgid "Alias to %s" |
86 | 58 | #. TRANSLATORS: error message |
87 | 59 | msgid "Command not found" |
88 | 60 | msgstr "" |
61 | ||
62 | #. TRANSLATORS: when an action has completed | |
63 | msgid "OK" | |
64 | msgstr "" | |
65 | ||
66 | #. TRANSLATORS: when moving from runtime to DFU mode | |
67 | msgid "Detaching" | |
68 | msgstr "" | |
69 | ||
70 | #. TRANSLATORS: when moving from DFU to runtime mode | |
71 | msgid "Attaching" | |
72 | msgstr "" | |
73 | ||
74 | #. TRANSLATORS: when copying from host to device | |
75 | msgid "Downloading" | |
76 | msgstr "" | |
77 | ||
78 | #. TRANSLATORS: when copying from device to host | |
79 | msgid "Uploading" | |
80 | msgstr "" | |
81 | ||
82 | #. TRANSLATORS: this is when a device is hotplugged | |
83 | msgid "Added" | |
84 | msgstr "" | |
85 | ||
86 | #. TRANSLATORS: this is when a device is hotplugged | |
87 | msgid "Removed" | |
88 | msgstr "" | |
89 | ||
90 | #. TRANSLATORS: this is when a device is hotplugged | |
91 | msgid "Changed" | |
92 | msgstr "" | |
93 | ||
94 | #. TRANSLATORS: this is when a device ctrl+c's a watch | |
95 | msgid "Cancelled" | |
96 | msgstr "" | |
97 | ||
98 | #. TRANSLATORS: Appstream ID for the hardware type | |
99 | msgid "ID" | |
100 | msgstr "" | |
101 | ||
102 | #. TRANSLATORS: interface name, e.g. "Flash" | |
103 | msgid "Name" | |
104 | msgstr "" | |
105 | ||
106 | #. TRANSLATORS: this is the encryption method used when writing | |
107 | msgid "Cipher" | |
108 | msgstr "" | |
109 | ||
110 | #. TRANSLATORS: these are areas of memory on the chip | |
111 | msgid "Region" | |
112 | msgstr "" | |
113 | ||
114 | #. TRANSLATORS: detected a DFU device | |
115 | msgid "Found" | |
116 | msgstr "" | |
117 | ||
118 | #. TRANSLATORS: probably not run as root... | |
119 | #. TRANSLATORS: device has failed to report status | |
120 | #. TRANSLATORS: device status, e.g. "OK" | |
121 | msgid "Status" | |
122 | msgstr "" | |
123 | ||
124 | msgid "Unknown: permission denied" | |
125 | msgstr "" | |
126 | ||
127 | #. TRANSLATORS: device mode, e.g. runtime or DFU | |
128 | msgid "Mode" | |
129 | msgstr "" | |
130 | ||
131 | #. TRANSLATORS: device state, i.e. appIDLE | |
132 | msgid "State" | |
133 | msgstr "" | |
134 | ||
135 | #. TRANSLATORS: device quirks, i.e. things that | |
136 | #. * it does that we have to work around | |
137 | msgid "Quirks" | |
138 | msgstr "" | |
139 | ||
140 | #. TRANSLATORS: command description | |
141 | msgid "Convert firmware to DFU format" | |
142 | msgstr "" | |
143 | ||
144 | #. TRANSLATORS: command description | |
145 | msgid "Merge multiple firmware files into one" | |
146 | msgstr "" | |
147 | ||
148 | #. TRANSLATORS: command description | |
149 | msgid "Set vendor ID on firmware file" | |
150 | msgstr "" | |
151 | ||
152 | #. TRANSLATORS: command description | |
153 | msgid "Set product ID on firmware file" | |
154 | msgstr "" | |
155 | ||
156 | #. TRANSLATORS: command description | |
157 | msgid "Set release version on firmware file" | |
158 | msgstr "" | |
159 | ||
160 | #. TRANSLATORS: command description | |
161 | msgid "Set alternative number on firmware file" | |
162 | msgstr "" | |
163 | ||
164 | #. TRANSLATORS: command description | |
165 | msgid "Set alternative name on firmware file" | |
166 | msgstr "" | |
167 | ||
168 | #. TRANSLATORS: command description | |
169 | msgid "Attach DFU capable device back to runtime" | |
170 | msgstr "" | |
171 | ||
172 | #. TRANSLATORS: command description | |
173 | msgid "Read firmware from device into a file" | |
174 | msgstr "" | |
175 | ||
176 | #. TRANSLATORS: command description | |
177 | msgid "Read firmware from one partition into a file" | |
178 | msgstr "" | |
179 | ||
180 | #. TRANSLATORS: command description | |
181 | msgid "Write firmware from file into device" | |
182 | msgstr "" | |
183 | ||
184 | #. TRANSLATORS: command description | |
185 | msgid "Write firmware from file into one partition" | |
186 | msgstr "" | |
187 | ||
188 | #. TRANSLATORS: command description | |
189 | msgid "List currently attached DFU capable devices" | |
190 | msgstr "" | |
191 | ||
192 | #. TRANSLATORS: command description | |
193 | msgid "Detach currently attached DFU capable device" | |
194 | msgstr "" | |
195 | ||
196 | #. TRANSLATORS: command description | |
197 | msgid "Dump details about a firmware file" | |
198 | msgstr "" | |
199 | ||
200 | #. TRANSLATORS: command description | |
201 | msgid "Watch DFU devices being hotplugged" | |
202 | msgstr "" | |
203 | ||
204 | #. TRANSLATORS: command description | |
205 | msgid "Encrypt firmware data" | |
206 | msgstr "" | |
207 | ||
208 | #. TRANSLATORS: command description | |
209 | msgid "Decrypt firmware data" | |
210 | msgstr "" | |
211 | ||
212 | #. TRANSLATORS: command description | |
213 | msgid "Sets metadata on a firmware file" | |
214 | msgstr "" | |
215 | ||
216 | #. TRANSLATORS: DFU stands for device firmware update | |
217 | msgid "DFU Utility" | |
218 | msgstr "" | |
219 | ||
220 | #. TRANSLATORS: the user didn't read the man page | |
221 | msgid "Failed to parse arguments" | |
222 | msgstr "Echec de l'analyse des paramètres" | |
223 | ||
224 | #. TRANSLATORS: turn on all debugging | |
225 | msgid "Show debugging information for all files" | |
226 | msgstr "Montrer les informations de débogage pour tous les fichiers" | |
227 | ||
228 | #. TRANSLATORS: for the --verbose arg | |
229 | msgid "Debugging Options" | |
230 | msgstr "Options de débogage" | |
231 | ||
232 | #. TRANSLATORS: for the --verbose arg | |
233 | msgid "Show debugging options" | |
234 | msgstr "Montrer les options de débogage" | |
235 | ||
236 | #. TRANSLATORS: exit after we've started up, used for user profiling | |
237 | msgid "Exit after a small delay" | |
238 | msgstr "Quitter après un bref délai" | |
239 | ||
240 | #. TRANSLATORS: exit straight away, used for automatic profiling | |
241 | msgid "Exit after the engine has loaded" | |
242 | msgstr "Quitter après le chargement du moteur" | |
243 | ||
244 | #. TRANSLATORS: program name | |
245 | msgid "Firmware Update Daemon" | |
246 | msgstr "" | |
247 | ||
248 | #. TRANSLATORS: program summary | |
249 | msgid "Firmware Update D-Bus Service" | |
250 | msgstr "Service D-Bus de mise à jour des micrologiciels" | |
89 | 251 | |
90 | 252 | #. TRANSLATORS: daemon is inactive |
91 | 253 | msgid "Idle" |
148 | 310 | msgid "Updating %s from %s to %s... " |
149 | 311 | msgstr "Mise à jour de %s de %s en %s" |
150 | 312 | |
151 | msgid "OK" | |
152 | msgstr "" | |
153 | ||
154 | 313 | #. TRANSLATORS: first replacement is device name |
155 | 314 | #, c-format |
156 | 315 | msgid "%s has firmware updates:" |
157 | 316 | msgstr "" |
158 | 317 | |
318 | #. TRANSLATORS: a GUID for the hardware | |
319 | msgid "GUID" | |
320 | msgstr "" | |
321 | ||
159 | 322 | #. TRANSLATORS: section header for firmware version |
160 | 323 | msgid "Version" |
161 | 324 | msgstr "" |
240 | 403 | msgid "Firmware Utility" |
241 | 404 | msgstr "" |
242 | 405 | |
243 | #. TRANSLATORS: the user didn't read the man page | |
244 | msgid "Failed to parse arguments" | |
245 | msgstr "Echec de l'analyse des paramètres" | |
246 | ||
247 | 406 | #. TRANSLATORS: the user is in a bad place |
248 | 407 | msgid "Failed to connect to D-Bus" |
249 | 408 | msgstr "" |
8 | 8 | msgstr "" |
9 | 9 | "Project-Id-Version: fwupd\n" |
10 | 10 | "Report-Msgid-Bugs-To: \n" |
11 | "POT-Creation-Date: 2015-10-28 09:58+0000\n" | |
12 | "PO-Revision-Date: 2015-10-22 20:05+0000\n" | |
13 | "Last-Translator: GenghisKhan <genghiskhan@gmx.ca>\n" | |
11 | "POT-Creation-Date: 2015-12-07 12:22+0000\n" | |
12 | "PO-Revision-Date: 2015-12-07 12:22+0000\n" | |
13 | "Last-Translator: Richard Hughes <richard@hughsie.com>\n" | |
14 | 14 | "Language-Team: Hebrew (http://www.transifex.com/hughsie/fwupd/language/he/)\n" |
15 | 15 | "MIME-Version: 1.0\n" |
16 | 16 | "Content-Type: text/plain; charset=UTF-8\n" |
51 | 51 | "Authentication is required to downgrade the firmware on a removable device" |
52 | 52 | msgstr "" |
53 | 53 | |
54 | #. TRANSLATORS: turn on all debugging | |
55 | msgid "Show debugging information for all files" | |
56 | msgstr "הצג מידע ניפוי שגיאות לכל הקבצים" | |
57 | ||
58 | #. TRANSLATORS: for the --verbose arg | |
59 | msgid "Debugging Options" | |
60 | msgstr "אפשרויות ניפוי שגיאות" | |
61 | ||
62 | #. TRANSLATORS: for the --verbose arg | |
63 | msgid "Show debugging options" | |
64 | msgstr "הצג אפשרויות ניפוי שגיאות" | |
65 | ||
66 | #. TRANSLATORS: exit after we've started up, used for user profiling | |
67 | msgid "Exit after a small delay" | |
68 | msgstr "יציאה לאחר השהייה קצרה" | |
69 | ||
70 | #. TRANSLATORS: exit straight away, used for automatic profiling | |
71 | msgid "Exit after the engine has loaded" | |
72 | msgstr "יציאה לאחר טעינת מנוע התכנה" | |
73 | ||
74 | #. TRANSLATORS: program name | |
75 | msgid "Firmware Update Daemon" | |
76 | msgstr "שדון עדכון קושחה" | |
77 | ||
78 | #. TRANSLATORS: program summary | |
79 | msgid "Firmware Update D-Bus Service" | |
80 | msgstr "שירות D-Bus עדכון קושחה" | |
81 | ||
82 | 54 | #. TRANSLATORS: this is a command alias, e.g. 'get-devices' |
83 | 55 | #, c-format |
84 | 56 | msgid "Alias to %s" |
87 | 59 | #. TRANSLATORS: error message |
88 | 60 | msgid "Command not found" |
89 | 61 | msgstr "פקודה לא נמצאה" |
62 | ||
63 | #. TRANSLATORS: when an action has completed | |
64 | msgid "OK" | |
65 | msgstr "אישור" | |
66 | ||
67 | #. TRANSLATORS: when moving from runtime to DFU mode | |
68 | msgid "Detaching" | |
69 | msgstr "" | |
70 | ||
71 | #. TRANSLATORS: when moving from DFU to runtime mode | |
72 | msgid "Attaching" | |
73 | msgstr "" | |
74 | ||
75 | #. TRANSLATORS: when copying from host to device | |
76 | msgid "Downloading" | |
77 | msgstr "" | |
78 | ||
79 | #. TRANSLATORS: when copying from device to host | |
80 | msgid "Uploading" | |
81 | msgstr "" | |
82 | ||
83 | #. TRANSLATORS: this is when a device is hotplugged | |
84 | msgid "Added" | |
85 | msgstr "" | |
86 | ||
87 | #. TRANSLATORS: this is when a device is hotplugged | |
88 | msgid "Removed" | |
89 | msgstr "" | |
90 | ||
91 | #. TRANSLATORS: this is when a device is hotplugged | |
92 | msgid "Changed" | |
93 | msgstr "" | |
94 | ||
95 | #. TRANSLATORS: this is when a device ctrl+c's a watch | |
96 | msgid "Cancelled" | |
97 | msgstr "" | |
98 | ||
99 | #. TRANSLATORS: Appstream ID for the hardware type | |
100 | msgid "ID" | |
101 | msgstr "" | |
102 | ||
103 | #. TRANSLATORS: interface name, e.g. "Flash" | |
104 | msgid "Name" | |
105 | msgstr "" | |
106 | ||
107 | #. TRANSLATORS: this is the encryption method used when writing | |
108 | msgid "Cipher" | |
109 | msgstr "" | |
110 | ||
111 | #. TRANSLATORS: these are areas of memory on the chip | |
112 | msgid "Region" | |
113 | msgstr "" | |
114 | ||
115 | #. TRANSLATORS: detected a DFU device | |
116 | msgid "Found" | |
117 | msgstr "" | |
118 | ||
119 | #. TRANSLATORS: probably not run as root... | |
120 | #. TRANSLATORS: device has failed to report status | |
121 | #. TRANSLATORS: device status, e.g. "OK" | |
122 | msgid "Status" | |
123 | msgstr "" | |
124 | ||
125 | msgid "Unknown: permission denied" | |
126 | msgstr "" | |
127 | ||
128 | #. TRANSLATORS: device mode, e.g. runtime or DFU | |
129 | msgid "Mode" | |
130 | msgstr "" | |
131 | ||
132 | #. TRANSLATORS: device state, i.e. appIDLE | |
133 | msgid "State" | |
134 | msgstr "" | |
135 | ||
136 | #. TRANSLATORS: device quirks, i.e. things that | |
137 | #. * it does that we have to work around | |
138 | msgid "Quirks" | |
139 | msgstr "" | |
140 | ||
141 | #. TRANSLATORS: command description | |
142 | msgid "Convert firmware to DFU format" | |
143 | msgstr "" | |
144 | ||
145 | #. TRANSLATORS: command description | |
146 | msgid "Merge multiple firmware files into one" | |
147 | msgstr "" | |
148 | ||
149 | #. TRANSLATORS: command description | |
150 | msgid "Set vendor ID on firmware file" | |
151 | msgstr "" | |
152 | ||
153 | #. TRANSLATORS: command description | |
154 | msgid "Set product ID on firmware file" | |
155 | msgstr "" | |
156 | ||
157 | #. TRANSLATORS: command description | |
158 | msgid "Set release version on firmware file" | |
159 | msgstr "" | |
160 | ||
161 | #. TRANSLATORS: command description | |
162 | msgid "Set alternative number on firmware file" | |
163 | msgstr "" | |
164 | ||
165 | #. TRANSLATORS: command description | |
166 | msgid "Set alternative name on firmware file" | |
167 | msgstr "" | |
168 | ||
169 | #. TRANSLATORS: command description | |
170 | msgid "Attach DFU capable device back to runtime" | |
171 | msgstr "" | |
172 | ||
173 | #. TRANSLATORS: command description | |
174 | msgid "Read firmware from device into a file" | |
175 | msgstr "" | |
176 | ||
177 | #. TRANSLATORS: command description | |
178 | msgid "Read firmware from one partition into a file" | |
179 | msgstr "" | |
180 | ||
181 | #. TRANSLATORS: command description | |
182 | msgid "Write firmware from file into device" | |
183 | msgstr "" | |
184 | ||
185 | #. TRANSLATORS: command description | |
186 | msgid "Write firmware from file into one partition" | |
187 | msgstr "" | |
188 | ||
189 | #. TRANSLATORS: command description | |
190 | msgid "List currently attached DFU capable devices" | |
191 | msgstr "" | |
192 | ||
193 | #. TRANSLATORS: command description | |
194 | msgid "Detach currently attached DFU capable device" | |
195 | msgstr "" | |
196 | ||
197 | #. TRANSLATORS: command description | |
198 | msgid "Dump details about a firmware file" | |
199 | msgstr "" | |
200 | ||
201 | #. TRANSLATORS: command description | |
202 | msgid "Watch DFU devices being hotplugged" | |
203 | msgstr "" | |
204 | ||
205 | #. TRANSLATORS: command description | |
206 | msgid "Encrypt firmware data" | |
207 | msgstr "" | |
208 | ||
209 | #. TRANSLATORS: command description | |
210 | msgid "Decrypt firmware data" | |
211 | msgstr "" | |
212 | ||
213 | #. TRANSLATORS: command description | |
214 | msgid "Sets metadata on a firmware file" | |
215 | msgstr "" | |
216 | ||
217 | #. TRANSLATORS: DFU stands for device firmware update | |
218 | msgid "DFU Utility" | |
219 | msgstr "" | |
220 | ||
221 | #. TRANSLATORS: the user didn't read the man page | |
222 | msgid "Failed to parse arguments" | |
223 | msgstr "נכשל בפענוח הארגומנטים" | |
224 | ||
225 | #. TRANSLATORS: turn on all debugging | |
226 | msgid "Show debugging information for all files" | |
227 | msgstr "הצג מידע ניפוי שגיאות לכל הקבצים" | |
228 | ||
229 | #. TRANSLATORS: for the --verbose arg | |
230 | msgid "Debugging Options" | |
231 | msgstr "אפשרויות ניפוי שגיאות" | |
232 | ||
233 | #. TRANSLATORS: for the --verbose arg | |
234 | msgid "Show debugging options" | |
235 | msgstr "הצג אפשרויות ניפוי שגיאות" | |
236 | ||
237 | #. TRANSLATORS: exit after we've started up, used for user profiling | |
238 | msgid "Exit after a small delay" | |
239 | msgstr "יציאה לאחר השהייה קצרה" | |
240 | ||
241 | #. TRANSLATORS: exit straight away, used for automatic profiling | |
242 | msgid "Exit after the engine has loaded" | |
243 | msgstr "יציאה לאחר טעינת מנוע התכנה" | |
244 | ||
245 | #. TRANSLATORS: program name | |
246 | msgid "Firmware Update Daemon" | |
247 | msgstr "שדון עדכון קושחה" | |
248 | ||
249 | #. TRANSLATORS: program summary | |
250 | msgid "Firmware Update D-Bus Service" | |
251 | msgstr "שירות D-Bus עדכון קושחה" | |
90 | 252 | |
91 | 253 | #. TRANSLATORS: daemon is inactive |
92 | 254 | msgid "Idle" |
149 | 311 | msgid "Updating %s from %s to %s... " |
150 | 312 | msgstr "מעדכן %s מ־%s ל־%s..." |
151 | 313 | |
152 | msgid "OK" | |
153 | msgstr "אישור" | |
154 | ||
155 | 314 | #. TRANSLATORS: first replacement is device name |
156 | 315 | #, c-format |
157 | 316 | msgid "%s has firmware updates:" |
158 | 317 | msgstr "ישנם עדכוני קושחה עבור %s:" |
159 | 318 | |
319 | #. TRANSLATORS: a GUID for the hardware | |
320 | msgid "GUID" | |
321 | msgstr "" | |
322 | ||
160 | 323 | #. TRANSLATORS: section header for firmware version |
161 | 324 | msgid "Version" |
162 | 325 | msgstr "גרסא" |
241 | 404 | msgid "Firmware Utility" |
242 | 405 | msgstr "" |
243 | 406 | |
244 | #. TRANSLATORS: the user didn't read the man page | |
245 | msgid "Failed to parse arguments" | |
246 | msgstr "נכשל בפענוח הארגומנטים" | |
247 | ||
248 | 407 | #. TRANSLATORS: the user is in a bad place |
249 | 408 | msgid "Failed to connect to D-Bus" |
250 | 409 | msgstr "" |
7 | 7 | msgstr "" |
8 | 8 | "Project-Id-Version: fwupd\n" |
9 | 9 | "Report-Msgid-Bugs-To: \n" |
10 | "POT-Creation-Date: 2015-09-10 09:28+0100\n" | |
11 | "PO-Revision-Date: 2015-09-10 08:28+0000\n" | |
10 | "POT-Creation-Date: 2015-12-07 12:22+0000\n" | |
11 | "PO-Revision-Date: 2015-12-07 12:22+0000\n" | |
12 | 12 | "Last-Translator: Richard Hughes <richard@hughsie.com>\n" |
13 | 13 | "Language-Team: Hindi (India) (http://www.transifex.com/hughsie/fwupd/language/hi_IN/)\n" |
14 | 14 | "MIME-Version: 1.0\n" |
50 | 50 | "Authentication is required to downgrade the firmware on a removable device" |
51 | 51 | msgstr "" |
52 | 52 | |
53 | #. TRANSLATORS: turn on all debugging | |
54 | msgid "Show debugging information for all files" | |
55 | msgstr "फाइल्स की डिबगिंग की जानकारी दिखाए " | |
56 | ||
57 | #. TRANSLATORS: for the --verbose arg | |
58 | msgid "Debugging Options" | |
59 | msgstr "डिबगिंग के विकल्प " | |
60 | ||
61 | #. TRANSLATORS: for the --verbose arg | |
62 | msgid "Show debugging options" | |
63 | msgstr "डिबगिंग के विकल्प दिखाए " | |
64 | ||
65 | #. TRANSLATORS: exit after we've started up, used for user profiling | |
66 | msgid "Exit after a small delay" | |
67 | msgstr "थोड़ी देरी के बाद बहार जाएँ " | |
68 | ||
69 | #. TRANSLATORS: exit straight away, used for automatic profiling | |
70 | msgid "Exit after the engine has loaded" | |
71 | msgstr "इंजन के लोड हो जाने पर बहार जाएँ " | |
72 | ||
73 | #. TRANSLATORS: program name | |
74 | msgid "Firmware Update Daemon" | |
75 | msgstr "" | |
76 | ||
77 | #. TRANSLATORS: program summary | |
78 | msgid "Firmware Update D-Bus Service" | |
79 | msgstr "फर्मवेयर अपडेट डी-बस सेवा " | |
80 | ||
81 | 53 | #. TRANSLATORS: this is a command alias, e.g. 'get-devices' |
82 | 54 | #, c-format |
83 | 55 | msgid "Alias to %s" |
86 | 58 | #. TRANSLATORS: error message |
87 | 59 | msgid "Command not found" |
88 | 60 | msgstr "" |
61 | ||
62 | #. TRANSLATORS: when an action has completed | |
63 | msgid "OK" | |
64 | msgstr "" | |
65 | ||
66 | #. TRANSLATORS: when moving from runtime to DFU mode | |
67 | msgid "Detaching" | |
68 | msgstr "" | |
69 | ||
70 | #. TRANSLATORS: when moving from DFU to runtime mode | |
71 | msgid "Attaching" | |
72 | msgstr "" | |
73 | ||
74 | #. TRANSLATORS: when copying from host to device | |
75 | msgid "Downloading" | |
76 | msgstr "" | |
77 | ||
78 | #. TRANSLATORS: when copying from device to host | |
79 | msgid "Uploading" | |
80 | msgstr "" | |
81 | ||
82 | #. TRANSLATORS: this is when a device is hotplugged | |
83 | msgid "Added" | |
84 | msgstr "" | |
85 | ||
86 | #. TRANSLATORS: this is when a device is hotplugged | |
87 | msgid "Removed" | |
88 | msgstr "" | |
89 | ||
90 | #. TRANSLATORS: this is when a device is hotplugged | |
91 | msgid "Changed" | |
92 | msgstr "" | |
93 | ||
94 | #. TRANSLATORS: this is when a device ctrl+c's a watch | |
95 | msgid "Cancelled" | |
96 | msgstr "" | |
97 | ||
98 | #. TRANSLATORS: Appstream ID for the hardware type | |
99 | msgid "ID" | |
100 | msgstr "" | |
101 | ||
102 | #. TRANSLATORS: interface name, e.g. "Flash" | |
103 | msgid "Name" | |
104 | msgstr "" | |
105 | ||
106 | #. TRANSLATORS: this is the encryption method used when writing | |
107 | msgid "Cipher" | |
108 | msgstr "" | |
109 | ||
110 | #. TRANSLATORS: these are areas of memory on the chip | |
111 | msgid "Region" | |
112 | msgstr "" | |
113 | ||
114 | #. TRANSLATORS: detected a DFU device | |
115 | msgid "Found" | |
116 | msgstr "" | |
117 | ||
118 | #. TRANSLATORS: probably not run as root... | |
119 | #. TRANSLATORS: device has failed to report status | |
120 | #. TRANSLATORS: device status, e.g. "OK" | |
121 | msgid "Status" | |
122 | msgstr "" | |
123 | ||
124 | msgid "Unknown: permission denied" | |
125 | msgstr "" | |
126 | ||
127 | #. TRANSLATORS: device mode, e.g. runtime or DFU | |
128 | msgid "Mode" | |
129 | msgstr "" | |
130 | ||
131 | #. TRANSLATORS: device state, i.e. appIDLE | |
132 | msgid "State" | |
133 | msgstr "" | |
134 | ||
135 | #. TRANSLATORS: device quirks, i.e. things that | |
136 | #. * it does that we have to work around | |
137 | msgid "Quirks" | |
138 | msgstr "" | |
139 | ||
140 | #. TRANSLATORS: command description | |
141 | msgid "Convert firmware to DFU format" | |
142 | msgstr "" | |
143 | ||
144 | #. TRANSLATORS: command description | |
145 | msgid "Merge multiple firmware files into one" | |
146 | msgstr "" | |
147 | ||
148 | #. TRANSLATORS: command description | |
149 | msgid "Set vendor ID on firmware file" | |
150 | msgstr "" | |
151 | ||
152 | #. TRANSLATORS: command description | |
153 | msgid "Set product ID on firmware file" | |
154 | msgstr "" | |
155 | ||
156 | #. TRANSLATORS: command description | |
157 | msgid "Set release version on firmware file" | |
158 | msgstr "" | |
159 | ||
160 | #. TRANSLATORS: command description | |
161 | msgid "Set alternative number on firmware file" | |
162 | msgstr "" | |
163 | ||
164 | #. TRANSLATORS: command description | |
165 | msgid "Set alternative name on firmware file" | |
166 | msgstr "" | |
167 | ||
168 | #. TRANSLATORS: command description | |
169 | msgid "Attach DFU capable device back to runtime" | |
170 | msgstr "" | |
171 | ||
172 | #. TRANSLATORS: command description | |
173 | msgid "Read firmware from device into a file" | |
174 | msgstr "" | |
175 | ||
176 | #. TRANSLATORS: command description | |
177 | msgid "Read firmware from one partition into a file" | |
178 | msgstr "" | |
179 | ||
180 | #. TRANSLATORS: command description | |
181 | msgid "Write firmware from file into device" | |
182 | msgstr "" | |
183 | ||
184 | #. TRANSLATORS: command description | |
185 | msgid "Write firmware from file into one partition" | |
186 | msgstr "" | |
187 | ||
188 | #. TRANSLATORS: command description | |
189 | msgid "List currently attached DFU capable devices" | |
190 | msgstr "" | |
191 | ||
192 | #. TRANSLATORS: command description | |
193 | msgid "Detach currently attached DFU capable device" | |
194 | msgstr "" | |
195 | ||
196 | #. TRANSLATORS: command description | |
197 | msgid "Dump details about a firmware file" | |
198 | msgstr "" | |
199 | ||
200 | #. TRANSLATORS: command description | |
201 | msgid "Watch DFU devices being hotplugged" | |
202 | msgstr "" | |
203 | ||
204 | #. TRANSLATORS: command description | |
205 | msgid "Encrypt firmware data" | |
206 | msgstr "" | |
207 | ||
208 | #. TRANSLATORS: command description | |
209 | msgid "Decrypt firmware data" | |
210 | msgstr "" | |
211 | ||
212 | #. TRANSLATORS: command description | |
213 | msgid "Sets metadata on a firmware file" | |
214 | msgstr "" | |
215 | ||
216 | #. TRANSLATORS: DFU stands for device firmware update | |
217 | msgid "DFU Utility" | |
218 | msgstr "" | |
219 | ||
220 | #. TRANSLATORS: the user didn't read the man page | |
221 | msgid "Failed to parse arguments" | |
222 | msgstr "आर्गुमेंट पार्स करने में असफल " | |
223 | ||
224 | #. TRANSLATORS: turn on all debugging | |
225 | msgid "Show debugging information for all files" | |
226 | msgstr "फाइल्स की डिबगिंग की जानकारी दिखाए " | |
227 | ||
228 | #. TRANSLATORS: for the --verbose arg | |
229 | msgid "Debugging Options" | |
230 | msgstr "डिबगिंग के विकल्प " | |
231 | ||
232 | #. TRANSLATORS: for the --verbose arg | |
233 | msgid "Show debugging options" | |
234 | msgstr "डिबगिंग के विकल्प दिखाए " | |
235 | ||
236 | #. TRANSLATORS: exit after we've started up, used for user profiling | |
237 | msgid "Exit after a small delay" | |
238 | msgstr "थोड़ी देरी के बाद बहार जाएँ " | |
239 | ||
240 | #. TRANSLATORS: exit straight away, used for automatic profiling | |
241 | msgid "Exit after the engine has loaded" | |
242 | msgstr "इंजन के लोड हो जाने पर बहार जाएँ " | |
243 | ||
244 | #. TRANSLATORS: program name | |
245 | msgid "Firmware Update Daemon" | |
246 | msgstr "" | |
247 | ||
248 | #. TRANSLATORS: program summary | |
249 | msgid "Firmware Update D-Bus Service" | |
250 | msgstr "फर्मवेयर अपडेट डी-बस सेवा " | |
89 | 251 | |
90 | 252 | #. TRANSLATORS: daemon is inactive |
91 | 253 | msgid "Idle" |
148 | 310 | msgid "Updating %s from %s to %s... " |
149 | 311 | msgstr "%s को %s से %s तक अपडेट करा जा रहा है " |
150 | 312 | |
151 | msgid "OK" | |
152 | msgstr "" | |
153 | ||
154 | 313 | #. TRANSLATORS: first replacement is device name |
155 | 314 | #, c-format |
156 | 315 | msgid "%s has firmware updates:" |
157 | 316 | msgstr "" |
158 | 317 | |
318 | #. TRANSLATORS: a GUID for the hardware | |
319 | msgid "GUID" | |
320 | msgstr "" | |
321 | ||
159 | 322 | #. TRANSLATORS: section header for firmware version |
160 | 323 | msgid "Version" |
161 | 324 | msgstr "" |
240 | 403 | msgid "Firmware Utility" |
241 | 404 | msgstr "" |
242 | 405 | |
243 | #. TRANSLATORS: the user didn't read the man page | |
244 | msgid "Failed to parse arguments" | |
245 | msgstr "आर्गुमेंट पार्स करने में असफल " | |
246 | ||
247 | 406 | #. TRANSLATORS: the user is in a bad place |
248 | 407 | msgid "Failed to connect to D-Bus" |
249 | 408 | msgstr "" |
7 | 7 | msgstr "" |
8 | 8 | "Project-Id-Version: fwupd\n" |
9 | 9 | "Report-Msgid-Bugs-To: \n" |
10 | "POT-Creation-Date: 2015-09-10 09:28+0100\n" | |
11 | "PO-Revision-Date: 2015-09-10 08:28+0000\n" | |
10 | "POT-Creation-Date: 2015-12-07 12:22+0000\n" | |
11 | "PO-Revision-Date: 2015-12-07 12:22+0000\n" | |
12 | 12 | "Last-Translator: Richard Hughes <richard@hughsie.com>\n" |
13 | 13 | "Language-Team: Hungarian (http://www.transifex.com/hughsie/fwupd/language/hu/)\n" |
14 | 14 | "MIME-Version: 1.0\n" |
50 | 50 | "Authentication is required to downgrade the firmware on a removable device" |
51 | 51 | msgstr "Hitelesítés szükséges a firmware visszafejlesztéséhez egy cserélhető eszközön" |
52 | 52 | |
53 | #. TRANSLATORS: turn on all debugging | |
54 | msgid "Show debugging information for all files" | |
55 | msgstr "Hibakeresési információk megjelenítése minden fájlnál" | |
56 | ||
57 | #. TRANSLATORS: for the --verbose arg | |
58 | msgid "Debugging Options" | |
59 | msgstr "Hibakeresési beállítások" | |
60 | ||
61 | #. TRANSLATORS: for the --verbose arg | |
62 | msgid "Show debugging options" | |
63 | msgstr "Hibakeresési beállítások megjelenítése" | |
64 | ||
65 | #. TRANSLATORS: exit after we've started up, used for user profiling | |
66 | msgid "Exit after a small delay" | |
67 | msgstr "Kilépés egy kis késleltetés után" | |
68 | ||
69 | #. TRANSLATORS: exit straight away, used for automatic profiling | |
70 | msgid "Exit after the engine has loaded" | |
71 | msgstr "Kilépés a motor betöltődése után" | |
72 | ||
73 | #. TRANSLATORS: program name | |
74 | msgid "Firmware Update Daemon" | |
75 | msgstr "" | |
76 | ||
77 | #. TRANSLATORS: program summary | |
78 | msgid "Firmware Update D-Bus Service" | |
79 | msgstr "Firmware frissítés D-Bus szolgáltatás" | |
80 | ||
81 | 53 | #. TRANSLATORS: this is a command alias, e.g. 'get-devices' |
82 | 54 | #, c-format |
83 | 55 | msgid "Alias to %s" |
86 | 58 | #. TRANSLATORS: error message |
87 | 59 | msgid "Command not found" |
88 | 60 | msgstr "" |
61 | ||
62 | #. TRANSLATORS: when an action has completed | |
63 | msgid "OK" | |
64 | msgstr "" | |
65 | ||
66 | #. TRANSLATORS: when moving from runtime to DFU mode | |
67 | msgid "Detaching" | |
68 | msgstr "" | |
69 | ||
70 | #. TRANSLATORS: when moving from DFU to runtime mode | |
71 | msgid "Attaching" | |
72 | msgstr "" | |
73 | ||
74 | #. TRANSLATORS: when copying from host to device | |
75 | msgid "Downloading" | |
76 | msgstr "" | |
77 | ||
78 | #. TRANSLATORS: when copying from device to host | |
79 | msgid "Uploading" | |
80 | msgstr "" | |
81 | ||
82 | #. TRANSLATORS: this is when a device is hotplugged | |
83 | msgid "Added" | |
84 | msgstr "" | |
85 | ||
86 | #. TRANSLATORS: this is when a device is hotplugged | |
87 | msgid "Removed" | |
88 | msgstr "" | |
89 | ||
90 | #. TRANSLATORS: this is when a device is hotplugged | |
91 | msgid "Changed" | |
92 | msgstr "" | |
93 | ||
94 | #. TRANSLATORS: this is when a device ctrl+c's a watch | |
95 | msgid "Cancelled" | |
96 | msgstr "" | |
97 | ||
98 | #. TRANSLATORS: Appstream ID for the hardware type | |
99 | msgid "ID" | |
100 | msgstr "" | |
101 | ||
102 | #. TRANSLATORS: interface name, e.g. "Flash" | |
103 | msgid "Name" | |
104 | msgstr "" | |
105 | ||
106 | #. TRANSLATORS: this is the encryption method used when writing | |
107 | msgid "Cipher" | |
108 | msgstr "" | |
109 | ||
110 | #. TRANSLATORS: these are areas of memory on the chip | |
111 | msgid "Region" | |
112 | msgstr "" | |
113 | ||
114 | #. TRANSLATORS: detected a DFU device | |
115 | msgid "Found" | |
116 | msgstr "" | |
117 | ||
118 | #. TRANSLATORS: probably not run as root... | |
119 | #. TRANSLATORS: device has failed to report status | |
120 | #. TRANSLATORS: device status, e.g. "OK" | |
121 | msgid "Status" | |
122 | msgstr "" | |
123 | ||
124 | msgid "Unknown: permission denied" | |
125 | msgstr "" | |
126 | ||
127 | #. TRANSLATORS: device mode, e.g. runtime or DFU | |
128 | msgid "Mode" | |
129 | msgstr "" | |
130 | ||
131 | #. TRANSLATORS: device state, i.e. appIDLE | |
132 | msgid "State" | |
133 | msgstr "" | |
134 | ||
135 | #. TRANSLATORS: device quirks, i.e. things that | |
136 | #. * it does that we have to work around | |
137 | msgid "Quirks" | |
138 | msgstr "" | |
139 | ||
140 | #. TRANSLATORS: command description | |
141 | msgid "Convert firmware to DFU format" | |
142 | msgstr "" | |
143 | ||
144 | #. TRANSLATORS: command description | |
145 | msgid "Merge multiple firmware files into one" | |
146 | msgstr "" | |
147 | ||
148 | #. TRANSLATORS: command description | |
149 | msgid "Set vendor ID on firmware file" | |
150 | msgstr "" | |
151 | ||
152 | #. TRANSLATORS: command description | |
153 | msgid "Set product ID on firmware file" | |
154 | msgstr "" | |
155 | ||
156 | #. TRANSLATORS: command description | |
157 | msgid "Set release version on firmware file" | |
158 | msgstr "" | |
159 | ||
160 | #. TRANSLATORS: command description | |
161 | msgid "Set alternative number on firmware file" | |
162 | msgstr "" | |
163 | ||
164 | #. TRANSLATORS: command description | |
165 | msgid "Set alternative name on firmware file" | |
166 | msgstr "" | |
167 | ||
168 | #. TRANSLATORS: command description | |
169 | msgid "Attach DFU capable device back to runtime" | |
170 | msgstr "" | |
171 | ||
172 | #. TRANSLATORS: command description | |
173 | msgid "Read firmware from device into a file" | |
174 | msgstr "" | |
175 | ||
176 | #. TRANSLATORS: command description | |
177 | msgid "Read firmware from one partition into a file" | |
178 | msgstr "" | |
179 | ||
180 | #. TRANSLATORS: command description | |
181 | msgid "Write firmware from file into device" | |
182 | msgstr "" | |
183 | ||
184 | #. TRANSLATORS: command description | |
185 | msgid "Write firmware from file into one partition" | |
186 | msgstr "" | |
187 | ||
188 | #. TRANSLATORS: command description | |
189 | msgid "List currently attached DFU capable devices" | |
190 | msgstr "" | |
191 | ||
192 | #. TRANSLATORS: command description | |
193 | msgid "Detach currently attached DFU capable device" | |
194 | msgstr "" | |
195 | ||
196 | #. TRANSLATORS: command description | |
197 | msgid "Dump details about a firmware file" | |
198 | msgstr "" | |
199 | ||
200 | #. TRANSLATORS: command description | |
201 | msgid "Watch DFU devices being hotplugged" | |
202 | msgstr "" | |
203 | ||
204 | #. TRANSLATORS: command description | |
205 | msgid "Encrypt firmware data" | |
206 | msgstr "" | |
207 | ||
208 | #. TRANSLATORS: command description | |
209 | msgid "Decrypt firmware data" | |
210 | msgstr "" | |
211 | ||
212 | #. TRANSLATORS: command description | |
213 | msgid "Sets metadata on a firmware file" | |
214 | msgstr "" | |
215 | ||
216 | #. TRANSLATORS: DFU stands for device firmware update | |
217 | msgid "DFU Utility" | |
218 | msgstr "" | |
219 | ||
220 | #. TRANSLATORS: the user didn't read the man page | |
221 | msgid "Failed to parse arguments" | |
222 | msgstr "Nem sikerült feldolgozni az argumentumokat" | |
223 | ||
224 | #. TRANSLATORS: turn on all debugging | |
225 | msgid "Show debugging information for all files" | |
226 | msgstr "Hibakeresési információk megjelenítése minden fájlnál" | |
227 | ||
228 | #. TRANSLATORS: for the --verbose arg | |
229 | msgid "Debugging Options" | |
230 | msgstr "Hibakeresési beállítások" | |
231 | ||
232 | #. TRANSLATORS: for the --verbose arg | |
233 | msgid "Show debugging options" | |
234 | msgstr "Hibakeresési beállítások megjelenítése" | |
235 | ||
236 | #. TRANSLATORS: exit after we've started up, used for user profiling | |
237 | msgid "Exit after a small delay" | |
238 | msgstr "Kilépés egy kis késleltetés után" | |
239 | ||
240 | #. TRANSLATORS: exit straight away, used for automatic profiling | |
241 | msgid "Exit after the engine has loaded" | |
242 | msgstr "Kilépés a motor betöltődése után" | |
243 | ||
244 | #. TRANSLATORS: program name | |
245 | msgid "Firmware Update Daemon" | |
246 | msgstr "" | |
247 | ||
248 | #. TRANSLATORS: program summary | |
249 | msgid "Firmware Update D-Bus Service" | |
250 | msgstr "Firmware frissítés D-Bus szolgáltatás" | |
89 | 251 | |
90 | 252 | #. TRANSLATORS: daemon is inactive |
91 | 253 | msgid "Idle" |
148 | 310 | msgid "Updating %s from %s to %s... " |
149 | 311 | msgstr "%s frissítése: %s -> %s…" |
150 | 312 | |
151 | msgid "OK" | |
152 | msgstr "" | |
153 | ||
154 | 313 | #. TRANSLATORS: first replacement is device name |
155 | 314 | #, c-format |
156 | 315 | msgid "%s has firmware updates:" |
157 | 316 | msgstr "%s firmware frissítésekkel rendelkezik:" |
158 | 317 | |
318 | #. TRANSLATORS: a GUID for the hardware | |
319 | msgid "GUID" | |
320 | msgstr "" | |
321 | ||
159 | 322 | #. TRANSLATORS: section header for firmware version |
160 | 323 | msgid "Version" |
161 | 324 | msgstr "Verzió" |
240 | 403 | msgid "Firmware Utility" |
241 | 404 | msgstr "" |
242 | 405 | |
243 | #. TRANSLATORS: the user didn't read the man page | |
244 | msgid "Failed to parse arguments" | |
245 | msgstr "Nem sikerült feldolgozni az argumentumokat" | |
246 | ||
247 | 406 | #. TRANSLATORS: the user is in a bad place |
248 | 407 | msgid "Failed to connect to D-Bus" |
249 | 408 | msgstr "Nem sikerült csatlakozni a D-Bus rendszerhez" |
7 | 7 | msgstr "" |
8 | 8 | "Project-Id-Version: fwupd\n" |
9 | 9 | "Report-Msgid-Bugs-To: \n" |
10 | "POT-Creation-Date: 2015-09-10 09:28+0100\n" | |
11 | "PO-Revision-Date: 2015-09-10 08:28+0000\n" | |
12 | "Last-Translator: Richard Hughes <richard@hughsie.com>\n" | |
10 | "POT-Creation-Date: 2015-12-07 12:22+0000\n" | |
11 | "PO-Revision-Date: 2015-12-07 14:56+0000\n" | |
12 | "Last-Translator: Piotr Drąg <piotrdrag@gmail.com>\n" | |
13 | 13 | "Language-Team: Polish (http://www.transifex.com/hughsie/fwupd/language/pl/)\n" |
14 | 14 | "MIME-Version: 1.0\n" |
15 | 15 | "Content-Type: text/plain; charset=UTF-8\n" |
50 | 50 | "Authentication is required to downgrade the firmware on a removable device" |
51 | 51 | msgstr "Wymagane jest uwierzytelnienie, aby zainstalować poprzednią wersję oprogramowania sprzętowego urządzenia wymiennego" |
52 | 52 | |
53 | #. TRANSLATORS: turn on all debugging | |
54 | msgid "Show debugging information for all files" | |
55 | msgstr "Wyświetla informacje o debugowaniu dla wszystkich plików" | |
56 | ||
57 | #. TRANSLATORS: for the --verbose arg | |
58 | msgid "Debugging Options" | |
59 | msgstr "Opcje debugowania" | |
60 | ||
61 | #. TRANSLATORS: for the --verbose arg | |
62 | msgid "Show debugging options" | |
63 | msgstr "Wyświetla opcje debugowania" | |
64 | ||
65 | #. TRANSLATORS: exit after we've started up, used for user profiling | |
66 | msgid "Exit after a small delay" | |
67 | msgstr "Kończy działanie po małym opóźnieniu" | |
68 | ||
69 | #. TRANSLATORS: exit straight away, used for automatic profiling | |
70 | msgid "Exit after the engine has loaded" | |
71 | msgstr "Kończy działanie po wczytaniu mechanizmu" | |
72 | ||
73 | #. TRANSLATORS: program name | |
74 | msgid "Firmware Update Daemon" | |
75 | msgstr "Usługa aktualizacji oprogramowania sprzętowego" | |
76 | ||
77 | #. TRANSLATORS: program summary | |
78 | msgid "Firmware Update D-Bus Service" | |
79 | msgstr "Usługa D-Bus aktualizacji oprogramowania sprzętowego" | |
80 | ||
81 | 53 | #. TRANSLATORS: this is a command alias, e.g. 'get-devices' |
82 | 54 | #, c-format |
83 | 55 | msgid "Alias to %s" |
86 | 58 | #. TRANSLATORS: error message |
87 | 59 | msgid "Command not found" |
88 | 60 | msgstr "Nie odnaleziono polecenia" |
61 | ||
62 | #. TRANSLATORS: when an action has completed | |
63 | msgid "OK" | |
64 | msgstr "OK" | |
65 | ||
66 | #. TRANSLATORS: when moving from runtime to DFU mode | |
67 | msgid "Detaching" | |
68 | msgstr "Odłączanie" | |
69 | ||
70 | #. TRANSLATORS: when moving from DFU to runtime mode | |
71 | msgid "Attaching" | |
72 | msgstr "Podłączanie" | |
73 | ||
74 | #. TRANSLATORS: when copying from host to device | |
75 | msgid "Downloading" | |
76 | msgstr "Pobieranie" | |
77 | ||
78 | #. TRANSLATORS: when copying from device to host | |
79 | msgid "Uploading" | |
80 | msgstr "Wysyłanie" | |
81 | ||
82 | #. TRANSLATORS: this is when a device is hotplugged | |
83 | msgid "Added" | |
84 | msgstr "Dodano" | |
85 | ||
86 | #. TRANSLATORS: this is when a device is hotplugged | |
87 | msgid "Removed" | |
88 | msgstr "Usunięto" | |
89 | ||
90 | #. TRANSLATORS: this is when a device is hotplugged | |
91 | msgid "Changed" | |
92 | msgstr "Zmieniono" | |
93 | ||
94 | #. TRANSLATORS: this is when a device ctrl+c's a watch | |
95 | msgid "Cancelled" | |
96 | msgstr "Anulowano" | |
97 | ||
98 | #. TRANSLATORS: Appstream ID for the hardware type | |
99 | msgid "ID" | |
100 | msgstr "Identyfikator" | |
101 | ||
102 | #. TRANSLATORS: interface name, e.g. "Flash" | |
103 | msgid "Name" | |
104 | msgstr "Nazwa" | |
105 | ||
106 | #. TRANSLATORS: this is the encryption method used when writing | |
107 | msgid "Cipher" | |
108 | msgstr "Szyfr" | |
109 | ||
110 | #. TRANSLATORS: these are areas of memory on the chip | |
111 | msgid "Region" | |
112 | msgstr "Region" | |
113 | ||
114 | #. TRANSLATORS: detected a DFU device | |
115 | msgid "Found" | |
116 | msgstr "Odnaleziono" | |
117 | ||
118 | #. TRANSLATORS: probably not run as root... | |
119 | #. TRANSLATORS: device has failed to report status | |
120 | #. TRANSLATORS: device status, e.g. "OK" | |
121 | msgid "Status" | |
122 | msgstr "Stan" | |
123 | ||
124 | msgid "Unknown: permission denied" | |
125 | msgstr "Nieznane: brak uprawnień" | |
126 | ||
127 | #. TRANSLATORS: device mode, e.g. runtime or DFU | |
128 | msgid "Mode" | |
129 | msgstr "Tryb" | |
130 | ||
131 | #. TRANSLATORS: device state, i.e. appIDLE | |
132 | msgid "State" | |
133 | msgstr "Stan" | |
134 | ||
135 | #. TRANSLATORS: device quirks, i.e. things that | |
136 | #. * it does that we have to work around | |
137 | msgid "Quirks" | |
138 | msgstr "Poprawki" | |
139 | ||
140 | #. TRANSLATORS: command description | |
141 | msgid "Convert firmware to DFU format" | |
142 | msgstr "Konwertuje oprogramowanie sprzętowe do formatu DFU" | |
143 | ||
144 | #. TRANSLATORS: command description | |
145 | msgid "Merge multiple firmware files into one" | |
146 | msgstr "Łączy wiele plików oprogramowania sprzętowego w jeden plik" | |
147 | ||
148 | #. TRANSLATORS: command description | |
149 | msgid "Set vendor ID on firmware file" | |
150 | msgstr "Ustawia identyfikator producenta pliku oprogramowania sprzętowego" | |
151 | ||
152 | #. TRANSLATORS: command description | |
153 | msgid "Set product ID on firmware file" | |
154 | msgstr "Ustawia identyfikator produktu pliku oprogramowania sprzętowego" | |
155 | ||
156 | #. TRANSLATORS: command description | |
157 | msgid "Set release version on firmware file" | |
158 | msgstr "Ustawia wersję wydania pliku oprogramowania sprzętowego" | |
159 | ||
160 | #. TRANSLATORS: command description | |
161 | msgid "Set alternative number on firmware file" | |
162 | msgstr "Ustawia alternatywny numer pliku oprogramowania sprzętowego" | |
163 | ||
164 | #. TRANSLATORS: command description | |
165 | msgid "Set alternative name on firmware file" | |
166 | msgstr "Ustawia alternatywną nazwę pliku oprogramowania sprzętowego" | |
167 | ||
168 | #. TRANSLATORS: command description | |
169 | msgid "Attach DFU capable device back to runtime" | |
170 | msgstr "Podłącza urządzenie DFU z powrotem do środowiska wykonawczego" | |
171 | ||
172 | #. TRANSLATORS: command description | |
173 | msgid "Read firmware from device into a file" | |
174 | msgstr "Odczytuje oprogramowanie sprzętowe z urządzenia do pliku" | |
175 | ||
176 | #. TRANSLATORS: command description | |
177 | msgid "Read firmware from one partition into a file" | |
178 | msgstr "Odczytuje oprogramowanie sprzętowe z jednej partycji do pliku" | |
179 | ||
180 | #. TRANSLATORS: command description | |
181 | msgid "Write firmware from file into device" | |
182 | msgstr "Zapisuje oprogramowanie sprzętowe z pliku na urządzenie" | |
183 | ||
184 | #. TRANSLATORS: command description | |
185 | msgid "Write firmware from file into one partition" | |
186 | msgstr "Zapisuje oprogramowanie sprzętowe z pliku na jedną partycję" | |
187 | ||
188 | #. TRANSLATORS: command description | |
189 | msgid "List currently attached DFU capable devices" | |
190 | msgstr "Wyświetla listę obecnie podłączonych urządzeń DFU" | |
191 | ||
192 | #. TRANSLATORS: command description | |
193 | msgid "Detach currently attached DFU capable device" | |
194 | msgstr "Odłącza obecnie podłączone urządzenie DFU" | |
195 | ||
196 | #. TRANSLATORS: command description | |
197 | msgid "Dump details about a firmware file" | |
198 | msgstr "Zrzuca informacje o pliku oprogramowania sprzętowego" | |
199 | ||
200 | #. TRANSLATORS: command description | |
201 | msgid "Watch DFU devices being hotplugged" | |
202 | msgstr "Obserwuje podłączanie urządzeń DFU w czasie działania" | |
203 | ||
204 | #. TRANSLATORS: command description | |
205 | msgid "Encrypt firmware data" | |
206 | msgstr "Szyfruje dane oprogramowania sprzętowego" | |
207 | ||
208 | #. TRANSLATORS: command description | |
209 | msgid "Decrypt firmware data" | |
210 | msgstr "Odszyfrowuje dane oprogramowania sprzętowego" | |
211 | ||
212 | #. TRANSLATORS: command description | |
213 | msgid "Sets metadata on a firmware file" | |
214 | msgstr "Ustawia metadane pliku oprogramowania sprzętowego" | |
215 | ||
216 | #. TRANSLATORS: DFU stands for device firmware update | |
217 | msgid "DFU Utility" | |
218 | msgstr "Narzędzie DFU" | |
219 | ||
220 | #. TRANSLATORS: the user didn't read the man page | |
221 | msgid "Failed to parse arguments" | |
222 | msgstr "Przetworzenie parametrów się nie powiodło" | |
223 | ||
224 | #. TRANSLATORS: turn on all debugging | |
225 | msgid "Show debugging information for all files" | |
226 | msgstr "Wyświetla informacje o debugowaniu dla wszystkich plików" | |
227 | ||
228 | #. TRANSLATORS: for the --verbose arg | |
229 | msgid "Debugging Options" | |
230 | msgstr "Opcje debugowania" | |
231 | ||
232 | #. TRANSLATORS: for the --verbose arg | |
233 | msgid "Show debugging options" | |
234 | msgstr "Wyświetla opcje debugowania" | |
235 | ||
236 | #. TRANSLATORS: exit after we've started up, used for user profiling | |
237 | msgid "Exit after a small delay" | |
238 | msgstr "Kończy działanie po małym opóźnieniu" | |
239 | ||
240 | #. TRANSLATORS: exit straight away, used for automatic profiling | |
241 | msgid "Exit after the engine has loaded" | |
242 | msgstr "Kończy działanie po wczytaniu mechanizmu" | |
243 | ||
244 | #. TRANSLATORS: program name | |
245 | msgid "Firmware Update Daemon" | |
246 | msgstr "Usługa aktualizacji oprogramowania sprzętowego" | |
247 | ||
248 | #. TRANSLATORS: program summary | |
249 | msgid "Firmware Update D-Bus Service" | |
250 | msgstr "Usługa D-Bus aktualizacji oprogramowania sprzętowego" | |
89 | 251 | |
90 | 252 | #. TRANSLATORS: daemon is inactive |
91 | 253 | msgid "Idle" |
148 | 310 | msgid "Updating %s from %s to %s... " |
149 | 311 | msgstr "Aktualizowanie %s z wersji %s do %s… " |
150 | 312 | |
151 | msgid "OK" | |
152 | msgstr "OK" | |
153 | ||
154 | 313 | #. TRANSLATORS: first replacement is device name |
155 | 314 | #, c-format |
156 | 315 | msgid "%s has firmware updates:" |
157 | 316 | msgstr "Dostępne są aktualizacje oprogramowania sprzętowego dla urządzenia %s:" |
158 | 317 | |
318 | #. TRANSLATORS: a GUID for the hardware | |
319 | msgid "GUID" | |
320 | msgstr "GUID" | |
321 | ||
159 | 322 | #. TRANSLATORS: section header for firmware version |
160 | 323 | msgid "Version" |
161 | 324 | msgstr "Wersja" |
240 | 403 | msgid "Firmware Utility" |
241 | 404 | msgstr "Narzędzie oprogramowania sprzętowego" |
242 | 405 | |
243 | #. TRANSLATORS: the user didn't read the man page | |
244 | msgid "Failed to parse arguments" | |
245 | msgstr "Przetworzenie parametrów się nie powiodło" | |
246 | ||
247 | 406 | #. TRANSLATORS: the user is in a bad place |
248 | 407 | msgid "Failed to connect to D-Bus" |
249 | 408 | msgstr "Połączenie z magistralą D-Bus się nie powiodło" |
8 | 8 | msgstr "" |
9 | 9 | "Project-Id-Version: fwupd\n" |
10 | 10 | "Report-Msgid-Bugs-To: \n" |
11 | "POT-Creation-Date: 2015-10-28 09:58+0000\n" | |
12 | "PO-Revision-Date: 2015-10-07 12:34+0000\n" | |
13 | "Last-Translator: Derek Stavis <dekestavis@gmail.com>\n" | |
11 | "POT-Creation-Date: 2015-12-07 12:22+0000\n" | |
12 | "PO-Revision-Date: 2015-12-07 12:22+0000\n" | |
13 | "Last-Translator: Richard Hughes <richard@hughsie.com>\n" | |
14 | 14 | "Language-Team: Portuguese (Brazil) (http://www.transifex.com/hughsie/fwupd/language/pt_BR/)\n" |
15 | 15 | "MIME-Version: 1.0\n" |
16 | 16 | "Content-Type: text/plain; charset=UTF-8\n" |
51 | 51 | "Authentication is required to downgrade the firmware on a removable device" |
52 | 52 | msgstr "É requerida autenticação para voltar a versão do firmware em um dispositivo removível" |
53 | 53 | |
54 | #. TRANSLATORS: turn on all debugging | |
55 | msgid "Show debugging information for all files" | |
56 | msgstr "Mostrar informações de depuração para todos os arquivos" | |
57 | ||
58 | #. TRANSLATORS: for the --verbose arg | |
59 | msgid "Debugging Options" | |
60 | msgstr "Opções de depuração" | |
61 | ||
62 | #. TRANSLATORS: for the --verbose arg | |
63 | msgid "Show debugging options" | |
64 | msgstr "Mostrar opções de depuração" | |
65 | ||
66 | #. TRANSLATORS: exit after we've started up, used for user profiling | |
67 | msgid "Exit after a small delay" | |
68 | msgstr "Sair após pequeno atraso" | |
69 | ||
70 | #. TRANSLATORS: exit straight away, used for automatic profiling | |
71 | msgid "Exit after the engine has loaded" | |
72 | msgstr "Sair após o carregamento do motor" | |
73 | ||
74 | #. TRANSLATORS: program name | |
75 | msgid "Firmware Update Daemon" | |
76 | msgstr "Daemon de Atualização de Firmware" | |
77 | ||
78 | #. TRANSLATORS: program summary | |
79 | msgid "Firmware Update D-Bus Service" | |
80 | msgstr "Serviço D-Bus de Atualização de Firmware" | |
81 | ||
82 | 54 | #. TRANSLATORS: this is a command alias, e.g. 'get-devices' |
83 | 55 | #, c-format |
84 | 56 | msgid "Alias to %s" |
87 | 59 | #. TRANSLATORS: error message |
88 | 60 | msgid "Command not found" |
89 | 61 | msgstr "Comando não encontrado" |
62 | ||
63 | #. TRANSLATORS: when an action has completed | |
64 | msgid "OK" | |
65 | msgstr "OK" | |
66 | ||
67 | #. TRANSLATORS: when moving from runtime to DFU mode | |
68 | msgid "Detaching" | |
69 | msgstr "" | |
70 | ||
71 | #. TRANSLATORS: when moving from DFU to runtime mode | |
72 | msgid "Attaching" | |
73 | msgstr "" | |
74 | ||
75 | #. TRANSLATORS: when copying from host to device | |
76 | msgid "Downloading" | |
77 | msgstr "" | |
78 | ||
79 | #. TRANSLATORS: when copying from device to host | |
80 | msgid "Uploading" | |
81 | msgstr "" | |
82 | ||
83 | #. TRANSLATORS: this is when a device is hotplugged | |
84 | msgid "Added" | |
85 | msgstr "" | |
86 | ||
87 | #. TRANSLATORS: this is when a device is hotplugged | |
88 | msgid "Removed" | |
89 | msgstr "" | |
90 | ||
91 | #. TRANSLATORS: this is when a device is hotplugged | |
92 | msgid "Changed" | |
93 | msgstr "" | |
94 | ||
95 | #. TRANSLATORS: this is when a device ctrl+c's a watch | |
96 | msgid "Cancelled" | |
97 | msgstr "" | |
98 | ||
99 | #. TRANSLATORS: Appstream ID for the hardware type | |
100 | msgid "ID" | |
101 | msgstr "" | |
102 | ||
103 | #. TRANSLATORS: interface name, e.g. "Flash" | |
104 | msgid "Name" | |
105 | msgstr "" | |
106 | ||
107 | #. TRANSLATORS: this is the encryption method used when writing | |
108 | msgid "Cipher" | |
109 | msgstr "" | |
110 | ||
111 | #. TRANSLATORS: these are areas of memory on the chip | |
112 | msgid "Region" | |
113 | msgstr "" | |
114 | ||
115 | #. TRANSLATORS: detected a DFU device | |
116 | msgid "Found" | |
117 | msgstr "" | |
118 | ||
119 | #. TRANSLATORS: probably not run as root... | |
120 | #. TRANSLATORS: device has failed to report status | |
121 | #. TRANSLATORS: device status, e.g. "OK" | |
122 | msgid "Status" | |
123 | msgstr "" | |
124 | ||
125 | msgid "Unknown: permission denied" | |
126 | msgstr "" | |
127 | ||
128 | #. TRANSLATORS: device mode, e.g. runtime or DFU | |
129 | msgid "Mode" | |
130 | msgstr "" | |
131 | ||
132 | #. TRANSLATORS: device state, i.e. appIDLE | |
133 | msgid "State" | |
134 | msgstr "" | |
135 | ||
136 | #. TRANSLATORS: device quirks, i.e. things that | |
137 | #. * it does that we have to work around | |
138 | msgid "Quirks" | |
139 | msgstr "" | |
140 | ||
141 | #. TRANSLATORS: command description | |
142 | msgid "Convert firmware to DFU format" | |
143 | msgstr "" | |
144 | ||
145 | #. TRANSLATORS: command description | |
146 | msgid "Merge multiple firmware files into one" | |
147 | msgstr "" | |
148 | ||
149 | #. TRANSLATORS: command description | |
150 | msgid "Set vendor ID on firmware file" | |
151 | msgstr "" | |
152 | ||
153 | #. TRANSLATORS: command description | |
154 | msgid "Set product ID on firmware file" | |
155 | msgstr "" | |
156 | ||
157 | #. TRANSLATORS: command description | |
158 | msgid "Set release version on firmware file" | |
159 | msgstr "" | |
160 | ||
161 | #. TRANSLATORS: command description | |
162 | msgid "Set alternative number on firmware file" | |
163 | msgstr "" | |
164 | ||
165 | #. TRANSLATORS: command description | |
166 | msgid "Set alternative name on firmware file" | |
167 | msgstr "" | |
168 | ||
169 | #. TRANSLATORS: command description | |
170 | msgid "Attach DFU capable device back to runtime" | |
171 | msgstr "" | |
172 | ||
173 | #. TRANSLATORS: command description | |
174 | msgid "Read firmware from device into a file" | |
175 | msgstr "" | |
176 | ||
177 | #. TRANSLATORS: command description | |
178 | msgid "Read firmware from one partition into a file" | |
179 | msgstr "" | |
180 | ||
181 | #. TRANSLATORS: command description | |
182 | msgid "Write firmware from file into device" | |
183 | msgstr "" | |
184 | ||
185 | #. TRANSLATORS: command description | |
186 | msgid "Write firmware from file into one partition" | |
187 | msgstr "" | |
188 | ||
189 | #. TRANSLATORS: command description | |
190 | msgid "List currently attached DFU capable devices" | |
191 | msgstr "" | |
192 | ||
193 | #. TRANSLATORS: command description | |
194 | msgid "Detach currently attached DFU capable device" | |
195 | msgstr "" | |
196 | ||
197 | #. TRANSLATORS: command description | |
198 | msgid "Dump details about a firmware file" | |
199 | msgstr "" | |
200 | ||
201 | #. TRANSLATORS: command description | |
202 | msgid "Watch DFU devices being hotplugged" | |
203 | msgstr "" | |
204 | ||
205 | #. TRANSLATORS: command description | |
206 | msgid "Encrypt firmware data" | |
207 | msgstr "" | |
208 | ||
209 | #. TRANSLATORS: command description | |
210 | msgid "Decrypt firmware data" | |
211 | msgstr "" | |
212 | ||
213 | #. TRANSLATORS: command description | |
214 | msgid "Sets metadata on a firmware file" | |
215 | msgstr "" | |
216 | ||
217 | #. TRANSLATORS: DFU stands for device firmware update | |
218 | msgid "DFU Utility" | |
219 | msgstr "" | |
220 | ||
221 | #. TRANSLATORS: the user didn't read the man page | |
222 | msgid "Failed to parse arguments" | |
223 | msgstr "Falha ao interpretar argumentos" | |
224 | ||
225 | #. TRANSLATORS: turn on all debugging | |
226 | msgid "Show debugging information for all files" | |
227 | msgstr "Mostrar informações de depuração para todos os arquivos" | |
228 | ||
229 | #. TRANSLATORS: for the --verbose arg | |
230 | msgid "Debugging Options" | |
231 | msgstr "Opções de depuração" | |
232 | ||
233 | #. TRANSLATORS: for the --verbose arg | |
234 | msgid "Show debugging options" | |
235 | msgstr "Mostrar opções de depuração" | |
236 | ||
237 | #. TRANSLATORS: exit after we've started up, used for user profiling | |
238 | msgid "Exit after a small delay" | |
239 | msgstr "Sair após pequeno atraso" | |
240 | ||
241 | #. TRANSLATORS: exit straight away, used for automatic profiling | |
242 | msgid "Exit after the engine has loaded" | |
243 | msgstr "Sair após o carregamento do motor" | |
244 | ||
245 | #. TRANSLATORS: program name | |
246 | msgid "Firmware Update Daemon" | |
247 | msgstr "Daemon de Atualização de Firmware" | |
248 | ||
249 | #. TRANSLATORS: program summary | |
250 | msgid "Firmware Update D-Bus Service" | |
251 | msgstr "Serviço D-Bus de Atualização de Firmware" | |
90 | 252 | |
91 | 253 | #. TRANSLATORS: daemon is inactive |
92 | 254 | msgid "Idle" |
149 | 311 | msgid "Updating %s from %s to %s... " |
150 | 312 | msgstr "Atualizando %s de %s para %s..." |
151 | 313 | |
152 | msgid "OK" | |
153 | msgstr "OK" | |
154 | ||
155 | 314 | #. TRANSLATORS: first replacement is device name |
156 | 315 | #, c-format |
157 | 316 | msgid "%s has firmware updates:" |
158 | 317 | msgstr "%s tem atualizações:" |
159 | 318 | |
319 | #. TRANSLATORS: a GUID for the hardware | |
320 | msgid "GUID" | |
321 | msgstr "" | |
322 | ||
160 | 323 | #. TRANSLATORS: section header for firmware version |
161 | 324 | msgid "Version" |
162 | 325 | msgstr "Versão" |
241 | 404 | msgid "Firmware Utility" |
242 | 405 | msgstr "Utilitário de Firmware" |
243 | 406 | |
244 | #. TRANSLATORS: the user didn't read the man page | |
245 | msgid "Failed to parse arguments" | |
246 | msgstr "Falha ao interpretar argumentos" | |
247 | ||
248 | 407 | #. TRANSLATORS: the user is in a bad place |
249 | 408 | msgid "Failed to connect to D-Bus" |
250 | 409 | msgstr "Falha ao conectar ao D-Bus" |
7 | 7 | msgstr "" |
8 | 8 | "Project-Id-Version: fwupd\n" |
9 | 9 | "Report-Msgid-Bugs-To: \n" |
10 | "POT-Creation-Date: 2015-09-10 09:28+0100\n" | |
11 | "PO-Revision-Date: 2015-09-10 08:28+0000\n" | |
10 | "POT-Creation-Date: 2015-12-07 12:22+0000\n" | |
11 | "PO-Revision-Date: 2015-12-07 12:22+0000\n" | |
12 | 12 | "Last-Translator: Richard Hughes <richard@hughsie.com>\n" |
13 | 13 | "Language-Team: Russian (http://www.transifex.com/hughsie/fwupd/language/ru/)\n" |
14 | 14 | "MIME-Version: 1.0\n" |
50 | 50 | "Authentication is required to downgrade the firmware on a removable device" |
51 | 51 | msgstr "Для понижения версии микропрограммы на съёмном устройстве требуется аутентификация" |
52 | 52 | |
53 | #. TRANSLATORS: turn on all debugging | |
54 | msgid "Show debugging information for all files" | |
55 | msgstr "Показать отладочную информацию для всех файлов" | |
56 | ||
57 | #. TRANSLATORS: for the --verbose arg | |
58 | msgid "Debugging Options" | |
59 | msgstr "Параметры отладки" | |
60 | ||
61 | #. TRANSLATORS: for the --verbose arg | |
62 | msgid "Show debugging options" | |
63 | msgstr "Показать параметры отладки" | |
64 | ||
65 | #. TRANSLATORS: exit after we've started up, used for user profiling | |
66 | msgid "Exit after a small delay" | |
67 | msgstr "Выйти после небольшой задержки" | |
68 | ||
69 | #. TRANSLATORS: exit straight away, used for automatic profiling | |
70 | msgid "Exit after the engine has loaded" | |
71 | msgstr "Выйти после загрузки движка" | |
72 | ||
73 | #. TRANSLATORS: program name | |
74 | msgid "Firmware Update Daemon" | |
75 | msgstr "Служба обновления микропрограммы" | |
76 | ||
77 | #. TRANSLATORS: program summary | |
78 | msgid "Firmware Update D-Bus Service" | |
79 | msgstr "D-Bus служба обновления микропрограммы" | |
80 | ||
81 | 53 | #. TRANSLATORS: this is a command alias, e.g. 'get-devices' |
82 | 54 | #, c-format |
83 | 55 | msgid "Alias to %s" |
86 | 58 | #. TRANSLATORS: error message |
87 | 59 | msgid "Command not found" |
88 | 60 | msgstr "Команда не найдена" |
61 | ||
62 | #. TRANSLATORS: when an action has completed | |
63 | msgid "OK" | |
64 | msgstr "ОК" | |
65 | ||
66 | #. TRANSLATORS: when moving from runtime to DFU mode | |
67 | msgid "Detaching" | |
68 | msgstr "" | |
69 | ||
70 | #. TRANSLATORS: when moving from DFU to runtime mode | |
71 | msgid "Attaching" | |
72 | msgstr "" | |
73 | ||
74 | #. TRANSLATORS: when copying from host to device | |
75 | msgid "Downloading" | |
76 | msgstr "" | |
77 | ||
78 | #. TRANSLATORS: when copying from device to host | |
79 | msgid "Uploading" | |
80 | msgstr "" | |
81 | ||
82 | #. TRANSLATORS: this is when a device is hotplugged | |
83 | msgid "Added" | |
84 | msgstr "" | |
85 | ||
86 | #. TRANSLATORS: this is when a device is hotplugged | |
87 | msgid "Removed" | |
88 | msgstr "" | |
89 | ||
90 | #. TRANSLATORS: this is when a device is hotplugged | |
91 | msgid "Changed" | |
92 | msgstr "" | |
93 | ||
94 | #. TRANSLATORS: this is when a device ctrl+c's a watch | |
95 | msgid "Cancelled" | |
96 | msgstr "" | |
97 | ||
98 | #. TRANSLATORS: Appstream ID for the hardware type | |
99 | msgid "ID" | |
100 | msgstr "" | |
101 | ||
102 | #. TRANSLATORS: interface name, e.g. "Flash" | |
103 | msgid "Name" | |
104 | msgstr "" | |
105 | ||
106 | #. TRANSLATORS: this is the encryption method used when writing | |
107 | msgid "Cipher" | |
108 | msgstr "" | |
109 | ||
110 | #. TRANSLATORS: these are areas of memory on the chip | |
111 | msgid "Region" | |
112 | msgstr "" | |
113 | ||
114 | #. TRANSLATORS: detected a DFU device | |
115 | msgid "Found" | |
116 | msgstr "" | |
117 | ||
118 | #. TRANSLATORS: probably not run as root... | |
119 | #. TRANSLATORS: device has failed to report status | |
120 | #. TRANSLATORS: device status, e.g. "OK" | |
121 | msgid "Status" | |
122 | msgstr "" | |
123 | ||
124 | msgid "Unknown: permission denied" | |
125 | msgstr "" | |
126 | ||
127 | #. TRANSLATORS: device mode, e.g. runtime or DFU | |
128 | msgid "Mode" | |
129 | msgstr "" | |
130 | ||
131 | #. TRANSLATORS: device state, i.e. appIDLE | |
132 | msgid "State" | |
133 | msgstr "" | |
134 | ||
135 | #. TRANSLATORS: device quirks, i.e. things that | |
136 | #. * it does that we have to work around | |
137 | msgid "Quirks" | |
138 | msgstr "" | |
139 | ||
140 | #. TRANSLATORS: command description | |
141 | msgid "Convert firmware to DFU format" | |
142 | msgstr "" | |
143 | ||
144 | #. TRANSLATORS: command description | |
145 | msgid "Merge multiple firmware files into one" | |
146 | msgstr "" | |
147 | ||
148 | #. TRANSLATORS: command description | |
149 | msgid "Set vendor ID on firmware file" | |
150 | msgstr "" | |
151 | ||
152 | #. TRANSLATORS: command description | |
153 | msgid "Set product ID on firmware file" | |
154 | msgstr "" | |
155 | ||
156 | #. TRANSLATORS: command description | |
157 | msgid "Set release version on firmware file" | |
158 | msgstr "" | |
159 | ||
160 | #. TRANSLATORS: command description | |
161 | msgid "Set alternative number on firmware file" | |
162 | msgstr "" | |
163 | ||
164 | #. TRANSLATORS: command description | |
165 | msgid "Set alternative name on firmware file" | |
166 | msgstr "" | |
167 | ||
168 | #. TRANSLATORS: command description | |
169 | msgid "Attach DFU capable device back to runtime" | |
170 | msgstr "" | |
171 | ||
172 | #. TRANSLATORS: command description | |
173 | msgid "Read firmware from device into a file" | |
174 | msgstr "" | |
175 | ||
176 | #. TRANSLATORS: command description | |
177 | msgid "Read firmware from one partition into a file" | |
178 | msgstr "" | |
179 | ||
180 | #. TRANSLATORS: command description | |
181 | msgid "Write firmware from file into device" | |
182 | msgstr "" | |
183 | ||
184 | #. TRANSLATORS: command description | |
185 | msgid "Write firmware from file into one partition" | |
186 | msgstr "" | |
187 | ||
188 | #. TRANSLATORS: command description | |
189 | msgid "List currently attached DFU capable devices" | |
190 | msgstr "" | |
191 | ||
192 | #. TRANSLATORS: command description | |
193 | msgid "Detach currently attached DFU capable device" | |
194 | msgstr "" | |
195 | ||
196 | #. TRANSLATORS: command description | |
197 | msgid "Dump details about a firmware file" | |
198 | msgstr "" | |
199 | ||
200 | #. TRANSLATORS: command description | |
201 | msgid "Watch DFU devices being hotplugged" | |
202 | msgstr "" | |
203 | ||
204 | #. TRANSLATORS: command description | |
205 | msgid "Encrypt firmware data" | |
206 | msgstr "" | |
207 | ||
208 | #. TRANSLATORS: command description | |
209 | msgid "Decrypt firmware data" | |
210 | msgstr "" | |
211 | ||
212 | #. TRANSLATORS: command description | |
213 | msgid "Sets metadata on a firmware file" | |
214 | msgstr "" | |
215 | ||
216 | #. TRANSLATORS: DFU stands for device firmware update | |
217 | msgid "DFU Utility" | |
218 | msgstr "" | |
219 | ||
220 | #. TRANSLATORS: the user didn't read the man page | |
221 | msgid "Failed to parse arguments" | |
222 | msgstr "Не удалось разобрать аргументы" | |
223 | ||
224 | #. TRANSLATORS: turn on all debugging | |
225 | msgid "Show debugging information for all files" | |
226 | msgstr "Показать отладочную информацию для всех файлов" | |
227 | ||
228 | #. TRANSLATORS: for the --verbose arg | |
229 | msgid "Debugging Options" | |
230 | msgstr "Параметры отладки" | |
231 | ||
232 | #. TRANSLATORS: for the --verbose arg | |
233 | msgid "Show debugging options" | |
234 | msgstr "Показать параметры отладки" | |
235 | ||
236 | #. TRANSLATORS: exit after we've started up, used for user profiling | |
237 | msgid "Exit after a small delay" | |
238 | msgstr "Выйти после небольшой задержки" | |
239 | ||
240 | #. TRANSLATORS: exit straight away, used for automatic profiling | |
241 | msgid "Exit after the engine has loaded" | |
242 | msgstr "Выйти после загрузки движка" | |
243 | ||
244 | #. TRANSLATORS: program name | |
245 | msgid "Firmware Update Daemon" | |
246 | msgstr "Служба обновления микропрограммы" | |
247 | ||
248 | #. TRANSLATORS: program summary | |
249 | msgid "Firmware Update D-Bus Service" | |
250 | msgstr "D-Bus служба обновления микропрограммы" | |
89 | 251 | |
90 | 252 | #. TRANSLATORS: daemon is inactive |
91 | 253 | msgid "Idle" |
148 | 310 | msgid "Updating %s from %s to %s... " |
149 | 311 | msgstr "Обновление %s с %s на %s…" |
150 | 312 | |
151 | msgid "OK" | |
152 | msgstr "ОК" | |
153 | ||
154 | 313 | #. TRANSLATORS: first replacement is device name |
155 | 314 | #, c-format |
156 | 315 | msgid "%s has firmware updates:" |
157 | 316 | msgstr "У %s есть обновления микропрограммы:" |
158 | 317 | |
318 | #. TRANSLATORS: a GUID for the hardware | |
319 | msgid "GUID" | |
320 | msgstr "" | |
321 | ||
159 | 322 | #. TRANSLATORS: section header for firmware version |
160 | 323 | msgid "Version" |
161 | 324 | msgstr "Версия" |
240 | 403 | msgid "Firmware Utility" |
241 | 404 | msgstr "Средство работы с микропрограммами" |
242 | 405 | |
243 | #. TRANSLATORS: the user didn't read the man page | |
244 | msgid "Failed to parse arguments" | |
245 | msgstr "Не удалось разобрать аргументы" | |
246 | ||
247 | 406 | #. TRANSLATORS: the user is in a bad place |
248 | 407 | msgid "Failed to connect to D-Bus" |
249 | 408 | msgstr "Не удалось подключиться к D-Bus" |
7 | 7 | msgstr "" |
8 | 8 | "Project-Id-Version: fwupd\n" |
9 | 9 | "Report-Msgid-Bugs-To: \n" |
10 | "POT-Creation-Date: 2015-09-10 09:28+0100\n" | |
11 | "PO-Revision-Date: 2015-09-10 08:28+0000\n" | |
12 | "Last-Translator: Richard Hughes <richard@hughsie.com>\n" | |
10 | "POT-Creation-Date: 2015-12-07 12:22+0000\n" | |
11 | "PO-Revision-Date: 2015-12-07 12:35+0000\n" | |
12 | "Last-Translator: Dušan Kazik <prescott66@gmail.com>\n" | |
13 | 13 | "Language-Team: Slovak (http://www.transifex.com/hughsie/fwupd/language/sk/)\n" |
14 | 14 | "MIME-Version: 1.0\n" |
15 | 15 | "Content-Type: text/plain; charset=UTF-8\n" |
50 | 50 | "Authentication is required to downgrade the firmware on a removable device" |
51 | 51 | msgstr "Vyžaduje sa overenie totožnosti na prechod na staršiu verziu firmvéru vymeniteľného zariadenia" |
52 | 52 | |
53 | #. TRANSLATORS: turn on all debugging | |
54 | msgid "Show debugging information for all files" | |
55 | msgstr "Zobrazí ladiace informácie pre všetky súbory" | |
56 | ||
57 | #. TRANSLATORS: for the --verbose arg | |
58 | msgid "Debugging Options" | |
59 | msgstr "Voľby ladenia" | |
60 | ||
61 | #. TRANSLATORS: for the --verbose arg | |
62 | msgid "Show debugging options" | |
63 | msgstr "Zobrazí voľby ladenia" | |
64 | ||
65 | #. TRANSLATORS: exit after we've started up, used for user profiling | |
66 | msgid "Exit after a small delay" | |
67 | msgstr "Skončí po krátkom oneskorení" | |
68 | ||
69 | #. TRANSLATORS: exit straight away, used for automatic profiling | |
70 | msgid "Exit after the engine has loaded" | |
71 | msgstr "Skončí po načítaní jadra" | |
72 | ||
73 | #. TRANSLATORS: program name | |
74 | msgid "Firmware Update Daemon" | |
75 | msgstr "Démon aktualizácie firmvéru" | |
76 | ||
77 | #. TRANSLATORS: program summary | |
78 | msgid "Firmware Update D-Bus Service" | |
79 | msgstr "Služba zbernice D-Bus na aktualizovanie firmvéru" | |
80 | ||
81 | 53 | #. TRANSLATORS: this is a command alias, e.g. 'get-devices' |
82 | 54 | #, c-format |
83 | 55 | msgid "Alias to %s" |
86 | 58 | #. TRANSLATORS: error message |
87 | 59 | msgid "Command not found" |
88 | 60 | msgstr "Príkaz nenájdený" |
61 | ||
62 | #. TRANSLATORS: when an action has completed | |
63 | msgid "OK" | |
64 | msgstr "OK" | |
65 | ||
66 | #. TRANSLATORS: when moving from runtime to DFU mode | |
67 | msgid "Detaching" | |
68 | msgstr "" | |
69 | ||
70 | #. TRANSLATORS: when moving from DFU to runtime mode | |
71 | msgid "Attaching" | |
72 | msgstr "" | |
73 | ||
74 | #. TRANSLATORS: when copying from host to device | |
75 | msgid "Downloading" | |
76 | msgstr "Preberá sa" | |
77 | ||
78 | #. TRANSLATORS: when copying from device to host | |
79 | msgid "Uploading" | |
80 | msgstr "Odovzdáva sa" | |
81 | ||
82 | #. TRANSLATORS: this is when a device is hotplugged | |
83 | msgid "Added" | |
84 | msgstr "Pridané" | |
85 | ||
86 | #. TRANSLATORS: this is when a device is hotplugged | |
87 | msgid "Removed" | |
88 | msgstr "Odstránené" | |
89 | ||
90 | #. TRANSLATORS: this is when a device is hotplugged | |
91 | msgid "Changed" | |
92 | msgstr "Zmenené" | |
93 | ||
94 | #. TRANSLATORS: this is when a device ctrl+c's a watch | |
95 | msgid "Cancelled" | |
96 | msgstr "Zrušené" | |
97 | ||
98 | #. TRANSLATORS: Appstream ID for the hardware type | |
99 | msgid "ID" | |
100 | msgstr "ID" | |
101 | ||
102 | #. TRANSLATORS: interface name, e.g. "Flash" | |
103 | msgid "Name" | |
104 | msgstr "Názov" | |
105 | ||
106 | #. TRANSLATORS: this is the encryption method used when writing | |
107 | msgid "Cipher" | |
108 | msgstr "Šifra" | |
109 | ||
110 | #. TRANSLATORS: these are areas of memory on the chip | |
111 | msgid "Region" | |
112 | msgstr "Oblasť" | |
113 | ||
114 | #. TRANSLATORS: detected a DFU device | |
115 | msgid "Found" | |
116 | msgstr "Nájdené" | |
117 | ||
118 | #. TRANSLATORS: probably not run as root... | |
119 | #. TRANSLATORS: device has failed to report status | |
120 | #. TRANSLATORS: device status, e.g. "OK" | |
121 | msgid "Status" | |
122 | msgstr "Stav" | |
123 | ||
124 | msgid "Unknown: permission denied" | |
125 | msgstr "Neznáme: prístup zamietnutý" | |
126 | ||
127 | #. TRANSLATORS: device mode, e.g. runtime or DFU | |
128 | msgid "Mode" | |
129 | msgstr "Režim" | |
130 | ||
131 | #. TRANSLATORS: device state, i.e. appIDLE | |
132 | msgid "State" | |
133 | msgstr "Stav" | |
134 | ||
135 | #. TRANSLATORS: device quirks, i.e. things that | |
136 | #. * it does that we have to work around | |
137 | msgid "Quirks" | |
138 | msgstr "" | |
139 | ||
140 | #. TRANSLATORS: command description | |
141 | msgid "Convert firmware to DFU format" | |
142 | msgstr "Skonvertuje firmvér do formátu DFU" | |
143 | ||
144 | #. TRANSLATORS: command description | |
145 | msgid "Merge multiple firmware files into one" | |
146 | msgstr "Zlúči viacero súborov s firmvérami do jedného" | |
147 | ||
148 | #. TRANSLATORS: command description | |
149 | msgid "Set vendor ID on firmware file" | |
150 | msgstr "Nastaví ID výrobcu pre súbor s firmvérom" | |
151 | ||
152 | #. TRANSLATORS: command description | |
153 | msgid "Set product ID on firmware file" | |
154 | msgstr "Nastaví ID produktu pre súbor s firmvérom" | |
155 | ||
156 | #. TRANSLATORS: command description | |
157 | msgid "Set release version on firmware file" | |
158 | msgstr "Nastaví verziu vydania pre súbor s firmvérom" | |
159 | ||
160 | #. TRANSLATORS: command description | |
161 | msgid "Set alternative number on firmware file" | |
162 | msgstr "Nastaví alternatívne číslo pre súbor s firmvérom" | |
163 | ||
164 | #. TRANSLATORS: command description | |
165 | msgid "Set alternative name on firmware file" | |
166 | msgstr "Nastaví alternatívny názov pre súbor s firmvérom" | |
167 | ||
168 | #. TRANSLATORS: command description | |
169 | msgid "Attach DFU capable device back to runtime" | |
170 | msgstr "" | |
171 | ||
172 | #. TRANSLATORS: command description | |
173 | msgid "Read firmware from device into a file" | |
174 | msgstr "Prečíta firmvér zo zariadenia do súboru" | |
175 | ||
176 | #. TRANSLATORS: command description | |
177 | msgid "Read firmware from one partition into a file" | |
178 | msgstr "" | |
179 | ||
180 | #. TRANSLATORS: command description | |
181 | msgid "Write firmware from file into device" | |
182 | msgstr "" | |
183 | ||
184 | #. TRANSLATORS: command description | |
185 | msgid "Write firmware from file into one partition" | |
186 | msgstr "" | |
187 | ||
188 | #. TRANSLATORS: command description | |
189 | msgid "List currently attached DFU capable devices" | |
190 | msgstr "" | |
191 | ||
192 | #. TRANSLATORS: command description | |
193 | msgid "Detach currently attached DFU capable device" | |
194 | msgstr "" | |
195 | ||
196 | #. TRANSLATORS: command description | |
197 | msgid "Dump details about a firmware file" | |
198 | msgstr "" | |
199 | ||
200 | #. TRANSLATORS: command description | |
201 | msgid "Watch DFU devices being hotplugged" | |
202 | msgstr "" | |
203 | ||
204 | #. TRANSLATORS: command description | |
205 | msgid "Encrypt firmware data" | |
206 | msgstr "" | |
207 | ||
208 | #. TRANSLATORS: command description | |
209 | msgid "Decrypt firmware data" | |
210 | msgstr "" | |
211 | ||
212 | #. TRANSLATORS: command description | |
213 | msgid "Sets metadata on a firmware file" | |
214 | msgstr "" | |
215 | ||
216 | #. TRANSLATORS: DFU stands for device firmware update | |
217 | msgid "DFU Utility" | |
218 | msgstr "" | |
219 | ||
220 | #. TRANSLATORS: the user didn't read the man page | |
221 | msgid "Failed to parse arguments" | |
222 | msgstr "Zlyhalo analyzovanie parametrov" | |
223 | ||
224 | #. TRANSLATORS: turn on all debugging | |
225 | msgid "Show debugging information for all files" | |
226 | msgstr "Zobrazí ladiace informácie pre všetky súbory" | |
227 | ||
228 | #. TRANSLATORS: for the --verbose arg | |
229 | msgid "Debugging Options" | |
230 | msgstr "Voľby ladenia" | |
231 | ||
232 | #. TRANSLATORS: for the --verbose arg | |
233 | msgid "Show debugging options" | |
234 | msgstr "Zobrazí voľby ladenia" | |
235 | ||
236 | #. TRANSLATORS: exit after we've started up, used for user profiling | |
237 | msgid "Exit after a small delay" | |
238 | msgstr "Skončí po krátkom oneskorení" | |
239 | ||
240 | #. TRANSLATORS: exit straight away, used for automatic profiling | |
241 | msgid "Exit after the engine has loaded" | |
242 | msgstr "Skončí po načítaní jadra" | |
243 | ||
244 | #. TRANSLATORS: program name | |
245 | msgid "Firmware Update Daemon" | |
246 | msgstr "Démon aktualizácie firmvéru" | |
247 | ||
248 | #. TRANSLATORS: program summary | |
249 | msgid "Firmware Update D-Bus Service" | |
250 | msgstr "Služba zbernice D-Bus na aktualizovanie firmvéru" | |
89 | 251 | |
90 | 252 | #. TRANSLATORS: daemon is inactive |
91 | 253 | msgid "Idle" |
148 | 310 | msgid "Updating %s from %s to %s... " |
149 | 311 | msgstr "Aktualizuje sa %s z verzie %s na verziu %s... " |
150 | 312 | |
151 | msgid "OK" | |
152 | msgstr "OK" | |
153 | ||
154 | 313 | #. TRANSLATORS: first replacement is device name |
155 | 314 | #, c-format |
156 | 315 | msgid "%s has firmware updates:" |
157 | 316 | msgstr "Pre zariadenie %s sú dostupné aktualizácie firmvéru:" |
158 | 317 | |
318 | #. TRANSLATORS: a GUID for the hardware | |
319 | msgid "GUID" | |
320 | msgstr "" | |
321 | ||
159 | 322 | #. TRANSLATORS: section header for firmware version |
160 | 323 | msgid "Version" |
161 | 324 | msgstr "Verzia" |
240 | 403 | msgid "Firmware Utility" |
241 | 404 | msgstr "Nástroj pre firmvéry" |
242 | 405 | |
243 | #. TRANSLATORS: the user didn't read the man page | |
244 | msgid "Failed to parse arguments" | |
245 | msgstr "Zlyhalo analyzovanie parametrov" | |
246 | ||
247 | 406 | #. TRANSLATORS: the user is in a bad place |
248 | 407 | msgid "Failed to connect to D-Bus" |
249 | 408 | msgstr "Zlyhalo pripojenie k zbernici D-Bus" |
7 | 7 | msgstr "" |
8 | 8 | "Project-Id-Version: fwupd\n" |
9 | 9 | "Report-Msgid-Bugs-To: \n" |
10 | "POT-Creation-Date: 2015-10-28 09:58+0000\n" | |
11 | "PO-Revision-Date: 2015-10-15 21:00+0000\n" | |
12 | "Last-Translator: Марко М. Костић (Marko M. Kostić) <marko.m.kostic@gmail.com>\n" | |
10 | "POT-Creation-Date: 2015-12-07 12:22+0000\n" | |
11 | "PO-Revision-Date: 2015-12-07 12:22+0000\n" | |
12 | "Last-Translator: Richard Hughes <richard@hughsie.com>\n" | |
13 | 13 | "Language-Team: Serbian (http://www.transifex.com/hughsie/fwupd/language/sr/)\n" |
14 | 14 | "MIME-Version: 1.0\n" |
15 | 15 | "Content-Type: text/plain; charset=UTF-8\n" |
50 | 50 | "Authentication is required to downgrade the firmware on a removable device" |
51 | 51 | msgstr "Потребна је пријава за уназађивање фирмвера на преносивом уређају" |
52 | 52 | |
53 | #. TRANSLATORS: turn on all debugging | |
54 | msgid "Show debugging information for all files" | |
55 | msgstr "Прикажи податке о отклањању проблема за све датотеке" | |
56 | ||
57 | #. TRANSLATORS: for the --verbose arg | |
58 | msgid "Debugging Options" | |
59 | msgstr "Опције отклањања проблема" | |
60 | ||
61 | #. TRANSLATORS: for the --verbose arg | |
62 | msgid "Show debugging options" | |
63 | msgstr "Прикажи опције за отклањање проблема" | |
64 | ||
65 | #. TRANSLATORS: exit after we've started up, used for user profiling | |
66 | msgid "Exit after a small delay" | |
67 | msgstr "Изађи након малог застоја" | |
68 | ||
69 | #. TRANSLATORS: exit straight away, used for automatic profiling | |
70 | msgid "Exit after the engine has loaded" | |
71 | msgstr "Изађи након учитавања мотора" | |
72 | ||
73 | #. TRANSLATORS: program name | |
74 | msgid "Firmware Update Daemon" | |
75 | msgstr "Демон за ажурирање фирмвера" | |
76 | ||
77 | #. TRANSLATORS: program summary | |
78 | msgid "Firmware Update D-Bus Service" | |
79 | msgstr "Д-Бус услуга ажурирања фирмвера" | |
80 | ||
81 | 53 | #. TRANSLATORS: this is a command alias, e.g. 'get-devices' |
82 | 54 | #, c-format |
83 | 55 | msgid "Alias to %s" |
86 | 58 | #. TRANSLATORS: error message |
87 | 59 | msgid "Command not found" |
88 | 60 | msgstr "Наредба није пронађена" |
61 | ||
62 | #. TRANSLATORS: when an action has completed | |
63 | msgid "OK" | |
64 | msgstr "У реду" | |
65 | ||
66 | #. TRANSLATORS: when moving from runtime to DFU mode | |
67 | msgid "Detaching" | |
68 | msgstr "" | |
69 | ||
70 | #. TRANSLATORS: when moving from DFU to runtime mode | |
71 | msgid "Attaching" | |
72 | msgstr "" | |
73 | ||
74 | #. TRANSLATORS: when copying from host to device | |
75 | msgid "Downloading" | |
76 | msgstr "" | |
77 | ||
78 | #. TRANSLATORS: when copying from device to host | |
79 | msgid "Uploading" | |
80 | msgstr "" | |
81 | ||
82 | #. TRANSLATORS: this is when a device is hotplugged | |
83 | msgid "Added" | |
84 | msgstr "" | |
85 | ||
86 | #. TRANSLATORS: this is when a device is hotplugged | |
87 | msgid "Removed" | |
88 | msgstr "" | |
89 | ||
90 | #. TRANSLATORS: this is when a device is hotplugged | |
91 | msgid "Changed" | |
92 | msgstr "" | |
93 | ||
94 | #. TRANSLATORS: this is when a device ctrl+c's a watch | |
95 | msgid "Cancelled" | |
96 | msgstr "" | |
97 | ||
98 | #. TRANSLATORS: Appstream ID for the hardware type | |
99 | msgid "ID" | |
100 | msgstr "" | |
101 | ||
102 | #. TRANSLATORS: interface name, e.g. "Flash" | |
103 | msgid "Name" | |
104 | msgstr "" | |
105 | ||
106 | #. TRANSLATORS: this is the encryption method used when writing | |
107 | msgid "Cipher" | |
108 | msgstr "" | |
109 | ||
110 | #. TRANSLATORS: these are areas of memory on the chip | |
111 | msgid "Region" | |
112 | msgstr "" | |
113 | ||
114 | #. TRANSLATORS: detected a DFU device | |
115 | msgid "Found" | |
116 | msgstr "" | |
117 | ||
118 | #. TRANSLATORS: probably not run as root... | |
119 | #. TRANSLATORS: device has failed to report status | |
120 | #. TRANSLATORS: device status, e.g. "OK" | |
121 | msgid "Status" | |
122 | msgstr "" | |
123 | ||
124 | msgid "Unknown: permission denied" | |
125 | msgstr "" | |
126 | ||
127 | #. TRANSLATORS: device mode, e.g. runtime or DFU | |
128 | msgid "Mode" | |
129 | msgstr "" | |
130 | ||
131 | #. TRANSLATORS: device state, i.e. appIDLE | |
132 | msgid "State" | |
133 | msgstr "" | |
134 | ||
135 | #. TRANSLATORS: device quirks, i.e. things that | |
136 | #. * it does that we have to work around | |
137 | msgid "Quirks" | |
138 | msgstr "" | |
139 | ||
140 | #. TRANSLATORS: command description | |
141 | msgid "Convert firmware to DFU format" | |
142 | msgstr "" | |
143 | ||
144 | #. TRANSLATORS: command description | |
145 | msgid "Merge multiple firmware files into one" | |
146 | msgstr "" | |
147 | ||
148 | #. TRANSLATORS: command description | |
149 | msgid "Set vendor ID on firmware file" | |
150 | msgstr "" | |
151 | ||
152 | #. TRANSLATORS: command description | |
153 | msgid "Set product ID on firmware file" | |
154 | msgstr "" | |
155 | ||
156 | #. TRANSLATORS: command description | |
157 | msgid "Set release version on firmware file" | |
158 | msgstr "" | |
159 | ||
160 | #. TRANSLATORS: command description | |
161 | msgid "Set alternative number on firmware file" | |
162 | msgstr "" | |
163 | ||
164 | #. TRANSLATORS: command description | |
165 | msgid "Set alternative name on firmware file" | |
166 | msgstr "" | |
167 | ||
168 | #. TRANSLATORS: command description | |
169 | msgid "Attach DFU capable device back to runtime" | |
170 | msgstr "" | |
171 | ||
172 | #. TRANSLATORS: command description | |
173 | msgid "Read firmware from device into a file" | |
174 | msgstr "" | |
175 | ||
176 | #. TRANSLATORS: command description | |
177 | msgid "Read firmware from one partition into a file" | |
178 | msgstr "" | |
179 | ||
180 | #. TRANSLATORS: command description | |
181 | msgid "Write firmware from file into device" | |
182 | msgstr "" | |
183 | ||
184 | #. TRANSLATORS: command description | |
185 | msgid "Write firmware from file into one partition" | |
186 | msgstr "" | |
187 | ||
188 | #. TRANSLATORS: command description | |
189 | msgid "List currently attached DFU capable devices" | |
190 | msgstr "" | |
191 | ||
192 | #. TRANSLATORS: command description | |
193 | msgid "Detach currently attached DFU capable device" | |
194 | msgstr "" | |
195 | ||
196 | #. TRANSLATORS: command description | |
197 | msgid "Dump details about a firmware file" | |
198 | msgstr "" | |
199 | ||
200 | #. TRANSLATORS: command description | |
201 | msgid "Watch DFU devices being hotplugged" | |
202 | msgstr "" | |
203 | ||
204 | #. TRANSLATORS: command description | |
205 | msgid "Encrypt firmware data" | |
206 | msgstr "" | |
207 | ||
208 | #. TRANSLATORS: command description | |
209 | msgid "Decrypt firmware data" | |
210 | msgstr "" | |
211 | ||
212 | #. TRANSLATORS: command description | |
213 | msgid "Sets metadata on a firmware file" | |
214 | msgstr "" | |
215 | ||
216 | #. TRANSLATORS: DFU stands for device firmware update | |
217 | msgid "DFU Utility" | |
218 | msgstr "" | |
219 | ||
220 | #. TRANSLATORS: the user didn't read the man page | |
221 | msgid "Failed to parse arguments" | |
222 | msgstr "Не могу да обрадим аргументе" | |
223 | ||
224 | #. TRANSLATORS: turn on all debugging | |
225 | msgid "Show debugging information for all files" | |
226 | msgstr "Прикажи податке о отклањању проблема за све датотеке" | |
227 | ||
228 | #. TRANSLATORS: for the --verbose arg | |
229 | msgid "Debugging Options" | |
230 | msgstr "Опције отклањања проблема" | |
231 | ||
232 | #. TRANSLATORS: for the --verbose arg | |
233 | msgid "Show debugging options" | |
234 | msgstr "Прикажи опције за отклањање проблема" | |
235 | ||
236 | #. TRANSLATORS: exit after we've started up, used for user profiling | |
237 | msgid "Exit after a small delay" | |
238 | msgstr "Изађи након малог застоја" | |
239 | ||
240 | #. TRANSLATORS: exit straight away, used for automatic profiling | |
241 | msgid "Exit after the engine has loaded" | |
242 | msgstr "Изађи након учитавања мотора" | |
243 | ||
244 | #. TRANSLATORS: program name | |
245 | msgid "Firmware Update Daemon" | |
246 | msgstr "Демон за ажурирање фирмвера" | |
247 | ||
248 | #. TRANSLATORS: program summary | |
249 | msgid "Firmware Update D-Bus Service" | |
250 | msgstr "Д-Бус услуга ажурирања фирмвера" | |
89 | 251 | |
90 | 252 | #. TRANSLATORS: daemon is inactive |
91 | 253 | msgid "Idle" |
148 | 310 | msgid "Updating %s from %s to %s... " |
149 | 311 | msgstr "Ажурирам %s са %s на %s..." |
150 | 312 | |
151 | msgid "OK" | |
152 | msgstr "У реду" | |
153 | ||
154 | 313 | #. TRANSLATORS: first replacement is device name |
155 | 314 | #, c-format |
156 | 315 | msgid "%s has firmware updates:" |
157 | 316 | msgstr "%s има ажурирања за фирмвер:" |
158 | 317 | |
318 | #. TRANSLATORS: a GUID for the hardware | |
319 | msgid "GUID" | |
320 | msgstr "" | |
321 | ||
159 | 322 | #. TRANSLATORS: section header for firmware version |
160 | 323 | msgid "Version" |
161 | 324 | msgstr "Издање" |
240 | 403 | msgid "Firmware Utility" |
241 | 404 | msgstr "Алатка за фирмвер" |
242 | 405 | |
243 | #. TRANSLATORS: the user didn't read the man page | |
244 | msgid "Failed to parse arguments" | |
245 | msgstr "Не могу да обрадим аргументе" | |
246 | ||
247 | 406 | #. TRANSLATORS: the user is in a bad place |
248 | 407 | msgid "Failed to connect to D-Bus" |
249 | 408 | msgstr "Не могу да се повежем на Д-Бус" |
7 | 7 | msgstr "" |
8 | 8 | "Project-Id-Version: fwupd\n" |
9 | 9 | "Report-Msgid-Bugs-To: \n" |
10 | "POT-Creation-Date: 2015-09-10 09:28+0100\n" | |
11 | "PO-Revision-Date: 2015-09-10 08:28+0000\n" | |
10 | "POT-Creation-Date: 2015-12-07 12:22+0000\n" | |
11 | "PO-Revision-Date: 2015-12-07 12:22+0000\n" | |
12 | 12 | "Last-Translator: Richard Hughes <richard@hughsie.com>\n" |
13 | 13 | "Language-Team: Swedish (http://www.transifex.com/hughsie/fwupd/language/sv/)\n" |
14 | 14 | "MIME-Version: 1.0\n" |
50 | 50 | "Authentication is required to downgrade the firmware on a removable device" |
51 | 51 | msgstr "Autentisering krävs för att nedgradera den fasta programvaran för en flyttbar enhet" |
52 | 52 | |
53 | #. TRANSLATORS: turn on all debugging | |
54 | msgid "Show debugging information for all files" | |
55 | msgstr "Visa felsökningsinformation för alla filer" | |
56 | ||
57 | #. TRANSLATORS: for the --verbose arg | |
58 | msgid "Debugging Options" | |
59 | msgstr "Felsökningsalternativ" | |
60 | ||
61 | #. TRANSLATORS: for the --verbose arg | |
62 | msgid "Show debugging options" | |
63 | msgstr "Visa felsökningsalternativ" | |
64 | ||
65 | #. TRANSLATORS: exit after we've started up, used for user profiling | |
66 | msgid "Exit after a small delay" | |
67 | msgstr "Avsluta efter en kort fördröjning" | |
68 | ||
69 | #. TRANSLATORS: exit straight away, used for automatic profiling | |
70 | msgid "Exit after the engine has loaded" | |
71 | msgstr "Avsluta efter att motorn har lästs in" | |
72 | ||
73 | #. TRANSLATORS: program name | |
74 | msgid "Firmware Update Daemon" | |
75 | msgstr "" | |
76 | ||
77 | #. TRANSLATORS: program summary | |
78 | msgid "Firmware Update D-Bus Service" | |
79 | msgstr "Firmware Update D-Bus-tjänst" | |
80 | ||
81 | 53 | #. TRANSLATORS: this is a command alias, e.g. 'get-devices' |
82 | 54 | #, c-format |
83 | 55 | msgid "Alias to %s" |
86 | 58 | #. TRANSLATORS: error message |
87 | 59 | msgid "Command not found" |
88 | 60 | msgstr "" |
61 | ||
62 | #. TRANSLATORS: when an action has completed | |
63 | msgid "OK" | |
64 | msgstr "" | |
65 | ||
66 | #. TRANSLATORS: when moving from runtime to DFU mode | |
67 | msgid "Detaching" | |
68 | msgstr "" | |
69 | ||
70 | #. TRANSLATORS: when moving from DFU to runtime mode | |
71 | msgid "Attaching" | |
72 | msgstr "" | |
73 | ||
74 | #. TRANSLATORS: when copying from host to device | |
75 | msgid "Downloading" | |
76 | msgstr "" | |
77 | ||
78 | #. TRANSLATORS: when copying from device to host | |
79 | msgid "Uploading" | |
80 | msgstr "" | |
81 | ||
82 | #. TRANSLATORS: this is when a device is hotplugged | |
83 | msgid "Added" | |
84 | msgstr "" | |
85 | ||
86 | #. TRANSLATORS: this is when a device is hotplugged | |
87 | msgid "Removed" | |
88 | msgstr "" | |
89 | ||
90 | #. TRANSLATORS: this is when a device is hotplugged | |
91 | msgid "Changed" | |
92 | msgstr "" | |
93 | ||
94 | #. TRANSLATORS: this is when a device ctrl+c's a watch | |
95 | msgid "Cancelled" | |
96 | msgstr "" | |
97 | ||
98 | #. TRANSLATORS: Appstream ID for the hardware type | |
99 | msgid "ID" | |
100 | msgstr "" | |
101 | ||
102 | #. TRANSLATORS: interface name, e.g. "Flash" | |
103 | msgid "Name" | |
104 | msgstr "" | |
105 | ||
106 | #. TRANSLATORS: this is the encryption method used when writing | |
107 | msgid "Cipher" | |
108 | msgstr "" | |
109 | ||
110 | #. TRANSLATORS: these are areas of memory on the chip | |
111 | msgid "Region" | |
112 | msgstr "" | |
113 | ||
114 | #. TRANSLATORS: detected a DFU device | |
115 | msgid "Found" | |
116 | msgstr "" | |
117 | ||
118 | #. TRANSLATORS: probably not run as root... | |
119 | #. TRANSLATORS: device has failed to report status | |
120 | #. TRANSLATORS: device status, e.g. "OK" | |
121 | msgid "Status" | |
122 | msgstr "" | |
123 | ||
124 | msgid "Unknown: permission denied" | |
125 | msgstr "" | |
126 | ||
127 | #. TRANSLATORS: device mode, e.g. runtime or DFU | |
128 | msgid "Mode" | |
129 | msgstr "" | |
130 | ||
131 | #. TRANSLATORS: device state, i.e. appIDLE | |
132 | msgid "State" | |
133 | msgstr "" | |
134 | ||
135 | #. TRANSLATORS: device quirks, i.e. things that | |
136 | #. * it does that we have to work around | |
137 | msgid "Quirks" | |
138 | msgstr "" | |
139 | ||
140 | #. TRANSLATORS: command description | |
141 | msgid "Convert firmware to DFU format" | |
142 | msgstr "" | |
143 | ||
144 | #. TRANSLATORS: command description | |
145 | msgid "Merge multiple firmware files into one" | |
146 | msgstr "" | |
147 | ||
148 | #. TRANSLATORS: command description | |
149 | msgid "Set vendor ID on firmware file" | |
150 | msgstr "" | |
151 | ||
152 | #. TRANSLATORS: command description | |
153 | msgid "Set product ID on firmware file" | |
154 | msgstr "" | |
155 | ||
156 | #. TRANSLATORS: command description | |
157 | msgid "Set release version on firmware file" | |
158 | msgstr "" | |
159 | ||
160 | #. TRANSLATORS: command description | |
161 | msgid "Set alternative number on firmware file" | |
162 | msgstr "" | |
163 | ||
164 | #. TRANSLATORS: command description | |
165 | msgid "Set alternative name on firmware file" | |
166 | msgstr "" | |
167 | ||
168 | #. TRANSLATORS: command description | |
169 | msgid "Attach DFU capable device back to runtime" | |
170 | msgstr "" | |
171 | ||
172 | #. TRANSLATORS: command description | |
173 | msgid "Read firmware from device into a file" | |
174 | msgstr "" | |
175 | ||
176 | #. TRANSLATORS: command description | |
177 | msgid "Read firmware from one partition into a file" | |
178 | msgstr "" | |
179 | ||
180 | #. TRANSLATORS: command description | |
181 | msgid "Write firmware from file into device" | |
182 | msgstr "" | |
183 | ||
184 | #. TRANSLATORS: command description | |
185 | msgid "Write firmware from file into one partition" | |
186 | msgstr "" | |
187 | ||
188 | #. TRANSLATORS: command description | |
189 | msgid "List currently attached DFU capable devices" | |
190 | msgstr "" | |
191 | ||
192 | #. TRANSLATORS: command description | |
193 | msgid "Detach currently attached DFU capable device" | |
194 | msgstr "" | |
195 | ||
196 | #. TRANSLATORS: command description | |
197 | msgid "Dump details about a firmware file" | |
198 | msgstr "" | |
199 | ||
200 | #. TRANSLATORS: command description | |
201 | msgid "Watch DFU devices being hotplugged" | |
202 | msgstr "" | |
203 | ||
204 | #. TRANSLATORS: command description | |
205 | msgid "Encrypt firmware data" | |
206 | msgstr "" | |
207 | ||
208 | #. TRANSLATORS: command description | |
209 | msgid "Decrypt firmware data" | |
210 | msgstr "" | |
211 | ||
212 | #. TRANSLATORS: command description | |
213 | msgid "Sets metadata on a firmware file" | |
214 | msgstr "" | |
215 | ||
216 | #. TRANSLATORS: DFU stands for device firmware update | |
217 | msgid "DFU Utility" | |
218 | msgstr "" | |
219 | ||
220 | #. TRANSLATORS: the user didn't read the man page | |
221 | msgid "Failed to parse arguments" | |
222 | msgstr "Misslyckades med att tolka argument" | |
223 | ||
224 | #. TRANSLATORS: turn on all debugging | |
225 | msgid "Show debugging information for all files" | |
226 | msgstr "Visa felsökningsinformation för alla filer" | |
227 | ||
228 | #. TRANSLATORS: for the --verbose arg | |
229 | msgid "Debugging Options" | |
230 | msgstr "Felsökningsalternativ" | |
231 | ||
232 | #. TRANSLATORS: for the --verbose arg | |
233 | msgid "Show debugging options" | |
234 | msgstr "Visa felsökningsalternativ" | |
235 | ||
236 | #. TRANSLATORS: exit after we've started up, used for user profiling | |
237 | msgid "Exit after a small delay" | |
238 | msgstr "Avsluta efter en kort fördröjning" | |
239 | ||
240 | #. TRANSLATORS: exit straight away, used for automatic profiling | |
241 | msgid "Exit after the engine has loaded" | |
242 | msgstr "Avsluta efter att motorn har lästs in" | |
243 | ||
244 | #. TRANSLATORS: program name | |
245 | msgid "Firmware Update Daemon" | |
246 | msgstr "" | |
247 | ||
248 | #. TRANSLATORS: program summary | |
249 | msgid "Firmware Update D-Bus Service" | |
250 | msgstr "Firmware Update D-Bus-tjänst" | |
89 | 251 | |
90 | 252 | #. TRANSLATORS: daemon is inactive |
91 | 253 | msgid "Idle" |
148 | 310 | msgid "Updating %s from %s to %s... " |
149 | 311 | msgstr "Uppdaterar %s från %s till %s..." |
150 | 312 | |
151 | msgid "OK" | |
152 | msgstr "" | |
153 | ||
154 | 313 | #. TRANSLATORS: first replacement is device name |
155 | 314 | #, c-format |
156 | 315 | msgid "%s has firmware updates:" |
157 | 316 | msgstr "%s har uppdateringar för fast programvara:" |
158 | 317 | |
318 | #. TRANSLATORS: a GUID for the hardware | |
319 | msgid "GUID" | |
320 | msgstr "" | |
321 | ||
159 | 322 | #. TRANSLATORS: section header for firmware version |
160 | 323 | msgid "Version" |
161 | 324 | msgstr "Version" |
240 | 403 | msgid "Firmware Utility" |
241 | 404 | msgstr "" |
242 | 405 | |
243 | #. TRANSLATORS: the user didn't read the man page | |
244 | msgid "Failed to parse arguments" | |
245 | msgstr "Misslyckades med att tolka argument" | |
246 | ||
247 | 406 | #. TRANSLATORS: the user is in a bad place |
248 | 407 | msgid "Failed to connect to D-Bus" |
249 | 408 | msgstr "Misslyckades med att ansluta till D-Bus" |
7 | 7 | msgstr "" |
8 | 8 | "Project-Id-Version: fwupd\n" |
9 | 9 | "Report-Msgid-Bugs-To: \n" |
10 | "POT-Creation-Date: 2015-09-10 09:28+0100\n" | |
11 | "PO-Revision-Date: 2015-09-10 08:28+0000\n" | |
12 | "Last-Translator: Richard Hughes <richard@hughsie.com>\n" | |
10 | "POT-Creation-Date: 2015-12-07 12:22+0000\n" | |
11 | "PO-Revision-Date: 2015-12-07 12:48+0000\n" | |
12 | "Last-Translator: Yuri Chornoivan <yurchor@ukr.net>\n" | |
13 | 13 | "Language-Team: Ukrainian (http://www.transifex.com/hughsie/fwupd/language/uk/)\n" |
14 | 14 | "MIME-Version: 1.0\n" |
15 | 15 | "Content-Type: text/plain; charset=UTF-8\n" |
50 | 50 | "Authentication is required to downgrade the firmware on a removable device" |
51 | 51 | msgstr "Для встановлення застарілої версії мікропрограми на портативний пристрій слід пройти розпізнавання" |
52 | 52 | |
53 | #. TRANSLATORS: turn on all debugging | |
54 | msgid "Show debugging information for all files" | |
55 | msgstr "Показувати діагностичні дані для всіх файлів" | |
56 | ||
57 | #. TRANSLATORS: for the --verbose arg | |
58 | msgid "Debugging Options" | |
59 | msgstr "Параметри діагностики" | |
60 | ||
61 | #. TRANSLATORS: for the --verbose arg | |
62 | msgid "Show debugging options" | |
63 | msgstr "Показувати параметри діагностики" | |
64 | ||
65 | #. TRANSLATORS: exit after we've started up, used for user profiling | |
66 | msgid "Exit after a small delay" | |
67 | msgstr "Завершити роботу з невеличкою затримкою" | |
68 | ||
69 | #. TRANSLATORS: exit straight away, used for automatic profiling | |
70 | msgid "Exit after the engine has loaded" | |
71 | msgstr "Завершити роботу після завантаження рушія" | |
72 | ||
73 | #. TRANSLATORS: program name | |
74 | msgid "Firmware Update Daemon" | |
75 | msgstr "Служба оновлення мікропрограми" | |
76 | ||
77 | #. TRANSLATORS: program summary | |
78 | msgid "Firmware Update D-Bus Service" | |
79 | msgstr "Служба D-Bus оновлення мікропрограми" | |
80 | ||
81 | 53 | #. TRANSLATORS: this is a command alias, e.g. 'get-devices' |
82 | 54 | #, c-format |
83 | 55 | msgid "Alias to %s" |
86 | 58 | #. TRANSLATORS: error message |
87 | 59 | msgid "Command not found" |
88 | 60 | msgstr "Такої команди не знайдено" |
61 | ||
62 | #. TRANSLATORS: when an action has completed | |
63 | msgid "OK" | |
64 | msgstr "Гаразд" | |
65 | ||
66 | #. TRANSLATORS: when moving from runtime to DFU mode | |
67 | msgid "Detaching" | |
68 | msgstr "Від’єднуємо" | |
69 | ||
70 | #. TRANSLATORS: when moving from DFU to runtime mode | |
71 | msgid "Attaching" | |
72 | msgstr "З’єднуємо" | |
73 | ||
74 | #. TRANSLATORS: when copying from host to device | |
75 | msgid "Downloading" | |
76 | msgstr "Отримуємо" | |
77 | ||
78 | #. TRANSLATORS: when copying from device to host | |
79 | msgid "Uploading" | |
80 | msgstr "Вивантажуємо" | |
81 | ||
82 | #. TRANSLATORS: this is when a device is hotplugged | |
83 | msgid "Added" | |
84 | msgstr "Додано" | |
85 | ||
86 | #. TRANSLATORS: this is when a device is hotplugged | |
87 | msgid "Removed" | |
88 | msgstr "Вилучено" | |
89 | ||
90 | #. TRANSLATORS: this is when a device is hotplugged | |
91 | msgid "Changed" | |
92 | msgstr "Змінено" | |
93 | ||
94 | #. TRANSLATORS: this is when a device ctrl+c's a watch | |
95 | msgid "Cancelled" | |
96 | msgstr "Скасовано" | |
97 | ||
98 | #. TRANSLATORS: Appstream ID for the hardware type | |
99 | msgid "ID" | |
100 | msgstr "Ід." | |
101 | ||
102 | #. TRANSLATORS: interface name, e.g. "Flash" | |
103 | msgid "Name" | |
104 | msgstr "Назва" | |
105 | ||
106 | #. TRANSLATORS: this is the encryption method used when writing | |
107 | msgid "Cipher" | |
108 | msgstr "Шифр" | |
109 | ||
110 | #. TRANSLATORS: these are areas of memory on the chip | |
111 | msgid "Region" | |
112 | msgstr "Регіон" | |
113 | ||
114 | #. TRANSLATORS: detected a DFU device | |
115 | msgid "Found" | |
116 | msgstr "Знайдено" | |
117 | ||
118 | #. TRANSLATORS: probably not run as root... | |
119 | #. TRANSLATORS: device has failed to report status | |
120 | #. TRANSLATORS: device status, e.g. "OK" | |
121 | msgid "Status" | |
122 | msgstr "Стан" | |
123 | ||
124 | msgid "Unknown: permission denied" | |
125 | msgstr "Невідомий: доступ заборонено" | |
126 | ||
127 | #. TRANSLATORS: device mode, e.g. runtime or DFU | |
128 | msgid "Mode" | |
129 | msgstr "Режим" | |
130 | ||
131 | #. TRANSLATORS: device state, i.e. appIDLE | |
132 | msgid "State" | |
133 | msgstr "Стан" | |
134 | ||
135 | #. TRANSLATORS: device quirks, i.e. things that | |
136 | #. * it does that we have to work around | |
137 | msgid "Quirks" | |
138 | msgstr "Негаразди" | |
139 | ||
140 | #. TRANSLATORS: command description | |
141 | msgid "Convert firmware to DFU format" | |
142 | msgstr "Перетворити мікропрограму у формат DFU" | |
143 | ||
144 | #. TRANSLATORS: command description | |
145 | msgid "Merge multiple firmware files into one" | |
146 | msgstr "Об’єднати декілька файлів мікропрограм у один" | |
147 | ||
148 | #. TRANSLATORS: command description | |
149 | msgid "Set vendor ID on firmware file" | |
150 | msgstr "Встановити ідентифікатор виробника для файла мікропрограми" | |
151 | ||
152 | #. TRANSLATORS: command description | |
153 | msgid "Set product ID on firmware file" | |
154 | msgstr "Встановити ідентифікатор продукту для файла мікропрограми" | |
155 | ||
156 | #. TRANSLATORS: command description | |
157 | msgid "Set release version on firmware file" | |
158 | msgstr "Встановити версію випуску для файла мікропрограми" | |
159 | ||
160 | #. TRANSLATORS: command description | |
161 | msgid "Set alternative number on firmware file" | |
162 | msgstr "Встановити альтернативний номер для файла мікропрограми" | |
163 | ||
164 | #. TRANSLATORS: command description | |
165 | msgid "Set alternative name on firmware file" | |
166 | msgstr "Встановити альтернативну назву для файла мікропрограми" | |
167 | ||
168 | #. TRANSLATORS: command description | |
169 | msgid "Attach DFU capable device back to runtime" | |
170 | msgstr "Повернути пристрій із можливостями DFU до використання" | |
171 | ||
172 | #. TRANSLATORS: command description | |
173 | msgid "Read firmware from device into a file" | |
174 | msgstr "Прочитати мікропрограму з пристрою до файла" | |
175 | ||
176 | #. TRANSLATORS: command description | |
177 | msgid "Read firmware from one partition into a file" | |
178 | msgstr "Прочитати мікропрограму з одного розділу до файла" | |
179 | ||
180 | #. TRANSLATORS: command description | |
181 | msgid "Write firmware from file into device" | |
182 | msgstr "Записати мікропрограму з файла на пристрій" | |
183 | ||
184 | #. TRANSLATORS: command description | |
185 | msgid "Write firmware from file into one partition" | |
186 | msgstr "Записати мікропрограму з файла на один розділ" | |
187 | ||
188 | #. TRANSLATORS: command description | |
189 | msgid "List currently attached DFU capable devices" | |
190 | msgstr "Вивести поточний список долучених пристроїв із можливостями DFU" | |
191 | ||
192 | #. TRANSLATORS: command description | |
193 | msgid "Detach currently attached DFU capable device" | |
194 | msgstr "Від’єднати поточний з’єднаний пристрій із можливостями DFU" | |
195 | ||
196 | #. TRANSLATORS: command description | |
197 | msgid "Dump details about a firmware file" | |
198 | msgstr "Створити дамп даних щодо файла мікропрограми" | |
199 | ||
200 | #. TRANSLATORS: command description | |
201 | msgid "Watch DFU devices being hotplugged" | |
202 | msgstr "Спостерігати за пристроями DFU, які з’єднують із комп’ютером" | |
203 | ||
204 | #. TRANSLATORS: command description | |
205 | msgid "Encrypt firmware data" | |
206 | msgstr "Зашифрувати дані мікропрограми" | |
207 | ||
208 | #. TRANSLATORS: command description | |
209 | msgid "Decrypt firmware data" | |
210 | msgstr "Розшифрувати дані мікропрограми" | |
211 | ||
212 | #. TRANSLATORS: command description | |
213 | msgid "Sets metadata on a firmware file" | |
214 | msgstr "Встановлює метадані щодо файла мікпропрограми" | |
215 | ||
216 | #. TRANSLATORS: DFU stands for device firmware update | |
217 | msgid "DFU Utility" | |
218 | msgstr "Засіб роботи з DFU" | |
219 | ||
220 | #. TRANSLATORS: the user didn't read the man page | |
221 | msgid "Failed to parse arguments" | |
222 | msgstr "Не вдалося обробити аргументи" | |
223 | ||
224 | #. TRANSLATORS: turn on all debugging | |
225 | msgid "Show debugging information for all files" | |
226 | msgstr "Показувати діагностичні дані для всіх файлів" | |
227 | ||
228 | #. TRANSLATORS: for the --verbose arg | |
229 | msgid "Debugging Options" | |
230 | msgstr "Параметри діагностики" | |
231 | ||
232 | #. TRANSLATORS: for the --verbose arg | |
233 | msgid "Show debugging options" | |
234 | msgstr "Показувати параметри діагностики" | |
235 | ||
236 | #. TRANSLATORS: exit after we've started up, used for user profiling | |
237 | msgid "Exit after a small delay" | |
238 | msgstr "Завершити роботу з невеличкою затримкою" | |
239 | ||
240 | #. TRANSLATORS: exit straight away, used for automatic profiling | |
241 | msgid "Exit after the engine has loaded" | |
242 | msgstr "Завершити роботу після завантаження рушія" | |
243 | ||
244 | #. TRANSLATORS: program name | |
245 | msgid "Firmware Update Daemon" | |
246 | msgstr "Служба оновлення мікропрограми" | |
247 | ||
248 | #. TRANSLATORS: program summary | |
249 | msgid "Firmware Update D-Bus Service" | |
250 | msgstr "Служба D-Bus оновлення мікропрограми" | |
89 | 251 | |
90 | 252 | #. TRANSLATORS: daemon is inactive |
91 | 253 | msgid "Idle" |
148 | 310 | msgid "Updating %s from %s to %s... " |
149 | 311 | msgstr "Оновлюємо %s з %s до %s... " |
150 | 312 | |
151 | msgid "OK" | |
152 | msgstr "Гаразд" | |
153 | ||
154 | 313 | #. TRANSLATORS: first replacement is device name |
155 | 314 | #, c-format |
156 | 315 | msgid "%s has firmware updates:" |
157 | 316 | msgstr "%s має такі оновлення мікропрограми:" |
158 | 317 | |
318 | #. TRANSLATORS: a GUID for the hardware | |
319 | msgid "GUID" | |
320 | msgstr "GUID" | |
321 | ||
159 | 322 | #. TRANSLATORS: section header for firmware version |
160 | 323 | msgid "Version" |
161 | 324 | msgstr "Версія" |
240 | 403 | msgid "Firmware Utility" |
241 | 404 | msgstr "Засіб роботи з мікропрограмами" |
242 | 405 | |
243 | #. TRANSLATORS: the user didn't read the man page | |
244 | msgid "Failed to parse arguments" | |
245 | msgstr "Не вдалося обробити аргументи" | |
246 | ||
247 | 406 | #. TRANSLATORS: the user is in a bad place |
248 | 407 | msgid "Failed to connect to D-Bus" |
249 | 408 | msgstr "Не вдалося з’єднатися з D-Bus" |
32 | 32 | FWUPD_LIBS = \ |
33 | 33 | $(top_builddir)/libfwupd/libfwupd.la |
34 | 34 | |
35 | DFU_LIBS = \ | |
36 | $(top_builddir)/libdfu/libdfu.la | |
37 | ||
35 | 38 | bin_PROGRAMS = fwupdmgr |
36 | 39 | |
37 | 40 | fwupdmgr_SOURCES = \ |
44 | 47 | fu-util.c |
45 | 48 | |
46 | 49 | fwupdmgr_LDADD = \ |
50 | $(LIBM) \ | |
47 | 51 | $(FWUPD_LIBS) \ |
48 | 52 | $(APPSTREAM_GLIB_LIBS) \ |
49 | 53 | $(SQLITE_LIBS) \ |
91 | 95 | fu-pending.h \ |
92 | 96 | fu-provider.c \ |
93 | 97 | fu-provider.h \ |
98 | fu-provider-dfu.c \ | |
99 | fu-provider-dfu.h \ | |
94 | 100 | fu-provider-rpi.c \ |
95 | 101 | fu-provider-rpi.h \ |
96 | 102 | fu-provider-udev.c \ |
117 | 123 | |
118 | 124 | fwupd_LDADD = \ |
119 | 125 | $(FWUPD_LIBS) \ |
126 | $(DFU_LIBS) \ | |
120 | 127 | $(APPSTREAM_GLIB_LIBS) \ |
121 | 128 | $(COLORHUG_LIBS) \ |
122 | 129 | $(GUSB_LIBS) \ |
164 | 171 | fu-self-test.c |
165 | 172 | |
166 | 173 | fu_self_test_LDADD = \ |
174 | $(LIBM) \ | |
167 | 175 | $(FWUPD_LIBS) \ |
168 | 176 | $(APPSTREAM_GLIB_LIBS) \ |
169 | 177 | $(SQLITE_LIBS) \ |
56 | 56 | #define FU_DEVICE_KEY_UPDATE_HASH "UpdateHash" /* s */ |
57 | 57 | #define FU_DEVICE_KEY_UPDATE_URI "UpdateUri" /* s */ |
58 | 58 | #define FU_DEVICE_KEY_UPDATE_DESCRIPTION "UpdateDescription" /* s */ |
59 | #define FU_DEVICE_KEY_APPSTREAM_ID "AppstreamId" /* s */ | |
59 | 60 | |
60 | 61 | FuDevice *fu_device_new (void); |
61 | 62 |
37 | 37 | #include "fu-keyring.h" |
38 | 38 | #include "fu-pending.h" |
39 | 39 | #include "fu-provider.h" |
40 | #include "fu-provider-dfu.h" | |
40 | 41 | #include "fu-provider-rpi.h" |
41 | 42 | #include "fu-provider-udev.h" |
42 | 43 | #include "fu-provider-usb.h" |
341 | 342 | } |
342 | 343 | |
343 | 344 | /* run the correct provider that added this */ |
344 | return fu_provider_update (item->provider, | |
345 | item->device, | |
346 | helper->blob_cab, | |
347 | helper->blob_fw, | |
348 | helper->flags, | |
349 | error); | |
345 | if (!fu_provider_update (item->provider, | |
346 | item->device, | |
347 | helper->blob_cab, | |
348 | helper->blob_fw, | |
349 | helper->flags, | |
350 | error)) | |
351 | return FALSE; | |
352 | ||
353 | /* make the UI update */ | |
354 | fu_main_emit_changed (helper->priv); | |
355 | return TRUE; | |
350 | 356 | } |
351 | 357 | |
352 | 358 | /** |
874 | 880 | } |
875 | 881 | |
876 | 882 | /* add application metadata */ |
883 | fu_device_set_metadata (item->device, | |
884 | FU_DEVICE_KEY_APPSTREAM_ID, | |
885 | as_app_get_id (app)); | |
877 | 886 | tmp = as_app_get_developer_name (app, NULL); |
878 | 887 | if (tmp != NULL) { |
879 | 888 | fu_device_set_metadata (item->device, |
1634 | 1643 | |
1635 | 1644 | /* remove any fake device */ |
1636 | 1645 | item = fu_main_get_item_by_id (priv, fu_device_get_id (device)); |
1637 | if (item != NULL) | |
1638 | g_ptr_array_remove (priv->devices, item); | |
1646 | if (item != NULL) { | |
1647 | g_debug ("already added %s by %s, ignoring same device from %s", | |
1648 | fu_device_get_id (item->device), | |
1649 | fu_device_get_metadata (item->device, FU_DEVICE_KEY_PROVIDER), | |
1650 | fu_provider_get_name (provider)); | |
1651 | return; | |
1652 | } | |
1639 | 1653 | |
1640 | 1654 | /* create new device */ |
1641 | 1655 | item = g_new0 (FuDeviceItem, 1); |
1658 | 1672 | |
1659 | 1673 | item = fu_main_get_item_by_id (priv, fu_device_get_id (device)); |
1660 | 1674 | if (item == NULL) { |
1661 | g_warning ("can't remove device %s", fu_device_get_id (device)); | |
1675 | g_debug ("no device to remove %s", fu_device_get_id (device)); | |
1662 | 1676 | return; |
1663 | 1677 | } |
1678 | ||
1679 | /* check this came from the same provider */ | |
1680 | if (g_strcmp0 (fu_provider_get_name (provider), | |
1681 | fu_provider_get_name (item->provider)) != 0) { | |
1682 | g_debug ("ignoring duplicate removal from %s", | |
1683 | fu_provider_get_name (provider)); | |
1684 | return; | |
1685 | } | |
1686 | ||
1664 | 1687 | g_ptr_array_remove (priv->devices, item); |
1665 | 1688 | fu_main_emit_changed (priv); |
1666 | 1689 | } |
1780 | 1803 | priv->providers = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref); |
1781 | 1804 | if (g_key_file_get_boolean (config, "fwupd", "EnableOptionROM", NULL)) |
1782 | 1805 | fu_main_add_provider (priv, fu_provider_udev_new ()); |
1783 | fu_main_add_provider (priv, fu_provider_usb_new ()); | |
1806 | fu_main_add_provider (priv, fu_provider_dfu_new ()); | |
1784 | 1807 | fu_main_add_provider (priv, fu_provider_rpi_new ()); |
1785 | 1808 | #ifdef HAVE_COLORHUG |
1786 | 1809 | fu_main_add_provider (priv, fu_provider_chug_new ()); |
1788 | 1811 | #ifdef HAVE_UEFI |
1789 | 1812 | fu_main_add_provider (priv, fu_provider_uefi_new ()); |
1790 | 1813 | #endif |
1814 | ||
1815 | /* last as least priority */ | |
1816 | fu_main_add_provider (priv, fu_provider_usb_new ()); | |
1791 | 1817 | |
1792 | 1818 | /* load introspection from file */ |
1793 | 1819 | priv->introspection_daemon = fu_main_load_introspection (FWUPD_DBUS_INTERFACE ".xml", |
142 | 142 | } |
143 | 143 | |
144 | 144 | /** |
145 | * fu_provider_chug_get_id: | |
146 | **/ | |
147 | static gchar * | |
148 | fu_provider_chug_get_id (GUsbDevice *device) | |
149 | { | |
150 | /* this identifies the *port* the device is plugged into and | |
151 | * the kind of device */ | |
152 | return g_strdup_printf ("CHug-%s-%s", | |
153 | g_usb_device_get_platform_id (device), | |
154 | ch_device_get_guid (device)); | |
155 | } | |
156 | ||
157 | /** | |
158 | 145 | * fu_provider_chug_get_firmware_version: |
159 | 146 | **/ |
160 | 147 | static void |
164 | 151 | guint16 major; |
165 | 152 | guint16 micro; |
166 | 153 | guint16 minor; |
167 | #if G_USB_CHECK_VERSION(0,2,5) | |
168 | 154 | guint8 idx; |
169 | #endif | |
170 | 155 | g_autoptr(GError) error = NULL; |
171 | 156 | g_autofree gchar *version = NULL; |
172 | 157 | |
173 | 158 | /* try to get the version without claiming interface */ |
174 | #if G_USB_CHECK_VERSION(0,2,5) | |
175 | 159 | if (!g_usb_device_open (item->usb_device, &error)) { |
176 | 160 | g_debug ("Failed to open, polling: %s", error->message); |
177 | 161 | return; |
193 | 177 | } |
194 | 178 | } |
195 | 179 | g_usb_device_close (item->usb_device, NULL); |
196 | #endif | |
197 | 180 | |
198 | 181 | /* attempt to open the device and get the serial number */ |
199 | 182 | item->persist_after_unplug = TRUE; |
251 | 234 | return FALSE; |
252 | 235 | } |
253 | 236 | |
254 | #if !CD_CHECK_VERSION(1,2,12) | |
255 | /* recompile colord */ | |
256 | g_set_error_literal (error, | |
257 | FWUPD_ERROR, | |
258 | FWUPD_ERROR_NOT_SUPPORTED, | |
259 | "Cannot read firmware: colord too old"); | |
260 | return FALSE; | |
261 | #endif | |
262 | ||
263 | 237 | /* open */ |
264 | 238 | if (!fu_provider_chug_open (item, error)) |
265 | 239 | return FALSE; |
266 | 240 | |
267 | 241 | /* get the firmware from the device */ |
268 | 242 | g_debug ("ColorHug: Verifying firmware"); |
269 | #if CD_CHECK_VERSION(1,2,12) | |
270 | 243 | ch_device_queue_read_firmware (priv->device_queue, item->usb_device, |
271 | 244 | &data, &len); |
272 | #endif | |
273 | 245 | fu_provider_set_status (provider, FWUPD_STATUS_DEVICE_VERIFY); |
274 | 246 | if (!ch_device_queue_process (priv->device_queue, |
275 | 247 | CH_DEVICE_QUEUE_PROCESS_FLAGS_NONE, |
488 | 460 | return TRUE; |
489 | 461 | } |
490 | 462 | |
491 | #if !CD_CHECK_VERSION(1,2,10) | |
492 | #define CH_DEVICE_GUID_COLORHUG "40338ceb-b966-4eae-adae-9c32edfcc484" | |
493 | #define CH_DEVICE_GUID_COLORHUG2 "2082b5e0-7a64-478a-b1b2-e3404fab6dad" | |
494 | #define CH_DEVICE_GUID_COLORHUG_ALS "84f40464-9272-4ef7-9399-cd95f12da696" | |
495 | #define CH_DEVICE_GUID_COLORHUG_PLUS "6d6f05a9-3ecb-43a2-bcbb-3844f1825366" | |
496 | ||
497 | static const gchar * | |
498 | ch_device_get_guid (GUsbDevice *device) | |
499 | { | |
500 | ChDeviceMode mode = ch_device_get_mode (device); | |
501 | if (mode == CH_DEVICE_MODE_LEGACY || | |
502 | mode == CH_DEVICE_MODE_FIRMWARE || | |
503 | mode == CH_DEVICE_MODE_BOOTLOADER) | |
504 | return CH_DEVICE_GUID_COLORHUG; | |
505 | if (mode == CH_DEVICE_MODE_FIRMWARE2 || | |
506 | mode == CH_DEVICE_MODE_BOOTLOADER2) | |
507 | return CH_DEVICE_GUID_COLORHUG2; | |
508 | if (mode == CH_DEVICE_MODE_FIRMWARE_PLUS || | |
509 | mode == CH_DEVICE_MODE_BOOTLOADER_PLUS) | |
510 | return CH_DEVICE_GUID_COLORHUG_PLUS; | |
511 | if (mode == CH_DEVICE_MODE_FIRMWARE_ALS || | |
512 | mode == CH_DEVICE_MODE_BOOTLOADER_ALS) | |
513 | return CH_DEVICE_GUID_COLORHUG_ALS; | |
514 | return NULL; | |
515 | } | |
516 | #endif | |
517 | ||
518 | 463 | /** |
519 | 464 | * fu_provider_chug_device_added_cb: |
520 | 465 | **/ |
526 | 471 | FuProviderChugPrivate *priv = GET_PRIVATE (provider_chug); |
527 | 472 | FuProviderChugItem *item; |
528 | 473 | ChDeviceMode mode; |
529 | g_autofree gchar *id = NULL; | |
474 | const gchar *platform_id = NULL; | |
530 | 475 | |
531 | 476 | /* ignore */ |
532 | 477 | mode = ch_device_get_mode (device); |
533 | 478 | if (mode == CH_DEVICE_MODE_UNKNOWN) |
534 | 479 | return; |
535 | 480 | |
481 | /* this is using DFU now */ | |
482 | if (mode == CH_DEVICE_MODE_BOOTLOADER_PLUS || | |
483 | mode == CH_DEVICE_MODE_FIRMWARE_PLUS) | |
484 | return; | |
485 | ||
536 | 486 | /* is already in database */ |
537 | id = fu_provider_chug_get_id (device); | |
538 | item = g_hash_table_lookup (priv->devices, id); | |
487 | platform_id = g_usb_device_get_platform_id (device); | |
488 | item = g_hash_table_lookup (priv->devices, platform_id); | |
539 | 489 | if (item == NULL) { |
540 | 490 | item = g_new0 (FuProviderChugItem, 1); |
541 | 491 | item->loop = g_main_loop_new (NULL, FALSE); |
542 | 492 | item->provider_chug = g_object_ref (provider_chug); |
543 | 493 | item->usb_device = g_object_ref (device); |
544 | 494 | item->device = fu_device_new (); |
545 | fu_device_set_id (item->device, id); | |
495 | fu_device_set_id (item->device, platform_id); | |
546 | 496 | fu_device_set_guid (item->device, ch_device_get_guid (device)); |
547 | 497 | fu_device_add_flag (item->device, FU_DEVICE_FLAG_ALLOW_OFFLINE); |
548 | 498 | fu_device_add_flag (item->device, FU_DEVICE_FLAG_ALLOW_ONLINE); |
557 | 507 | |
558 | 508 | /* insert to hash */ |
559 | 509 | g_hash_table_insert (priv->devices, |
560 | g_strdup (id), item); | |
510 | g_strdup (platform_id), item); | |
561 | 511 | } else { |
562 | 512 | /* update the device */ |
563 | 513 | g_object_unref (item->usb_device); |
617 | 567 | { |
618 | 568 | FuProviderChugPrivate *priv = GET_PRIVATE (provider_chug); |
619 | 569 | FuProviderChugItem *item; |
620 | g_autofree gchar *id = NULL; | |
570 | const gchar *platform_id = NULL; | |
621 | 571 | |
622 | 572 | /* already in database */ |
623 | id = fu_provider_chug_get_id (device); | |
624 | item = g_hash_table_lookup (priv->devices, id); | |
573 | platform_id = g_usb_device_get_platform_id (device); | |
574 | item = g_hash_table_lookup (priv->devices, platform_id); | |
625 | 575 | if (item == NULL) |
626 | 576 | return; |
627 | 577 | |
636 | 586 | * rescan each time so we don't get confused when different |
637 | 587 | * kinds of ColorHug device are plugged in... */ |
638 | 588 | if (!item->persist_after_unplug) |
639 | g_hash_table_remove (priv->devices, id); | |
589 | g_hash_table_remove (priv->devices, platform_id); | |
640 | 590 | } |
641 | 591 | |
642 | 592 | /** |
0 | /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- | |
1 | * | |
2 | * Copyright (C) 2015 Richard Hughes <richard@hughsie.com> | |
3 | * | |
4 | * Licensed under the GNU General Public License Version 2 | |
5 | * | |
6 | * This program is free software; you can redistribute it and/or modify | |
7 | * it under the terms of the GNU General Public License as published by | |
8 | * the Free Software Foundation; either version 2 of the License, or | |
9 | * (at your option) any later version. | |
10 | * | |
11 | * This program is distributed in the hope that it will be useful, | |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
14 | * GNU General Public License for more details. | |
15 | * | |
16 | * You should have received a copy of the GNU General Public License | |
17 | * along with this program; if not, write to the Free Software | |
18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | |
19 | */ | |
20 | ||
21 | #include "config.h" | |
22 | ||
23 | #include <appstream-glib.h> | |
24 | #include <fwupd.h> | |
25 | #include <glib-object.h> | |
26 | #include <libdfu/dfu.h> | |
27 | ||
28 | #include "fu-device.h" | |
29 | #include "fu-provider-dfu.h" | |
30 | ||
31 | static void fu_provider_dfu_finalize (GObject *object); | |
32 | ||
33 | /** | |
34 | * FuProviderDfuPrivate: | |
35 | **/ | |
36 | typedef struct { | |
37 | DfuContext *context; | |
38 | GHashTable *devices; /* platform_id:DfuDevice */ | |
39 | } FuProviderDfuPrivate; | |
40 | ||
41 | G_DEFINE_TYPE_WITH_PRIVATE (FuProviderDfu, fu_provider_dfu, FU_TYPE_PROVIDER) | |
42 | #define GET_PRIVATE(o) (fu_provider_dfu_get_instance_private (o)) | |
43 | ||
44 | /** | |
45 | * fu_provider_dfu_get_name: | |
46 | **/ | |
47 | static const gchar * | |
48 | fu_provider_dfu_get_name (FuProvider *provider) | |
49 | { | |
50 | return "DFU"; | |
51 | } | |
52 | ||
53 | /** | |
54 | * fu_provider_dfu_device_update: | |
55 | **/ | |
56 | static void | |
57 | fu_provider_dfu_device_update (FuProviderDfu *provider_dfu, | |
58 | FuDevice *dev, | |
59 | DfuDevice *device) | |
60 | { | |
61 | const gchar *platform_id; | |
62 | guint16 release; | |
63 | g_autofree gchar *guid = NULL; | |
64 | g_autofree gchar *version = NULL; | |
65 | g_autofree gchar *vid_pid = NULL; | |
66 | ||
67 | /* check mode */ | |
68 | platform_id = dfu_device_get_platform_id (device); | |
69 | if (dfu_device_get_runtime_vid (device) == 0xffff) { | |
70 | g_debug ("Ignoring DFU device not in runtime: %s", platform_id); | |
71 | return; | |
72 | } | |
73 | ||
74 | /* check capabilities */ | |
75 | if (dfu_device_can_download (device)) { | |
76 | fu_device_add_flag (dev, FU_DEVICE_FLAG_ALLOW_ONLINE); | |
77 | fu_device_add_flag (dev, FU_DEVICE_FLAG_ALLOW_OFFLINE); | |
78 | } | |
79 | ||
80 | /* get version number, falling back to the DFU device release */ | |
81 | release = dfu_device_get_runtime_release (device); | |
82 | if (release != 0xffff) { | |
83 | version = as_utils_version_from_uint16 (release, | |
84 | AS_VERSION_PARSE_FLAG_NONE); | |
85 | fu_device_set_metadata (dev, FU_DEVICE_KEY_VERSION, version); | |
86 | } | |
87 | ||
88 | vid_pid = g_strdup_printf ("USB\\VID_%04X&PID_%04X", | |
89 | dfu_device_get_runtime_vid (device), | |
90 | dfu_device_get_runtime_pid (device)); | |
91 | guid = as_utils_guid_from_string (vid_pid); | |
92 | g_debug ("using %s for %s", guid, vid_pid); | |
93 | fu_device_set_guid (dev, guid); | |
94 | } | |
95 | ||
96 | /** | |
97 | * fu_provider_dfu_device_changed_cb: | |
98 | **/ | |
99 | static void | |
100 | fu_provider_dfu_device_changed_cb (DfuContext *ctx, | |
101 | DfuDevice *device, | |
102 | FuProviderDfu *provider_dfu) | |
103 | { | |
104 | FuProviderDfuPrivate *priv = GET_PRIVATE (provider_dfu); | |
105 | FuDevice *dev; | |
106 | const gchar *platform_id; | |
107 | ||
108 | /* convert DfuDevice to FuDevice */ | |
109 | platform_id = dfu_device_get_platform_id (device); | |
110 | dev = g_hash_table_lookup (priv->devices, platform_id); | |
111 | if (dev == NULL) { | |
112 | g_warning ("cannot find device %s", platform_id); | |
113 | return; | |
114 | } | |
115 | fu_provider_dfu_device_update (provider_dfu, dev, device); | |
116 | } | |
117 | ||
118 | /** | |
119 | * fu_provider_dfu_device_added_cb: | |
120 | **/ | |
121 | static void | |
122 | fu_provider_dfu_device_added_cb (DfuContext *ctx, | |
123 | DfuDevice *device, | |
124 | FuProviderDfu *provider_dfu) | |
125 | { | |
126 | FuProviderDfuPrivate *priv = GET_PRIVATE (provider_dfu); | |
127 | const gchar *platform_id; | |
128 | const gchar *display_name; | |
129 | g_autofree gchar *id = NULL; | |
130 | g_autoptr(AsProfile) profile = as_profile_new (); | |
131 | g_autoptr(AsProfileTask) ptask = NULL; | |
132 | g_autoptr(FuDevice) dev = NULL; | |
133 | g_autoptr(GError) error = NULL; | |
134 | ||
135 | platform_id = dfu_device_get_platform_id (device); | |
136 | ptask = as_profile_start (profile, "FuProviderDfu:added{%s} [%04x:%04x]", | |
137 | platform_id, | |
138 | dfu_device_get_runtime_vid (device), | |
139 | dfu_device_get_runtime_pid (device)); | |
140 | ||
141 | /* create new device */ | |
142 | dev = fu_device_new (); | |
143 | fu_device_set_id (dev, platform_id); | |
144 | fu_provider_dfu_device_update (provider_dfu, dev, device); | |
145 | ||
146 | /* open device to get display name */ | |
147 | if (!dfu_device_open (device, DFU_DEVICE_OPEN_FLAG_NONE, NULL, &error)) { | |
148 | g_warning ("Failed to open DFU device: %s", error->message); | |
149 | return; | |
150 | } | |
151 | display_name = dfu_device_get_display_name (device); | |
152 | if (display_name != NULL) | |
153 | fu_device_set_display_name (dev, display_name); | |
154 | ||
155 | /* we're done here */ | |
156 | if (!dfu_device_close (device, &error)) | |
157 | g_debug ("Failed to close %s: %s", platform_id, error->message); | |
158 | ||
159 | /* attempt to add */ | |
160 | fu_provider_device_add (FU_PROVIDER (provider_dfu), dev); | |
161 | g_hash_table_insert (priv->devices, | |
162 | g_strdup (platform_id), | |
163 | g_object_ref (dev)); | |
164 | } | |
165 | ||
166 | /** | |
167 | * fu_provider_dfu_device_removed_cb: | |
168 | **/ | |
169 | static void | |
170 | fu_provider_dfu_device_removed_cb (DfuContext *ctx, | |
171 | DfuDevice *device, | |
172 | FuProviderDfu *provider_dfu) | |
173 | { | |
174 | FuProviderDfuPrivate *priv = GET_PRIVATE (provider_dfu); | |
175 | FuDevice *dev; | |
176 | const gchar *platform_id; | |
177 | ||
178 | /* convert DfuDevice to FuDevice */ | |
179 | platform_id = dfu_device_get_platform_id (device); | |
180 | dev = g_hash_table_lookup (priv->devices, platform_id); | |
181 | if (dev == NULL) { | |
182 | g_warning ("cannot find device %s", platform_id); | |
183 | return; | |
184 | } | |
185 | ||
186 | fu_provider_device_remove (FU_PROVIDER (provider_dfu), dev); | |
187 | } | |
188 | ||
189 | /** | |
190 | * fu_provider_dfu_coldplug: | |
191 | **/ | |
192 | static gboolean | |
193 | fu_provider_dfu_coldplug (FuProvider *provider, GError **error) | |
194 | { | |
195 | FuProviderDfu *provider_dfu = FU_PROVIDER_DFU (provider); | |
196 | FuProviderDfuPrivate *priv = GET_PRIVATE (provider_dfu); | |
197 | dfu_context_enumerate (priv->context, NULL); | |
198 | return TRUE; | |
199 | } | |
200 | ||
201 | /** | |
202 | * fu_provider_dfu_state_changed_cb: | |
203 | **/ | |
204 | static void | |
205 | fu_provider_dfu_state_changed_cb (DfuDevice *device, | |
206 | DfuState state, | |
207 | FuProvider *provider) | |
208 | { | |
209 | switch (state) { | |
210 | case DFU_STATE_DFU_UPLOAD_IDLE: | |
211 | fu_provider_set_status (provider, FWUPD_STATUS_DEVICE_VERIFY); | |
212 | break; | |
213 | case DFU_STATE_DFU_DNLOAD_IDLE: | |
214 | fu_provider_set_status (provider, FWUPD_STATUS_DEVICE_WRITE); | |
215 | break; | |
216 | default: | |
217 | break; | |
218 | } | |
219 | } | |
220 | ||
221 | /** | |
222 | * fu_provider_dfu_update: | |
223 | * | |
224 | * This updates using DFU. | |
225 | **/ | |
226 | static gboolean | |
227 | fu_provider_dfu_update (FuProvider *provider, | |
228 | FuDevice *dev, | |
229 | GBytes *blob_fw, | |
230 | FuProviderFlags flags, | |
231 | GError **error) | |
232 | { | |
233 | FuProviderDfu *provider_dfu = FU_PROVIDER_DFU (provider); | |
234 | FuProviderDfuPrivate *priv = GET_PRIVATE (provider_dfu); | |
235 | DfuDevice *device; | |
236 | const gchar *platform_id; | |
237 | g_autoptr(DfuDevice) dfu_device = NULL; | |
238 | g_autoptr(DfuFirmware) dfu_firmware = NULL; | |
239 | g_autoptr(GError) error_local = NULL; | |
240 | ||
241 | /* get device */ | |
242 | platform_id = fu_device_get_id (dev); | |
243 | device = dfu_context_get_device_by_platform_id (priv->context, | |
244 | platform_id, | |
245 | &error_local); | |
246 | if (device == NULL) { | |
247 | g_set_error (error, | |
248 | FWUPD_ERROR, | |
249 | FWUPD_ERROR_INTERNAL, | |
250 | "cannot find device %s: %s", | |
251 | platform_id, error_local->message); | |
252 | return FALSE; | |
253 | } | |
254 | ||
255 | /* open it */ | |
256 | if (!dfu_device_open (device, DFU_DEVICE_OPEN_FLAG_NONE, | |
257 | NULL, &error_local)) { | |
258 | g_set_error (error, | |
259 | FWUPD_ERROR, | |
260 | FWUPD_ERROR_INTERNAL, | |
261 | "failed to open DFU device %s: %s", | |
262 | platform_id, error_local->message); | |
263 | return FALSE; | |
264 | } | |
265 | g_signal_connect (device, "state-changed", | |
266 | G_CALLBACK (fu_provider_dfu_state_changed_cb), provider); | |
267 | ||
268 | /* hit hardware */ | |
269 | dfu_firmware = dfu_firmware_new (); | |
270 | if (!dfu_firmware_parse_data (dfu_firmware, blob_fw, | |
271 | DFU_FIRMWARE_PARSE_FLAG_NONE, error)) | |
272 | return FALSE; | |
273 | if (!dfu_device_download (device, dfu_firmware, | |
274 | DFU_TARGET_TRANSFER_FLAG_DETACH | | |
275 | DFU_TARGET_TRANSFER_FLAG_VERIFY | | |
276 | DFU_TARGET_TRANSFER_FLAG_WAIT_RUNTIME, | |
277 | NULL, | |
278 | error)) | |
279 | return FALSE; | |
280 | ||
281 | /* we're done */ | |
282 | if (!dfu_device_close (device, &error_local)) { | |
283 | g_set_error_literal (error, | |
284 | FWUPD_ERROR, | |
285 | FWUPD_ERROR_INTERNAL, | |
286 | error_local->message); | |
287 | return FALSE; | |
288 | } | |
289 | fu_provider_set_status (provider, FWUPD_STATUS_IDLE); | |
290 | return TRUE; | |
291 | } | |
292 | ||
293 | /** | |
294 | * fu_provider_dfu_verify: | |
295 | **/ | |
296 | static gboolean | |
297 | fu_provider_dfu_verify (FuProvider *provider, | |
298 | FuDevice *dev, | |
299 | FuProviderVerifyFlags flags, | |
300 | GError **error) | |
301 | { | |
302 | FuProviderDfu *provider_dfu = FU_PROVIDER_DFU (provider); | |
303 | FuProviderDfuPrivate *priv = GET_PRIVATE (provider_dfu); | |
304 | GBytes *blob_fw; | |
305 | DfuDevice *device; | |
306 | const gchar *platform_id; | |
307 | g_autofree gchar *hash = NULL; | |
308 | g_autoptr(DfuDevice) dfu_device = NULL; | |
309 | g_autoptr(DfuFirmware) dfu_firmware = NULL; | |
310 | g_autoptr(GError) error_local = NULL; | |
311 | ||
312 | /* get device */ | |
313 | platform_id = fu_device_get_id (dev); | |
314 | device = dfu_context_get_device_by_platform_id (priv->context, | |
315 | platform_id, | |
316 | &error_local); | |
317 | if (device == NULL) { | |
318 | g_set_error (error, | |
319 | FWUPD_ERROR, | |
320 | FWUPD_ERROR_INTERNAL, | |
321 | "cannot find device %s: %s", | |
322 | platform_id, error_local->message); | |
323 | return FALSE; | |
324 | } | |
325 | ||
326 | /* open it */ | |
327 | if (!dfu_device_open (device, DFU_DEVICE_OPEN_FLAG_NONE, | |
328 | NULL, &error_local)) { | |
329 | g_set_error (error, | |
330 | FWUPD_ERROR, | |
331 | FWUPD_ERROR_INTERNAL, | |
332 | "failed to open DFU device %s: %s", | |
333 | platform_id, error_local->message); | |
334 | return FALSE; | |
335 | } | |
336 | g_signal_connect (device, "state-changed", | |
337 | G_CALLBACK (fu_provider_dfu_state_changed_cb), provider); | |
338 | ||
339 | /* get data from hardware */ | |
340 | g_debug ("uploading from device->host"); | |
341 | dfu_firmware = dfu_device_upload (device, | |
342 | DFU_TARGET_TRANSFER_FLAG_DETACH | | |
343 | DFU_TARGET_TRANSFER_FLAG_WAIT_RUNTIME, | |
344 | NULL, | |
345 | error); | |
346 | if (dfu_firmware == NULL) | |
347 | return FALSE; | |
348 | ||
349 | /* we're done */ | |
350 | if (!dfu_device_close (device, &error_local)) { | |
351 | g_set_error_literal (error, | |
352 | FWUPD_ERROR, | |
353 | FWUPD_ERROR_INTERNAL, | |
354 | error_local->message); | |
355 | return FALSE; | |
356 | } | |
357 | ||
358 | /* get the SHA1 hash */ | |
359 | blob_fw = dfu_firmware_write_data (dfu_firmware, error); | |
360 | if (blob_fw == NULL) | |
361 | return FALSE; | |
362 | hash = g_compute_checksum_for_bytes (G_CHECKSUM_SHA1, blob_fw); | |
363 | fu_device_set_metadata (dev, FU_DEVICE_KEY_FIRMWARE_HASH, hash); | |
364 | fu_provider_set_status (provider, FWUPD_STATUS_IDLE); | |
365 | return TRUE; | |
366 | } | |
367 | ||
368 | /** | |
369 | * fu_provider_dfu_class_init: | |
370 | **/ | |
371 | static void | |
372 | fu_provider_dfu_class_init (FuProviderDfuClass *klass) | |
373 | { | |
374 | FuProviderClass *provider_class = FU_PROVIDER_CLASS (klass); | |
375 | GObjectClass *object_class = G_OBJECT_CLASS (klass); | |
376 | ||
377 | provider_class->get_name = fu_provider_dfu_get_name; | |
378 | provider_class->coldplug = fu_provider_dfu_coldplug; | |
379 | provider_class->update_online = fu_provider_dfu_update; | |
380 | provider_class->verify = fu_provider_dfu_verify; | |
381 | object_class->finalize = fu_provider_dfu_finalize; | |
382 | } | |
383 | ||
384 | /** | |
385 | * fu_provider_dfu_init: | |
386 | **/ | |
387 | static void | |
388 | fu_provider_dfu_init (FuProviderDfu *provider_dfu) | |
389 | { | |
390 | FuProviderDfuPrivate *priv = GET_PRIVATE (provider_dfu); | |
391 | priv->devices = g_hash_table_new_full (g_str_hash, g_str_equal, | |
392 | g_free, (GDestroyNotify) g_object_unref); | |
393 | priv->context = dfu_context_new (); | |
394 | g_signal_connect (priv->context, "device-added", | |
395 | G_CALLBACK (fu_provider_dfu_device_added_cb), | |
396 | provider_dfu); | |
397 | g_signal_connect (priv->context, "device-removed", | |
398 | G_CALLBACK (fu_provider_dfu_device_removed_cb), | |
399 | provider_dfu); | |
400 | g_signal_connect (priv->context, "device-changed", | |
401 | G_CALLBACK (fu_provider_dfu_device_changed_cb), | |
402 | provider_dfu); | |
403 | } | |
404 | ||
405 | /** | |
406 | * fu_provider_dfu_finalize: | |
407 | **/ | |
408 | static void | |
409 | fu_provider_dfu_finalize (GObject *object) | |
410 | { | |
411 | FuProviderDfu *provider_dfu = FU_PROVIDER_DFU (object); | |
412 | FuProviderDfuPrivate *priv = GET_PRIVATE (provider_dfu); | |
413 | ||
414 | g_hash_table_unref (priv->devices); | |
415 | g_object_unref (priv->context); | |
416 | ||
417 | G_OBJECT_CLASS (fu_provider_dfu_parent_class)->finalize (object); | |
418 | } | |
419 | ||
420 | /** | |
421 | * fu_provider_dfu_new: | |
422 | **/ | |
423 | FuProvider * | |
424 | fu_provider_dfu_new (void) | |
425 | { | |
426 | FuProviderDfu *provider; | |
427 | provider = g_object_new (FU_TYPE_PROVIDER_DFU, NULL); | |
428 | return FU_PROVIDER (provider); | |
429 | } |
0 | /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- | |
1 | * | |
2 | * Copyright (C) 2015 Richard Hughes <richard@hughsie.com> | |
3 | * | |
4 | * Licensed under the GNU General Public License Version 2 | |
5 | * | |
6 | * This program is free software; you can redistribute it and/or modify | |
7 | * it under the terms of the GNU General Public License as published by | |
8 | * the Free Software Foundation; either version 2 of the License, or | |
9 | * (at your option) any later version. | |
10 | * | |
11 | * This program is distributed in the hope that it will be useful, | |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
14 | * GNU General Public License for more details. | |
15 | * | |
16 | * You should have received a copy of the GNU General Public License | |
17 | * along with this program; if not, write to the Free Software | |
18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | |
19 | */ | |
20 | ||
21 | #ifndef __FU_PROVIDER_DFU_H | |
22 | #define __FU_PROVIDER_DFU_H | |
23 | ||
24 | #include <glib-object.h> | |
25 | ||
26 | #include "fu-device.h" | |
27 | #include "fu-provider.h" | |
28 | ||
29 | G_BEGIN_DECLS | |
30 | ||
31 | #define FU_TYPE_PROVIDER_DFU (fu_provider_dfu_get_type ()) | |
32 | G_DECLARE_DERIVABLE_TYPE (FuProviderDfu, fu_provider_dfu, FU, PROVIDER_DFU, FuProvider) | |
33 | ||
34 | struct _FuProviderDfuClass | |
35 | { | |
36 | FuProviderClass parent_class; | |
37 | }; | |
38 | ||
39 | FuProvider *fu_provider_dfu_new (void); | |
40 | ||
41 | G_END_DECLS | |
42 | ||
43 | #endif /* __FU_PROVIDER_DFU_H */ |
292 | 292 | continue; |
293 | 293 | } |
294 | 294 | fwup_get_fw_version(re, &version_raw); |
295 | #if AS_CHECK_VERSION(0,5,2) | |
296 | 295 | version = as_utils_version_from_uint32 (version_raw, |
297 | 296 | AS_VERSION_PARSE_FLAG_USE_TRIPLET); |
298 | #else | |
299 | version = g_strdup_printf ("%" G_GUINT32_FORMAT, version_raw); | |
300 | #endif | |
301 | 297 | id = g_strdup_printf ("UEFI-%s-dev%" G_GUINT64_FORMAT, |
302 | 298 | guid, hardware_instance); |
303 | 299 | |
307 | 303 | fu_device_set_metadata (dev, FU_DEVICE_KEY_VERSION, version); |
308 | 304 | fwup_get_lowest_supported_fw_version (re, &version_raw); |
309 | 305 | if (version_raw != 0) { |
310 | #if AS_CHECK_VERSION(0,5,2) | |
311 | 306 | version_lowest = as_utils_version_from_uint32 (version_raw, |
312 | 307 | AS_VERSION_PARSE_FLAG_USE_TRIPLET); |
313 | #else | |
314 | version_lowest = g_strdup_printf ("%" G_GUINT32_FORMAT, | |
315 | version_raw); | |
316 | #endif | |
317 | 308 | fu_device_set_metadata (dev, FU_DEVICE_KEY_VERSION_LOWEST, |
318 | 309 | version_lowest); |
319 | 310 | } |
36 | 36 | typedef struct { |
37 | 37 | GHashTable *devices; |
38 | 38 | GUsbContext *usb_ctx; |
39 | gboolean done_enumerate; | |
39 | 40 | } FuProviderUsbPrivate; |
40 | 41 | |
41 | 42 | G_DEFINE_TYPE_WITH_PRIVATE (FuProviderUsb, fu_provider_usb, FU_TYPE_PROVIDER) |
51 | 52 | } |
52 | 53 | |
53 | 54 | /** |
54 | * fu_provider_usb_get_id: | |
55 | **/ | |
56 | static gchar * | |
57 | fu_provider_usb_get_id (GUsbDevice *device) | |
58 | { | |
59 | /* this identifies the *port* the device is plugged into */ | |
60 | return g_strdup_printf ("ro-%s", g_usb_device_get_platform_id (device)); | |
61 | } | |
62 | ||
63 | /** | |
64 | * fu_provider_usb_device_add: | |
65 | * | |
66 | * Important, the device must already be open! | |
67 | **/ | |
68 | static void | |
69 | fu_provider_usb_device_add (FuProviderUsb *provider_usb, const gchar *id, GUsbDevice *device) | |
70 | { | |
71 | FuProviderUsbPrivate *priv = GET_PRIVATE (provider_usb); | |
72 | FuDevice *dev; | |
55 | * fu_provider_usb_device_added: | |
56 | **/ | |
57 | static void | |
58 | fu_provider_usb_device_added (FuProviderUsb *provider_usb, GUsbDevice *device) | |
59 | { | |
60 | FuProviderUsbPrivate *priv = GET_PRIVATE (provider_usb); | |
61 | const gchar *platform_id = NULL; | |
73 | 62 | guint8 idx = 0x00; |
74 | 63 | g_autofree gchar *guid = NULL; |
75 | 64 | g_autofree gchar *product = NULL; |
76 | 65 | g_autofree gchar *version = NULL; |
77 | 66 | g_autoptr(AsProfile) profile = as_profile_new (); |
78 | 67 | g_autoptr(AsProfileTask) ptask = NULL; |
68 | g_autoptr(FuDevice) dev = NULL; | |
69 | g_autoptr(GError) error = NULL; | |
70 | ||
71 | /* ignore hubs */ | |
72 | if (g_usb_device_get_device_class (device) == G_USB_DEVICE_CLASS_HUB) | |
73 | return; | |
74 | ptask = as_profile_start (profile, "FuProviderUsb:added{%04x:%04x}", | |
75 | g_usb_device_get_vid (device), | |
76 | g_usb_device_get_pid (device)); | |
77 | ||
78 | /* is already in database */ | |
79 | platform_id = g_usb_device_get_platform_id (device); | |
80 | dev = g_hash_table_lookup (priv->devices, platform_id); | |
81 | if (dev != NULL) { | |
82 | g_debug ("ignoring duplicate %s", platform_id); | |
83 | return; | |
84 | } | |
85 | ||
86 | /* try to get the version without claiming interface */ | |
87 | if (!g_usb_device_open (device, &error)) { | |
88 | g_debug ("Failed to open: %s", error->message); | |
89 | return; | |
90 | } | |
91 | ||
92 | /* insert to hash if valid */ | |
93 | dev = fu_device_new (); | |
94 | fu_device_set_id (dev, platform_id); | |
79 | 95 | |
80 | 96 | /* get product */ |
81 | 97 | idx = g_usb_device_get_product_index (device); |
85 | 101 | product = g_usb_device_get_string_descriptor (device, idx, NULL); |
86 | 102 | } |
87 | 103 | if (product == NULL) { |
88 | g_debug ("ignoring %s as no product string descriptor", id); | |
89 | return; | |
90 | } | |
91 | ||
92 | ptask = as_profile_start_literal (profile, "FuProviderUsb:get-custom-index"); | |
93 | #if G_USB_CHECK_VERSION(0,2,5) | |
104 | g_debug ("no product string descriptor"); | |
105 | return; | |
106 | } | |
107 | fu_device_set_display_name (dev, product); | |
108 | ||
109 | /* get version number, falling back to the USB device release */ | |
94 | 110 | idx = g_usb_device_get_custom_index (device, |
95 | 111 | G_USB_DEVICE_CLASS_VENDOR_SPECIFIC, |
96 | 112 | 'F', 'W', NULL); |
97 | #endif | |
98 | 113 | if (idx != 0x00) |
99 | 114 | version = g_usb_device_get_string_descriptor (device, idx, NULL); |
100 | 115 | if (version == NULL) { |
101 | g_debug ("ignoring %s [%s] as no version", id, product); | |
102 | return; | |
103 | } | |
104 | #if G_USB_CHECK_VERSION(0,2,5) | |
116 | guint16 release; | |
117 | release = g_usb_device_get_release (device); | |
118 | version = as_utils_version_from_uint16 (release, | |
119 | AS_VERSION_PARSE_FLAG_NONE); | |
120 | } | |
121 | fu_device_set_metadata (dev, FU_DEVICE_KEY_VERSION, version); | |
122 | ||
123 | /* get GUID, falling back to the USB VID:PID hash */ | |
105 | 124 | idx = g_usb_device_get_custom_index (device, |
106 | 125 | G_USB_DEVICE_CLASS_VENDOR_SPECIFIC, |
107 | 126 | 'G', 'U', NULL); |
108 | #endif | |
109 | 127 | if (idx != 0x00) |
110 | 128 | guid = g_usb_device_get_string_descriptor (device, idx, NULL); |
111 | 129 | if (guid == NULL) { |
112 | g_debug ("ignoring %s [%s] as no GUID", id, product); | |
113 | return; | |
114 | } | |
130 | g_autofree gchar *vid_pid = NULL; | |
131 | vid_pid = g_strdup_printf ("USB\\VID_%04X&PID_%04X", | |
132 | g_usb_device_get_vid (device), | |
133 | g_usb_device_get_pid (device)); | |
134 | guid = as_utils_guid_from_string (vid_pid); | |
135 | } | |
136 | fu_device_set_guid (dev, guid); | |
137 | ||
138 | /* we're done here */ | |
139 | if (!g_usb_device_close (device, &error)) | |
140 | g_debug ("Failed to close: %s", error->message); | |
115 | 141 | |
116 | 142 | /* insert to hash */ |
117 | dev = fu_device_new (); | |
118 | fu_device_set_id (dev, id); | |
119 | fu_device_set_guid (dev, guid); | |
120 | fu_device_set_display_name (dev, product); | |
121 | fu_device_set_metadata (dev, FU_DEVICE_KEY_VERSION, version); | |
122 | g_hash_table_insert (priv->devices, | |
123 | g_strdup (id), dev); | |
124 | 143 | fu_provider_device_add (FU_PROVIDER (provider_usb), dev); |
144 | g_hash_table_insert (priv->devices, g_strdup (platform_id), g_object_ref (dev)); | |
145 | } | |
146 | ||
147 | typedef struct { | |
148 | FuProviderUsb *provider_usb; | |
149 | GUsbDevice *device; | |
150 | } FuProviderUsbHelper; | |
151 | ||
152 | /** | |
153 | * fu_provider_usb_device_added_delay_cb: | |
154 | **/ | |
155 | static gboolean | |
156 | fu_provider_usb_device_added_delay_cb (gpointer user_data) | |
157 | { | |
158 | FuProviderUsbHelper *helper = (FuProviderUsbHelper *) user_data; | |
159 | fu_provider_usb_device_added (helper->provider_usb, helper->device); | |
160 | g_object_unref (helper->provider_usb); | |
161 | g_object_unref (helper->device); | |
162 | g_free (helper); | |
163 | return FALSE; | |
125 | 164 | } |
126 | 165 | |
127 | 166 | /** |
133 | 172 | FuProviderUsb *provider_usb) |
134 | 173 | { |
135 | 174 | FuProviderUsbPrivate *priv = GET_PRIVATE (provider_usb); |
136 | FuDevice *dev; | |
137 | g_autoptr(GError) error = NULL; | |
138 | g_autofree gchar *id = NULL; | |
139 | g_autoptr(AsProfile) profile = as_profile_new (); | |
140 | g_autoptr(AsProfileTask) ptask = NULL; | |
141 | ||
142 | /* ignore hubs */ | |
143 | #if G_USB_CHECK_VERSION(0,2,5) | |
144 | if (g_usb_device_get_device_class (device) == G_USB_DEVICE_CLASS_HUB) | |
145 | return; | |
146 | #endif | |
147 | ptask = as_profile_start (profile, "FuProviderUsb:added{%04x:%04x}", | |
148 | g_usb_device_get_vid (device), | |
149 | g_usb_device_get_pid (device)); | |
150 | ||
151 | /* handled by another provider */ | |
152 | id = fu_provider_usb_get_id (device); | |
153 | if (g_usb_device_get_vid (device) == 0x273f) { | |
154 | g_debug ("handling %s in another provider", id); | |
155 | return; | |
156 | } | |
157 | ||
158 | /* is already in database */ | |
159 | dev = g_hash_table_lookup (priv->devices, id); | |
160 | if (dev != NULL) { | |
161 | g_debug ("ignoring duplicate %s", id); | |
162 | return; | |
163 | } | |
164 | ||
165 | /* try to get the version without claiming interface */ | |
166 | if (!g_usb_device_open (device, &error)) { | |
167 | g_debug ("Failed to open: %s", error->message); | |
168 | return; | |
169 | } | |
170 | ||
171 | /* try to add the device */ | |
172 | fu_provider_usb_device_add (provider_usb, id, device); | |
173 | ||
174 | /* we're done here */ | |
175 | if (!g_usb_device_close (device, &error)) | |
176 | g_debug ("Failed to close: %s", error->message); | |
175 | ||
176 | /* use a small delay for hotplugging so that other, better, providers | |
177 | * can claim this interface and add the FuDevice */ | |
178 | if (priv->done_enumerate) { | |
179 | FuProviderUsbHelper *helper; | |
180 | g_debug ("waiting a small time for other providers"); | |
181 | helper = g_new0 (FuProviderUsbHelper, 1); | |
182 | helper->provider_usb = g_object_ref (provider_usb); | |
183 | helper->device = g_object_ref (device); | |
184 | g_timeout_add (500, fu_provider_usb_device_added_delay_cb, helper); | |
185 | return; | |
186 | } | |
187 | fu_provider_usb_device_added (provider_usb, device); | |
177 | 188 | } |
178 | 189 | |
179 | 190 | /** |
186 | 197 | { |
187 | 198 | FuProviderUsbPrivate *priv = GET_PRIVATE (provider_usb); |
188 | 199 | FuDevice *dev; |
189 | g_autofree gchar *id = NULL; | |
200 | const gchar *platform_id = NULL; | |
190 | 201 | |
191 | 202 | /* already in database */ |
192 | id = fu_provider_usb_get_id (device); | |
193 | dev = g_hash_table_lookup (priv->devices, id); | |
203 | platform_id = g_usb_device_get_platform_id (device); | |
204 | dev = g_hash_table_lookup (priv->devices, platform_id); | |
194 | 205 | if (dev == NULL) |
195 | 206 | return; |
207 | ||
196 | 208 | fu_provider_device_remove (FU_PROVIDER (provider_usb), dev); |
209 | g_hash_table_remove (priv->devices, platform_id); | |
197 | 210 | } |
198 | 211 | |
199 | 212 | /** |
204 | 217 | { |
205 | 218 | FuProviderUsb *provider_usb = FU_PROVIDER_USB (provider); |
206 | 219 | FuProviderUsbPrivate *priv = GET_PRIVATE (provider_usb); |
207 | ||
208 | 220 | g_usb_context_enumerate (priv->usb_ctx); |
221 | priv->done_enumerate = TRUE; | |
209 | 222 | return TRUE; |
210 | 223 | } |
211 | 224 |
370 | 370 | void |
371 | 371 | fu_provider_device_add (FuProvider *provider, FuDevice *device) |
372 | 372 | { |
373 | g_debug ("emit added: %s", fu_device_get_id (device)); | |
373 | g_debug ("emit added from %s: %s", | |
374 | fu_provider_get_name (provider), | |
375 | fu_device_get_id (device)); | |
374 | 376 | fu_device_set_metadata (device, FU_DEVICE_KEY_PROVIDER, |
375 | 377 | fu_provider_get_name (provider)); |
376 | 378 | g_signal_emit (provider, signals[SIGNAL_DEVICE_ADDED], 0, device); |
382 | 384 | void |
383 | 385 | fu_provider_device_remove (FuProvider *provider, FuDevice *device) |
384 | 386 | { |
385 | g_debug ("emit removed: %s", fu_device_get_id (device)); | |
387 | g_debug ("emit removed from %s: %s", | |
388 | fu_provider_get_name (provider), | |
389 | fu_device_get_id (device)); | |
386 | 390 | g_signal_emit (provider, signals[SIGNAL_DEVICE_REMOVED], 0, device); |
387 | 391 | } |
388 | 392 |
65 | 65 | FuRomKind kind; |
66 | 66 | gchar *version; |
67 | 67 | gchar *guid; |
68 | guint16 vendor; | |
69 | guint16 model; | |
68 | guint16 vendor_id; | |
69 | guint16 device_id; | |
70 | 70 | GPtrArray *hdrs; /* of FuRomPciHeader */ |
71 | 71 | } FuRomPrivate; |
72 | 72 | |
160 | 160 | str = g_string_new (""); |
161 | 161 | if (sz <= 0) |
162 | 162 | return NULL; |
163 | for (i = 0; i < sz; i++) | |
163 | for (i = 0; i < (guint) sz; i++) | |
164 | 164 | g_string_append_printf (str, "%02x ", buffer[i]); |
165 | 165 | g_string_append (str, " "); |
166 | for (i = 0; i < sz; i++) { | |
166 | for (i = 0; i < (guint) sz; i++) { | |
167 | 167 | gchar tmp = '?'; |
168 | 168 | if (g_ascii_isprint (buffer[i])) |
169 | 169 | tmp = buffer[i]; |
670 | 670 | g_set_error (error, |
671 | 671 | FWUPD_ERROR, |
672 | 672 | FWUPD_ERROR_INVALID_FILE, |
673 | "Firmware too small: %" G_GSIZE_FORMAT " bytes", sz); | |
673 | "Firmware too small: %" G_GSSIZE_FORMAT " bytes", sz); | |
674 | 674 | return FALSE; |
675 | 675 | } |
676 | 676 | |
699 | 699 | return FALSE; |
700 | 700 | } |
701 | 701 | } |
702 | g_debug ("ROM buffer filled %likb/%likb", sz / 0x400, buffer_sz / 0x400); | |
702 | g_debug ("ROM buffer filled %" G_GSSIZE_FORMAT "kb/%" G_GSSIZE_FORMAT "kb", | |
703 | sz / 0x400, buffer_sz / 0x400); | |
703 | 704 | |
704 | 705 | /* detect optional IFR header and skip to option ROM */ |
705 | 706 | if (memcmp (buffer, "NVGI", 4) == 0) |
772 | 773 | |
773 | 774 | /* find first ROM header */ |
774 | 775 | hdr = g_ptr_array_index (priv->hdrs, 0); |
775 | priv->vendor = hdr->vendor_id; | |
776 | priv->model = hdr->device_id; | |
776 | priv->vendor_id = hdr->vendor_id; | |
777 | priv->device_id = hdr->device_id; | |
777 | 778 | priv->kind = FU_ROM_KIND_PCI; |
778 | 779 | |
779 | 780 | /* detect intel header */ |
820 | 821 | } |
821 | 822 | |
822 | 823 | /* update guid */ |
823 | id = g_strdup_printf ("0x%04x:0x%04x", priv->vendor, priv->model); | |
824 | id = g_strdup_printf ("PCI\\VEN_%04X&DEV_%04X", | |
825 | priv->vendor_id, priv->device_id); | |
824 | 826 | priv->guid = as_utils_guid_from_string (id); |
825 | 827 | g_debug ("using %s for %s", priv->guid, id); |
826 | 828 | |
877 | 879 | { |
878 | 880 | FuRomPrivate *priv = GET_PRIVATE (rom); |
879 | 881 | g_return_val_if_fail (FU_IS_ROM (rom), 0x0000); |
880 | return priv->vendor; | |
882 | return priv->vendor_id; | |
881 | 883 | } |
882 | 884 | |
883 | 885 | /** |
888 | 890 | { |
889 | 891 | FuRomPrivate *priv = GET_PRIVATE (rom); |
890 | 892 | g_return_val_if_fail (FU_IS_ROM (rom), 0x0000); |
891 | return priv->model; | |
893 | return priv->device_id; | |
892 | 894 | } |
893 | 895 | |
894 | 896 | /** |
311 | 311 | g_assert (device != NULL); |
312 | 312 | g_assert_cmpstr (fu_device_get_id (device), ==, "raspberry-pi"); |
313 | 313 | g_assert_cmpstr (fu_device_get_guid (device), ==, |
314 | "c77029fe-ffb2-3706-dc67-67af4a132afd"); | |
314 | "91dd7368-8640-5d72-a217-a505c034dd0b"); | |
315 | 315 | g_assert_cmpstr (fu_device_get_metadata (device, FU_DEVICE_KEY_VERSION), ==, |
316 | 316 | "20150803"); |
317 | 317 |
346 | 346 | const gchar *keys[] = { |
347 | 347 | FU_DEVICE_KEY_DISPLAY_NAME, |
348 | 348 | FU_DEVICE_KEY_PROVIDER, |
349 | FU_DEVICE_KEY_APPSTREAM_ID, | |
349 | 350 | FU_DEVICE_KEY_GUID, |
350 | 351 | FU_DEVICE_KEY_VERSION, |
351 | 352 | FU_DEVICE_KEY_URL_HOMEPAGE, |
1360 | 1361 | /* TRANSLATORS: first replacement is device name */ |
1361 | 1362 | g_print (_("%s has firmware updates:"), fu_device_get_display_name (dev)); |
1362 | 1363 | g_print ("\n"); |
1364 | ||
1365 | /* TRANSLATORS: Appstream ID for the hardware type */ | |
1366 | fu_util_print_data (_("ID"), | |
1367 | fu_device_get_metadata (dev, FU_DEVICE_KEY_APPSTREAM_ID)); | |
1368 | ||
1369 | /* TRANSLATORS: a GUID for the hardware */ | |
1370 | fu_util_print_data (_("GUID"), | |
1371 | fu_device_get_metadata (dev, FU_DEVICE_KEY_GUID)); | |
1363 | 1372 | |
1364 | 1373 | /* TRANSLATORS: section header for firmware version */ |
1365 | 1374 | fu_util_print_data (_("Version"), |