From e7aaf39de20067f1d310ca56175a6df68ac6bf2c Mon Sep 17 00:00:00 2001 From: Richard Hughes Date: Sun, 22 Nov 2015 10:15:48 +0000 Subject: [PATCH] libdfu: Fix multi-interface devices like the Neo Freerunner The DFU specification specifies that only one of the DFU interfaces has to export a functional descriptor; I assumed they all had to. Adding support for this kind of device rapidly turned into a massive restructure and it was all too complicated anyway. Reorganise the code so that we can support these kinds of devices and clean up the API so it's sane and easy to use. This also allows us to generate the GObject introspection GIR and to also install libdfu as a shared library. If you've got any comments about the API, please shout now as when 6.0 is released it will become API and ABI stable. --- contrib/fwupd.spec.in | 1 - docs/libdfu/Makefile.am | 2 +- libdfu/Makefile.am | 64 ++- libdfu/dfu-device-private.h | 63 ++- libdfu/dfu-device.c | 934 +++++++++++++++++++++++++++------ libdfu/dfu-device.h | 44 +- libdfu/dfu-element-private.h | 4 +- libdfu/dfu-element.c | 8 +- libdfu/dfu-firmware.c | 6 +- libdfu/dfu-image-private.h | 4 +- libdfu/dfu-image.c | 18 +- libdfu/dfu-self-test.c | 59 +-- libdfu/dfu-target-private.h | 6 +- libdfu/dfu-target.c | 966 ++++++----------------------------- libdfu/dfu-target.h | 46 +- libdfu/dfu-tool.c | 174 ++++--- src/Makefile.am | 2 +- src/fu-provider-usb.c | 37 +- 18 files changed, 1242 insertions(+), 1196 deletions(-) diff --git a/contrib/fwupd.spec.in b/contrib/fwupd.spec.in index 5086a29a7..c42fdb83d 100644 --- a/contrib/fwupd.spec.in +++ b/contrib/fwupd.spec.in @@ -100,7 +100,6 @@ find %{buildroot} -name '*.la' -exec rm -f {} ';' %{_unitdir}/system-update.target.wants/ %dir %{_localstatedir}/lib/fwupd %{_libdir}/lib*.so.* -%{_libdir}/fwupd/libdfu-private.so %{_libdir}/girepository-1.0/*.typelib %dir %{_localstatedir}/cache/app-info %dir %{_localstatedir}/cache/app-info/icons diff --git a/docs/libdfu/Makefile.am b/docs/libdfu/Makefile.am index 11fef2956..a05ab30c6 100644 --- a/docs/libdfu/Makefile.am +++ b/docs/libdfu/Makefile.am @@ -73,7 +73,7 @@ expand_content_files= # e.g. GTKDOC_CFLAGS=-I$(top_srcdir) -I$(top_builddir) $(GTK_DEBUG_FLAGS) # e.g. GTKDOC_LIBS=$(top_builddir)/gtk/$(gtktargetlib) GTKDOC_CFLAGS= -GTKDOC_LIBS=$(top_builddir)/libdfu/libdfu-private.la +GTKDOC_LIBS=$(top_builddir)/libdfu/libdfu.la # This includes the standard gtk-doc make rules, copied by gtkdocize. include $(top_srcdir)/gtk-doc.make diff --git a/libdfu/Makefile.am b/libdfu/Makefile.am index 16cb65288..4e3a28fc2 100644 --- a/libdfu/Makefile.am +++ b/libdfu/Makefile.am @@ -1,3 +1,10 @@ +if HAVE_INTROSPECTION +-include $(INTROSPECTION_MAKEFILE) +INTROSPECTION_GIRS = +INTROSPECTION_SCANNER_ARGS = --add-include-path=$(srcdir) +INTROSPECTION_COMPILER_ARGS = --includedir=$(srcdir) +endif + AM_CPPFLAGS = \ $(APPSTREAM_GLIB_CFLAGS) \ $(GLIB_CFLAGS) \ @@ -13,11 +20,25 @@ AM_CPPFLAGS = \ FWUPD_LIBS = \ $(top_builddir)/libfwupd/libfwupd.la -privlibdir=$(libdir)/fwupd -privlib_LTLIBRARIES = \ - libdfu-private.la +lib_LTLIBRARIES = \ + libdfu.la -libdfu_private_la_SOURCES = \ +libdfu_includedir = $(includedir)/fwupd-1 +libdfu_include_HEADERS = \ + dfu.h + +libdfubase_includedir = $(libdfu_includedir)/libdfu +libdfubase_include_HEADERS = \ + dfu-common.h \ + dfu-device.h \ + dfu-element.h \ + dfu-error.h \ + dfu-firmware.h \ + dfu-image.h \ + dfu-sector.h \ + dfu-target.h + +libdfu_la_SOURCES = \ dfu.h \ dfu-common.c \ dfu-common.h \ @@ -41,18 +62,18 @@ libdfu_private_la_SOURCES = \ dfu-target.h \ dfu-target-private.h -libdfu_private_la_LIBADD = \ +libdfu_la_LIBADD = \ $(FWUPD_LIBS) \ $(GUSB_LIBS) \ $(GLIB_LIBS) -libdfu_private_la_LDFLAGS = \ +libdfu_la_LDFLAGS = \ -export-dynamic \ -no-undefined \ -avoid-version \ -export-symbols-regex '^dfu_.*' -libdfu_private_la_CFLAGS = \ +libdfu_la_CFLAGS = \ $(WARNINGFLAGS_C) bin_PROGRAMS = \ @@ -62,7 +83,7 @@ dfu_tool_SOURCES = \ dfu-tool.c dfu_tool_LDADD = \ - $(privlib_LTLIBRARIES) \ + $(lib_LTLIBRARIES) \ $(APPSTREAM_GLIB_LIBS) \ $(GLIB_LIBS) \ $(GUSB_LIBS) @@ -82,7 +103,7 @@ dfu_self_test_SOURCES = \ dfu-self-test.c dfu_self_test_LDADD = \ - $(privlib_LTLIBRARIES) \ + $(lib_LTLIBRARIES) \ $(GLIB_LIBS) \ $(GUSB_LIBS) @@ -93,6 +114,31 @@ TESTS = dfu-self-test CLEANFILES = *.log *.trs $(BUILT_SOURCES) MAINTAINERCLEANFILES = *.dfu *.bin +if HAVE_INTROSPECTION +introspection_sources = \ + $(libdfu_la_SOURCES) + +Dfu-1.0.gir: libdfu.la +Dfu_1_0_gir_INCLUDES = GObject-2.0 Gio-2.0 GUsb-1.0 +Dfu_1_0_gir_CFLAGS = $(AM_CPPFLAGS) +Dfu_1_0_gir_SCANNERFLAGS = --identifier-prefix=Dfu \ + --symbol-prefix=dfu \ + --warn-all \ + --add-include-path=$(srcdir) \ + --c-include="dfu.h" +Dfu_1_0_gir_EXPORT_PACKAGES = dfu +Dfu_1_0_gir_LIBS = libdfu.la +Dfu_1_0_gir_FILES = $(introspection_sources) +INTROSPECTION_GIRS += Dfu-1.0.gir + +girdir = $(datadir)/gir-1.0 +gir_DATA = $(INTROSPECTION_GIRS) + +typelibdir = $(libdir)/girepository-1.0 +typelib_DATA = $(INTROSPECTION_GIRS:.gir=.typelib) + +CLEANFILES += $(gir_DATA) $(typelib_DATA) *.log *.trs *.test +endif clean-local: rm -f *~ diff --git a/libdfu/dfu-device-private.h b/libdfu/dfu-device-private.h index 8275a36be..b2cfebb97 100644 --- a/libdfu/dfu-device-private.h +++ b/libdfu/dfu-device-private.h @@ -30,11 +30,64 @@ G_BEGIN_DECLS -GUsbDevice *_dfu_device_get_usb_dev (DfuDevice *device); -void _dfu_device_set_runtime_vid (DfuDevice *device, - guint16 runtime_vid); -void _dfu_device_set_runtime_pid (DfuDevice *device, - guint16 runtime_pid); +/** + * DfuDeviceQuirks: + * @DFU_DEVICE_QUIRK_NONE: No device quirks + * @DFU_DEVICE_QUIRK_IGNORE_POLLTIMEOUT: Ignore the device download timeout + * @DFU_DEVICE_QUIRK_FORCE_DFU_MODE: Force DFU mode + * @DFU_DEVICE_QUIRK_IGNORE_INVALID_VERSION: Ignore invalid version numbers + * @DFU_DEVICE_QUIRK_USE_PROTOCOL_ZERO: Fix up the protocol number + * @DFU_DEVICE_QUIRK_NO_PID_CHANGE: Accept the same VID:PID when changing modes + * @DFU_DEVICE_QUIRK_NO_GET_STATUS_UPLOAD: Do not do GetStatus when uploading + * + * The workarounds for different devices. + **/ +typedef enum { + DFU_DEVICE_QUIRK_NONE = 0, + DFU_DEVICE_QUIRK_IGNORE_POLLTIMEOUT = (1 << 0), + DFU_DEVICE_QUIRK_FORCE_DFU_MODE = (1 << 1), + DFU_DEVICE_QUIRK_IGNORE_INVALID_VERSION = (1 << 2), + DFU_DEVICE_QUIRK_USE_PROTOCOL_ZERO = (1 << 3), + DFU_DEVICE_QUIRK_NO_PID_CHANGE = (1 << 4), + DFU_DEVICE_QUIRK_NO_GET_STATUS_UPLOAD = (1 << 5), + /*< private >*/ + DFU_DEVICE_QUIRK_LAST +} DfuDeviceQuirks; + +/** + * DfuDeviceAttributes: + * @DFU_DEVICE_ATTRIBUTE_NONE: No attributes set + * @DFU_DEVICE_ATTRIBUTE_CAN_DOWNLOAD: Can download from host->device + * @DFU_DEVICE_ATTRIBUTE_CAN_UPLOAD: Can upload from device->host + * @DFU_DEVICE_ATTRIBUTE_MANIFEST_TOL: Can answer GetStatus in manifest + * @DFU_DEVICE_ATTRIBUTE_WILL_DETACH: Will self-detach + * @DFU_DEVICE_ATTRIBUTE_CAN_ACCELERATE: Use a larger transfer size for speed + * + * The device DFU attributes. + **/ +typedef enum { + DFU_DEVICE_ATTRIBUTE_NONE = 0, + DFU_DEVICE_ATTRIBUTE_CAN_DOWNLOAD = (1 << 0), + DFU_DEVICE_ATTRIBUTE_CAN_UPLOAD = (1 << 1), + DFU_DEVICE_ATTRIBUTE_MANIFEST_TOL = (1 << 2), + DFU_DEVICE_ATTRIBUTE_WILL_DETACH = (1 << 3), + DFU_DEVICE_ATTRIBUTE_CAN_ACCELERATE = (1 << 7), + /*< private >*/ + DFU_DEVICE_ATTRIBUTE_LAST +} DfuDeviceAttributes; + +GUsbDevice *dfu_device_get_usb_dev (DfuDevice *device); + +gboolean dfu_device_has_dfuse_support (DfuDevice *device); +gboolean dfu_device_has_attribute (DfuDevice *device, + DfuDeviceAttributes attribute); +gboolean dfu_device_has_quirk (DfuDevice *device, + DfuDeviceQuirks quirk); + +void dfu_device_error_fixup (DfuDevice *device, + GCancellable *cancellable, + GError **error); +guint dfu_device_get_download_timeout (DfuDevice *device); G_END_DECLS diff --git a/libdfu/dfu-device.c b/libdfu/dfu-device.c index 2b03a2118..ead97ddd2 100644 --- a/libdfu/dfu-device.c +++ b/libdfu/dfu-device.c @@ -52,11 +52,21 @@ static void dfu_device_finalize (GObject *object); * Private #DfuDevice data **/ typedef struct { - GUsbDevice *dev; + DfuDeviceAttributes attributes; + DfuDeviceQuirks quirks; + DfuMode mode; + DfuState state; + DfuStatus status; GPtrArray *targets; + GUsbDevice *dev; gboolean device_open; + gboolean dfuse_supported; guint16 runtime_pid; guint16 runtime_vid; + guint16 transfer_size; + guint8 iface_number; + guint dnload_timeout; + guint timeout_ms; } DfuDevicePrivate; G_DEFINE_TYPE_WITH_PRIVATE (DfuDevice, dfu_device, G_TYPE_OBJECT) @@ -79,9 +89,66 @@ static void dfu_device_init (DfuDevice *device) { DfuDevicePrivate *priv = GET_PRIVATE (device); - priv->runtime_vid = 0xffff; priv->runtime_pid = 0xffff; + priv->runtime_vid = 0xffff; + priv->state = DFU_STATE_APP_IDLE; + priv->status = DFU_STATUS_OK; priv->targets = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref); + priv->timeout_ms = 500; + priv->transfer_size = 64; +} + +/** + * dfu_device_get_transfer_size: + * @device: a #GUsbDevice + * + * Gets the transfer size in bytes. + * + * Return value: packet size, or 0 for unknown + * + * Since: 0.5.4 + **/ +guint16 +dfu_device_get_transfer_size (DfuDevice *device) +{ + DfuDevicePrivate *priv = GET_PRIVATE (device); + g_return_val_if_fail (DFU_IS_DEVICE (device), 0xffff); + return priv->transfer_size; +} + +/** + * dfu_device_get_download_timeout: + * @device: a #GUsbDevice + * + * Gets the download timeout in ms. + * + * Return value: delay, or 0 for unknown + * + * Since: 0.5.4 + **/ +guint +dfu_device_get_download_timeout (DfuDevice *device) +{ + DfuDevicePrivate *priv = GET_PRIVATE (device); + g_return_val_if_fail (DFU_IS_DEVICE (device), 0); + return priv->dnload_timeout; +} + +/** + * dfu_device_set_transfer_size: + * @device: a #GUsbDevice + * @transfer_size: maximum packet size + * + * Sets the transfer size in bytes. + * + * Since: 0.5.4 + **/ +void +dfu_device_set_transfer_size (DfuDevice *device, guint16 transfer_size) +{ + DfuDevicePrivate *priv = GET_PRIVATE (device); + g_return_if_fail (DFU_IS_DEVICE (device)); + priv->transfer_size = transfer_size; } /** @@ -104,6 +171,118 @@ dfu_device_finalize (GObject *object) G_OBJECT_CLASS (dfu_device_parent_class)->finalize (object); } +typedef struct __attribute__((packed)) { + guint8 bLength; + guint8 bDescriptorType; + guint8 bmAttributes; + guint16 wDetachTimeOut; + guint16 wTransferSize; + guint16 bcdDFUVersion; +} DfuFuncDescriptor; + +/** + * dfu_device_parse_iface_data: + **/ +static void +dfu_device_parse_iface_data (DfuDevice *device, GBytes *iface_data) +{ + DfuDevicePrivate *priv = GET_PRIVATE (device); + const DfuFuncDescriptor *desc; + gsize iface_data_length; + + /* parse the functional descriptor */ + desc = g_bytes_get_data (iface_data, &iface_data_length); + if (iface_data_length != 0x09) { + g_warning ("interface found, but not interface data"); + return; + } + + /* check sanity */ + if (desc->bLength != 0x09) { + g_warning ("DFU interface data has incorrect length: 0x%02x", + desc->bLength); + } + + /* check transfer size */ + priv->transfer_size = desc->wTransferSize; + if (priv->transfer_size == 0x0000) { + g_warning ("DFU transfer size invalid, using default: 0x%04x", + desc->wTransferSize); + priv->transfer_size = 64; + } + + /* check DFU version */ + if (priv->quirks & DFU_DEVICE_QUIRK_IGNORE_INVALID_VERSION) { + g_debug ("ignoring quirked DFU version"); + } else { + if (desc->bcdDFUVersion == 0x0100 || + desc->bcdDFUVersion == 0x0101) { + g_debug ("basic DFU, no DfuSe support"); + priv->dfuse_supported = FALSE; + } else if (desc->bcdDFUVersion == 0x011a) { + g_debug ("DfuSe support"); + priv->dfuse_supported = TRUE; + } else { + g_warning ("DFU version is invalid: 0x%04x", + desc->bcdDFUVersion); + } + } + + /* ST-specific */ + if (priv->dfuse_supported && + desc->bmAttributes & DFU_DEVICE_ATTRIBUTE_CAN_ACCELERATE) + priv->transfer_size = 0x1000; + + /* get attributes about the DFU operation */ + priv->attributes = desc->bmAttributes; +} + +/** + * dfu_device_update_from_iface: + **/ +static gboolean +dfu_device_update_from_iface (DfuDevice *device, GUsbInterface *iface) +{ + DfuMode target_mode = DFU_MODE_UNKNOWN; + DfuDevicePrivate *priv = GET_PRIVATE (device); + + /* runtime */ + if (g_usb_interface_get_protocol (iface) == 0x01) + target_mode = DFU_MODE_RUNTIME; + + /* DFU */ + if (g_usb_interface_get_protocol (iface) == 0x02) + target_mode = DFU_MODE_DFU; + + /* the DSO Nano has uses 0 instead of 2 when in DFU target_mode */ + if (dfu_device_has_quirk (device, DFU_DEVICE_QUIRK_USE_PROTOCOL_ZERO) && + g_usb_interface_get_protocol (iface) == 0x00) + target_mode = DFU_MODE_DFU; + + /* nothing found */ + if (target_mode == DFU_MODE_UNKNOWN) + return FALSE; + + /* in DFU mode, the interface is supposed to be 0 */ + if (target_mode == DFU_MODE_DFU && g_usb_interface_get_number (iface) != 0) + g_warning ("iface has to be 0 in DFU mode, got 0x%02i", + g_usb_interface_get_number (iface)); + + /* some devices set the wrong mode */ + if (dfu_device_has_quirk (device, DFU_DEVICE_QUIRK_FORCE_DFU_MODE)) + target_mode = DFU_MODE_DFU; + + /* save for reset */ + if (target_mode == DFU_MODE_RUNTIME || + (priv->quirks & DFU_DEVICE_QUIRK_NO_PID_CHANGE)) { + priv->runtime_vid = g_usb_device_get_vid (priv->dev); + priv->runtime_pid = g_usb_device_get_pid (priv->dev); + } + + priv->mode = target_mode; + return TRUE; +} + /** * dfu_device_add_targets: **/ @@ -121,20 +300,255 @@ dfu_device_add_targets (DfuDevice *device) return FALSE; g_ptr_array_set_size (priv->targets, 0); for (i = 0; i < ifaces->len; i++) { + GBytes *iface_data = NULL; DfuTarget *target; iface = g_ptr_array_index (ifaces, i); if (g_usb_interface_get_class (iface) != G_USB_DEVICE_CLASS_APPLICATION_SPECIFIC) continue; if (g_usb_interface_get_subclass (iface) != 0x01) continue; - target = _dfu_target_new (device, iface); + target = dfu_target_new (device, iface); if (target == NULL) continue; + + /* add target */ + priv->iface_number = g_usb_interface_get_number (iface); g_ptr_array_add (priv->targets, target); + dfu_device_update_from_iface (device, iface); + + /* parse any interface data */ + iface_data = g_usb_interface_get_extra (iface); + if (g_bytes_get_size (iface_data) > 0) + dfu_device_parse_iface_data (device, iface_data); } return priv->targets->len > 0; } +/** + * dfu_device_has_quirk: (skip) + * @device: A #DfuDevice + * @quirk: A #DfuDeviceQuirks + * + * Returns if a device has a specific quirk + * + * Return value: %TRUE if the device has this quirk + * + * Since: 0.5.4 + **/ +gboolean +dfu_device_has_quirk (DfuDevice *device, DfuDeviceQuirks quirk) +{ + DfuDevicePrivate *priv = GET_PRIVATE (device); + g_return_val_if_fail (DFU_IS_DEVICE (device), 0x0); + return (priv->quirks & quirk) > 0; +} + +/** + * dfu_device_can_upload: + * @device: a #GUsbDevice + * + * Gets if the device can upload. + * + * Return value: %TRUE if the device can upload from device to host + * + * Since: 0.5.4 + **/ +gboolean +dfu_device_can_upload (DfuDevice *device) +{ + DfuDevicePrivate *priv = GET_PRIVATE (device); + g_return_val_if_fail (DFU_IS_DEVICE (device), FALSE); + return (priv->attributes & DFU_DEVICE_ATTRIBUTE_CAN_UPLOAD) > 0; +} + +/** + * dfu_device_can_download: + * @device: a #GUsbDevice + * + * Gets if the device can download. + * + * Return value: %TRUE if the device can download from host to device + * + * Since: 0.5.4 + **/ +gboolean +dfu_device_can_download (DfuDevice *device) +{ + DfuDevicePrivate *priv = GET_PRIVATE (device); + g_return_val_if_fail (DFU_IS_DEVICE (device), FALSE); + return (priv->attributes & DFU_DEVICE_ATTRIBUTE_CAN_DOWNLOAD) > 0; +} + +/** + * dfu_device_set_timeout: + * @device: a #DfuDevice + * @timeout_ms: the timeout in ms + * + * Sets the USB timeout to use when contacting the USB device. + * + * Since: 0.5.4 + **/ +void +dfu_device_set_timeout (DfuDevice *device, guint timeout_ms) +{ + DfuDevicePrivate *priv = GET_PRIVATE (device); + g_return_if_fail (DFU_IS_DEVICE (device)); + priv->timeout_ms = timeout_ms; +} + +/** + * dfu_device_get_mode: + * @device: a #GUsbDevice + * + * Gets the device mode. + * + * Return value: enumerated mode, e.g. %DFU_MODE_RUNTIME + * + * Since: 0.5.4 + **/ +DfuMode +dfu_device_get_mode (DfuDevice *device) +{ + DfuDevicePrivate *priv = GET_PRIVATE (device); + g_return_val_if_fail (DFU_IS_DEVICE (device), 0); + return priv->mode; +} + +/** + * dfu_device_get_timeout: + * @device: a #GUsbDevice + * + * Gets the device timeout. + * + * Return value: enumerated timeout in ms + * + * Since: 0.5.4 + **/ +guint +dfu_device_get_timeout (DfuDevice *device) +{ + DfuDevicePrivate *priv = GET_PRIVATE (device); + g_return_val_if_fail (DFU_IS_DEVICE (device), 0); + return priv->timeout_ms; +} + +/** + * dfu_device_get_state: + * @device: a #GUsbDevice + * + * Gets the device state. + * + * Return value: enumerated state, e.g. %DFU_STATE_DFU_UPLOAD_IDLE + * + * Since: 0.5.4 + **/ +DfuState +dfu_device_get_state (DfuDevice *device) +{ + DfuDevicePrivate *priv = GET_PRIVATE (device); + g_return_val_if_fail (DFU_IS_DEVICE (device), 0); + return priv->state; +} + +/** + * dfu_device_get_status: + * @device: a #GUsbDevice + * + * Gets the device status. + * + * Return value: enumerated status, e.g. %DFU_STATUS_ERR_ADDRESS + * + * Since: 0.5.4 + **/ +DfuStatus +dfu_device_get_status (DfuDevice *device) +{ + DfuDevicePrivate *priv = GET_PRIVATE (device); + g_return_val_if_fail (DFU_IS_DEVICE (device), 0); + return priv->status; +} + +/** + * dfu_device_has_attribute: (skip) + * @device: A #DfuDevice + * @attribute: A #DfuDeviceAttributes, e.g. %DFU_DEVICE_ATTRIBUTE_CAN_DOWNLOAD + * + * Returns if an attribute set for the device. + * + * Return value: %TRUE if the attribute is set + * + * Since: 0.5.4 + **/ +gboolean +dfu_device_has_attribute (DfuDevice *device, DfuDeviceAttributes attribute) +{ + DfuDevicePrivate *priv = GET_PRIVATE (device); + g_return_val_if_fail (DFU_IS_DEVICE (device), 0x0); + return (priv->attributes & attribute) > 0; +} + +/** + * dfu_device_has_dfuse_support: + * @device: A #DfuDevice + * + * Returns is DfuSe is supported on a device. + * + * Return value: %TRUE for DfuSe + * + * Since: 0.5.4 + **/ +gboolean +dfu_device_has_dfuse_support (DfuDevice *device) +{ + DfuDevicePrivate *priv = GET_PRIVATE (device); + g_return_val_if_fail (DFU_IS_DEVICE (device), FALSE); + return priv->dfuse_supported; +} + +/** + * dfu_device_set_quirks: + **/ +static void +dfu_target_set_quirks (DfuDevice *device) +{ + DfuDevicePrivate *priv = GET_PRIVATE (device); + guint16 vid, pid, release; + + vid = g_usb_device_get_vid (priv->dev); + pid = g_usb_device_get_pid (priv->dev); + release = g_usb_device_get_release (priv->dev); + + /* Openmoko Freerunner / GTA02 */ + if ((vid == 0x1d50 || vid == 0x1457) && + pid >= 0x5117 && pid <= 0x5126) + priv->quirks |= DFU_DEVICE_QUIRK_IGNORE_POLLTIMEOUT | + DFU_DEVICE_QUIRK_NO_PID_CHANGE | + DFU_DEVICE_QUIRK_NO_GET_STATUS_UPLOAD; + + /* OpenPCD Reader */ + if (vid == 0x16c0 && pid == 0x076b) + priv->quirks |= DFU_DEVICE_QUIRK_IGNORE_POLLTIMEOUT; + + /* Siemens AG, PXM 40 & PXM 50 */ + if (vid == 0x0908 && (pid == 0x02c4 || pid == 0x02c5) && release == 0x0) + priv->quirks |= DFU_DEVICE_QUIRK_IGNORE_POLLTIMEOUT; + + /* Midiman M-Audio Transit */ + if (vid == 0x0763 && pid == 0x2806) + priv->quirks |= DFU_DEVICE_QUIRK_IGNORE_POLLTIMEOUT; + + /* the LPC DFU bootloader uses the wrong mode */ + if (vid == 0x1fc9 && pid == 0x000c) + priv->quirks |= DFU_DEVICE_QUIRK_FORCE_DFU_MODE; + + /* the Leaflabs Maple3 is known broken */ + if (vid == 0x1eaf && pid == 0x0003 && release == 0x0200) + priv->quirks |= DFU_DEVICE_QUIRK_IGNORE_INVALID_VERSION; + + /* the DSO Nano has uses 0 instead of 2 when in DFU mode */ +// quirks |= DFU_DEVICE_QUIRK_USE_PROTOCOL_ZERO; +} + /** * dfu_device_new: * @dev: A #GUsbDevice @@ -157,6 +571,7 @@ dfu_device_new (GUsbDevice *dev) g_object_unref (device); return NULL; } + dfu_target_set_quirks (device); return device; } @@ -166,7 +581,7 @@ dfu_device_new (GUsbDevice *dev) * * Gets all the targets for this device. * - * Return value: (transfer none): (element-type DfuTarget): #DfuTarget, or %NULL + * Return value: (transfer none) (element-type DfuTarget): #DfuTarget, or %NULL * * Since: 0.5.4 **/ @@ -205,7 +620,7 @@ dfu_device_get_target_by_alt_setting (DfuDevice *device, /* find by ID */ for (i = 0; i < priv->targets->len; i++) { target = g_ptr_array_index (priv->targets, i); - if (dfu_target_get_interface_alt_setting (target) == alt_setting) + if (dfu_target_get_alt_setting (target) == alt_setting) return g_object_ref (target); } @@ -218,36 +633,6 @@ dfu_device_get_target_by_alt_setting (DfuDevice *device, return NULL; } -/** - * dfu_device_get_target_default: - * @device: a #DfuDevice - * @error: a #GError, or %NULL - * - * Gets the default target. - * - * Return value: (transfer full): a #DfuTarget, or %NULL - * - * Since: 0.5.4 - **/ -DfuTarget * -dfu_device_get_target_default (DfuDevice *device, GError **error) -{ - DfuDevicePrivate *priv = GET_PRIVATE (device); - - g_return_val_if_fail (DFU_IS_DEVICE (device), NULL); - g_return_val_if_fail (error == NULL || *error == NULL, NULL); - - /* find first target */ - if (priv->targets->len == 0) { - g_set_error_literal (error, - DFU_ERROR, - DFU_ERROR_NOT_FOUND, - "No default target"); - return NULL; - } - return g_object_ref (g_ptr_array_index (priv->targets, 0)); -} - /** * dfu_device_get_target_by_alt_name: * @device: a #DfuDevice @@ -275,7 +660,7 @@ dfu_device_get_target_by_alt_name (DfuDevice *device, /* find by ID */ for (i = 0; i < priv->targets->len; i++) { target = g_ptr_array_index (priv->targets, i); - if (g_strcmp0 (dfu_target_get_interface_alt_name (target), alt_name) == 0) + if (g_strcmp0 (dfu_target_get_alt_name (target, NULL), alt_name) == 0) return g_object_ref (target); } @@ -325,41 +710,7 @@ dfu_device_get_runtime_pid (DfuDevice *device) } /** - * _dfu_device_set_runtime_vid: - * @device: a #DfuDevice - * @runtime_vid: a vendor ID, or 0xffff for unknown - * - * Sets the runtime vendor ID. - * - * Since: 0.5.4 - **/ -void -_dfu_device_set_runtime_vid (DfuDevice *device, guint16 runtime_vid) -{ - DfuDevicePrivate *priv = GET_PRIVATE (device); - g_return_if_fail (DFU_IS_DEVICE (device)); - priv->runtime_vid = runtime_vid; -} - -/** - * _dfu_device_set_runtime_pid: - * @device: a #DfuDevice - * @runtime_pid: a product ID, or 0xffff for unknown - * - * Sets the runtime product ID. - * - * Since: 0.5.4 - **/ -void -_dfu_device_set_runtime_pid (DfuDevice *device, guint16 runtime_pid) -{ - DfuDevicePrivate *priv = GET_PRIVATE (device); - g_return_if_fail (DFU_IS_DEVICE (device)); - priv->runtime_pid = runtime_pid; -} - -/** - * _dfu_device_get_usb_dev: (skip) + * dfu_device_get_usb_dev: (skip) * @device: a #DfuDevice * * Gets the internal USB device for the #DfuDevice. @@ -370,16 +721,238 @@ _dfu_device_set_runtime_pid (DfuDevice *device, guint16 runtime_pid) * Returns: (transfer none): the internal USB device **/ GUsbDevice * -_dfu_device_get_usb_dev (DfuDevice *device) +dfu_device_get_usb_dev (DfuDevice *device) { DfuDevicePrivate *priv = GET_PRIVATE (device); g_return_val_if_fail (DFU_IS_DEVICE (device), NULL); return priv->dev; } + +/** + * dfu_device_refresh: + * @device: a #DfuDevice + * @cancellable: a #GCancellable, or %NULL + * @error: a #GError, or %NULL + * + * Refreshes the cached properties on the DFU device. + * + * Return value: %TRUE for success + * + * Since: 0.5.4 + **/ +gboolean +dfu_device_refresh (DfuDevice *device, GCancellable *cancellable, GError **error) +{ + DfuDevicePrivate *priv = GET_PRIVATE (device); + gsize actual_length = 0; + guint8 buf[6]; + g_autoptr(GError) error_local = NULL; + + g_return_val_if_fail (DFU_IS_DEVICE (device), FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + if (!g_usb_device_control_transfer (priv->dev, + G_USB_DEVICE_DIRECTION_DEVICE_TO_HOST, + G_USB_DEVICE_REQUEST_TYPE_CLASS, + G_USB_DEVICE_RECIPIENT_INTERFACE, + DFU_REQUEST_GETSTATUS, + 0, + priv->iface_number, + buf, sizeof(buf), &actual_length, + priv->timeout_ms, + cancellable, + &error_local)) { + g_set_error (error, + DFU_ERROR, + DFU_ERROR_NOT_SUPPORTED, + "cannot get device state: %s", + error_local->message); + return FALSE; + } + if (actual_length != 6) { + g_set_error (error, + DFU_ERROR, + DFU_ERROR_INTERNAL, + "cannot get device status, invalid size: %04x", + (guint) actual_length); + } + priv->status = buf[0]; + priv->state = buf[4]; + if (dfu_device_has_quirk (device, DFU_DEVICE_QUIRK_IGNORE_POLLTIMEOUT)) { + priv->dnload_timeout = 5; + } else { + priv->dnload_timeout = buf[1] + + (((guint32) buf[2]) << 8) + + (((guint32) buf[3]) << 16); + } + g_debug ("refreshed status=%s and state=%s", + dfu_status_to_string (priv->status), + dfu_state_to_string (priv->state)); + return TRUE; +} + +/** + * dfu_device_detach: + * @device: a #DfuDevice + * @cancellable: a #GCancellable, or %NULL + * @error: a #GError, or %NULL + * + * Detaches the device putting it into DFU-mode. + * + * Return value: %TRUE for success + * + * Since: 0.5.4 + **/ +gboolean +dfu_device_detach (DfuDevice *device, GCancellable *cancellable, GError **error) +{ + DfuDevicePrivate *priv = GET_PRIVATE (device); + g_autoptr(GError) error_local = NULL; + + g_return_val_if_fail (DFU_IS_DEVICE (device), FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + if (!g_usb_device_control_transfer (priv->dev, + G_USB_DEVICE_DIRECTION_HOST_TO_DEVICE, + G_USB_DEVICE_REQUEST_TYPE_CLASS, + G_USB_DEVICE_RECIPIENT_INTERFACE, + DFU_REQUEST_DETACH, + 0, + priv->iface_number, + NULL, 0, NULL, + priv->timeout_ms, + cancellable, + &error_local)) { + /* refresh the error code */ + dfu_device_error_fixup (device, cancellable, &error_local); + g_set_error (error, + DFU_ERROR, + DFU_ERROR_NOT_SUPPORTED, + "cannot detach device: %s", + error_local->message); + return FALSE; + } + + /* do a host reset */ + if ((priv->attributes & DFU_DEVICE_ATTRIBUTE_WILL_DETACH) == 0) { + g_debug ("doing device reset as host will not self-reset"); + if (!dfu_device_reset (device, error)) + return FALSE; + } + return TRUE; +} + +/** + * dfu_device_abort: + * @device: a #DfuDevice + * @cancellable: a #GCancellable, or %NULL + * @error: a #GError, or %NULL + * + * Aborts any upload or download in progress. + * + * Return value: %TRUE for success + * + * Since: 0.5.4 + **/ +gboolean +dfu_device_abort (DfuDevice *device, GCancellable *cancellable, GError **error) +{ + DfuDevicePrivate *priv = GET_PRIVATE (device); + g_autoptr(GError) error_local = NULL; + + g_return_val_if_fail (DFU_IS_DEVICE (device), FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + if (!g_usb_device_control_transfer (priv->dev, + G_USB_DEVICE_DIRECTION_HOST_TO_DEVICE, + G_USB_DEVICE_REQUEST_TYPE_CLASS, + G_USB_DEVICE_RECIPIENT_INTERFACE, + DFU_REQUEST_ABORT, + 0, + priv->iface_number, + NULL, 0, NULL, + priv->timeout_ms, + cancellable, + &error_local)) { + /* refresh the error code */ + dfu_device_error_fixup (device, cancellable, &error_local); + g_set_error (error, + DFU_ERROR, + DFU_ERROR_NOT_SUPPORTED, + "cannot abort device: %s", + error_local->message); + return FALSE; + } + + return TRUE; +} + +/** + * dfu_device_clear_status: + * @device: a #DfuDevice + * @cancellable: a #GCancellable, or %NULL + * @error: a #GError, or %NULL + * + * Clears any error status on the DFU device. + * + * Return value: %TRUE for success + * + * Since: 0.5.4 + **/ +gboolean +dfu_device_clear_status (DfuDevice *device, GCancellable *cancellable, GError **error) +{ + DfuDevicePrivate *priv = GET_PRIVATE (device); + g_autoptr(GError) error_local = NULL; + + g_return_val_if_fail (DFU_IS_DEVICE (device), FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + if (!g_usb_device_control_transfer (priv->dev, + G_USB_DEVICE_DIRECTION_HOST_TO_DEVICE, + G_USB_DEVICE_REQUEST_TYPE_CLASS, + G_USB_DEVICE_RECIPIENT_INTERFACE, + DFU_REQUEST_CLRSTATUS, + 0, + priv->iface_number, + NULL, 0, NULL, + priv->timeout_ms, + cancellable, + &error_local)) { + /* refresh the error code */ + dfu_device_error_fixup (device, cancellable, &error_local); + g_set_error (error, + DFU_ERROR, + DFU_ERROR_NOT_SUPPORTED, + "cannot clear status on the device: %s", + error_local->message); + return FALSE; + } + return TRUE; +} + +/** + * dfu_device_get_interface: + * @device: a #DfuDevice + * + * Gets the interface number. + * + * Since: 0.5.4 + **/ +guint8 +dfu_device_get_interface (DfuDevice *device) +{ + DfuDevicePrivate *priv = GET_PRIVATE (device); + g_return_val_if_fail (DFU_IS_DEVICE (device), 0xff); + return priv->iface_number; +} + /** * dfu_device_open: * @device: a #DfuDevice + * @flags: #DfuDeviceOpenFlags, e.g. %DFU_DEVICE_OPEN_FLAG_NONE + * @cancellable: a #GCancellable, or %NULL * @error: a #GError, or %NULL * * Opens a DFU-capable device. @@ -389,7 +962,8 @@ _dfu_device_get_usb_dev (DfuDevice *device) * Since: 0.5.4 **/ gboolean -dfu_device_open (DfuDevice *device, GError **error) +dfu_device_open (DfuDevice *device, DfuDeviceOpenFlags flags, + GCancellable *cancellable, GError **error) { DfuDevicePrivate *priv = GET_PRIVATE (device); g_autoptr(GError) error_local = NULL; @@ -420,8 +994,41 @@ dfu_device_open (DfuDevice *device, GError **error) error_local->message); return FALSE; } - priv->device_open = TRUE; + /* claim the correct interface */ + if (!g_usb_device_claim_interface (priv->dev, (gint) priv->iface_number, 0, &error_local)) { + g_set_error (error, + DFU_ERROR, + DFU_ERROR_INVALID_DEVICE, + "cannot claim interface %i: %s", + priv->iface_number, error_local->message); + return FALSE; + } + + + /* automatically abort any uploads or downloads */ + if ((flags & DFU_DEVICE_OPEN_FLAG_NO_AUTO_REFRESH) == 0) { + if (!dfu_device_refresh (device, cancellable, error)) + return FALSE; + switch (priv->state) { + case DFU_STATE_DFU_UPLOAD_IDLE: + case DFU_STATE_DFU_DNLOAD_IDLE: + case DFU_STATE_DFU_DNLOAD_SYNC: + g_debug ("aborting transfer %s", dfu_status_to_string (priv->status)); + if (!dfu_device_abort (device, cancellable, error)) + return FALSE; + break; + case DFU_STATE_DFU_ERROR: + g_debug ("clearing error %s", dfu_status_to_string (priv->status)); + if (!dfu_device_clear_status (device, cancellable, error)) + return FALSE; + break; + default: + break; + } + } + + priv->device_open = TRUE; return TRUE; } @@ -440,9 +1047,13 @@ gboolean dfu_device_close (DfuDevice *device, GError **error) { DfuDevicePrivate *priv = GET_PRIVATE (device); + g_autoptr(GError) error_local = NULL; /* only close if open */ if (priv->device_open) { + g_usb_device_release_interface (priv->dev, + (gint) priv->iface_number, + 0, NULL); if (!g_usb_device_close (priv->dev, error)) return FALSE; priv->device_open = FALSE; @@ -458,43 +1069,35 @@ dfu_device_set_new_usb_dev (DfuDevice *device, GUsbDevice *dev, GCancellable *cancellable, GError **error) { DfuDevicePrivate *priv = GET_PRIVATE (device); - guint i; - GUsbInterface *iface; - g_autoptr(GPtrArray) ifaces = NULL; + gboolean reopen_device = FALSE; - /* mark all existing interfaces as unclaimed */ - for (i = 0; i < priv->targets->len; i++) { - DfuTarget *target = g_ptr_array_index (priv->targets, i); - dfu_target_close (target, NULL); + /* close */ + if (priv->device_open) { + if (!dfu_device_close (device, error)) + return FALSE; + reopen_device = TRUE; } - /* close existing device */ - if (!dfu_device_close (device, error)) - return FALSE; - /* set the new USB device */ g_set_object (&priv->dev, dev); - /* update each interface */ - ifaces = g_usb_device_get_interfaces (dev, error); - if (ifaces == NULL) + /* update all the targets */ + g_ptr_array_set_size (priv->targets, 0); + if (!dfu_device_add_targets (device)) { + g_set_error_literal (error, + DFU_ERROR, + DFU_ERROR_NOT_SUPPORTED, + "replugged device is not DFU-capable"); return FALSE; - for (i = 0; i < ifaces->len; i++) { - guint8 alt_setting; - g_autoptr(DfuTarget) target = NULL; - iface = g_ptr_array_index (ifaces, i); - if (g_usb_interface_get_class (iface) != G_USB_DEVICE_CLASS_APPLICATION_SPECIFIC) - continue; - if (g_usb_interface_get_subclass (iface) != 0x01) - continue; - alt_setting = g_usb_interface_get_alternate (iface); - target = dfu_device_get_target_by_alt_setting (device, alt_setting, NULL); - if (target == NULL) - continue; - if (!_dfu_target_update (target, iface, cancellable, error)) + } + + /* reclaim */ + if (reopen_device) { + if (!dfu_device_open (device, DFU_DEVICE_OPEN_FLAG_NONE, + cancellable, error)) return FALSE; } - return dfu_device_open (device, error); + return TRUE; } /** @@ -543,6 +1146,12 @@ dfu_device_wait_for_replug (DfuDevice *device, guint timeout, continue; } + /* some devices use the same PID for DFU mode */ + if (priv->quirks & DFU_DEVICE_QUIRK_NO_PID_CHANGE) { + return dfu_device_set_new_usb_dev (device, dev_tmp, + cancellable, error); + } + /* VID:PID changed so find a DFU iface with the same alt */ if (vid != g_usb_device_get_vid (dev_tmp) || pid != g_usb_device_get_pid (dev_tmp)) { @@ -605,7 +1214,7 @@ dfu_device_reset (DfuDevice *device, GError **error) * @device: a #DfuDevice * @flags: flags to use, e.g. %DFU_TARGET_TRANSFER_FLAG_VERIFY * @cancellable: a #GCancellable, or %NULL - * @progress_cb: a #GFileProgressCallback, or %NULL + * @progress_cb: (scope call): a #GFileProgressCallback, or %NULL * @progress_cb_data: user data to pass to @progress_cb * @error: a #GError, or %NULL * @@ -624,19 +1233,10 @@ dfu_device_upload (DfuDevice *device, GError **error) { DfuDevicePrivate *priv = GET_PRIVATE (device); - gboolean auto_opened = FALSE; guint i; g_autoptr(DfuFirmware) firmware = NULL; - g_autoptr(DfuTarget) target_default = NULL; g_autoptr(GPtrArray) targets = NULL; - /* auto-open */ - if (!priv->device_open) { - if (!dfu_device_open (device, error)) - return FALSE; - auto_opened = TRUE; - } - /* create ahead of time */ firmware = dfu_firmware_new (); dfu_firmware_set_vid (firmware, priv->runtime_vid); @@ -644,10 +1244,7 @@ dfu_device_upload (DfuDevice *device, dfu_firmware_set_release (firmware, 0xffff); /* APP -> DFU */ - target_default = dfu_device_get_target_default (device, error); - if (target_default == NULL) - return NULL; - if (dfu_target_get_mode (target_default) == DFU_MODE_RUNTIME) { + if (dfu_device_get_mode (device) == DFU_MODE_RUNTIME) { if ((flags & DFU_TARGET_TRANSFER_FLAG_DETACH) == 0) { g_set_error (error, DFU_ERROR, @@ -664,7 +1261,7 @@ dfu_device_upload (DfuDevice *device, } /* detach and USB reset */ - if (!dfu_target_detach (target_default, NULL, error)) + if (!dfu_device_detach (device, NULL, error)) return NULL; if (!dfu_device_wait_for_replug (device, 5000, NULL, error)) return NULL; @@ -676,11 +1273,6 @@ dfu_device_upload (DfuDevice *device, DfuTarget *target; g_autoptr(DfuImage) image = NULL; target = g_ptr_array_index (targets, i); - if (!dfu_target_open (target, - DFU_TARGET_OPEN_FLAG_NONE, - cancellable, - error)) - return NULL; image = dfu_target_upload (target, DFU_TARGET_TRANSFER_FLAG_NONE, cancellable, @@ -722,12 +1314,6 @@ dfu_device_upload (DfuDevice *device, return NULL; } - /* auto-close */ - if (auto_opened) { - if (!dfu_device_close (device, error)) - return FALSE; - } - /* success */ return g_object_ref (firmware); } @@ -738,7 +1324,7 @@ dfu_device_upload (DfuDevice *device, * @firmware: a #DfuFirmware * @flags: flags to use, e.g. %DFU_TARGET_TRANSFER_FLAG_VERIFY * @cancellable: a #GCancellable, or %NULL - * @progress_cb: a #GFileProgressCallback, or %NULL + * @progress_cb: (scope call): a #GFileProgressCallback, or %NULL * @progress_cb_data: user data to pass to @progress_cb * @error: a #GError, or %NULL * @@ -760,9 +1346,7 @@ dfu_device_download (DfuDevice *device, { DfuDevicePrivate *priv = GET_PRIVATE (device); GPtrArray *images; - gboolean auto_opened = FALSE; guint i; - g_autoptr(DfuTarget) target_default = NULL; g_autoptr(GPtrArray) targets = NULL; /* do we allow wildcard VID:PID matches */ @@ -787,43 +1371,32 @@ dfu_device_download (DfuDevice *device, /* check vendor matches */ if (dfu_firmware_get_vid (firmware) != 0xffff && - dfu_device_get_runtime_pid (device) != 0xffff && - dfu_firmware_get_vid (firmware) != dfu_device_get_runtime_vid (device)) { + priv->runtime_pid != 0xffff && + dfu_firmware_get_vid (firmware) != priv->runtime_vid) { g_set_error (error, DFU_ERROR, DFU_ERROR_NOT_SUPPORTED, "vendor ID incorrect, expected 0x%04x got 0x%04x\n", dfu_firmware_get_vid (firmware), - dfu_device_get_runtime_vid (device)); + priv->runtime_vid); return FALSE; } /* check product matches */ if (dfu_firmware_get_pid (firmware) != 0xffff && - dfu_device_get_runtime_pid (device) != 0xffff && - dfu_firmware_get_pid (firmware) != dfu_device_get_runtime_pid (device)) { + priv->runtime_pid != 0xffff && + dfu_firmware_get_pid (firmware) != priv->runtime_pid) { g_set_error (error, DFU_ERROR, DFU_ERROR_NOT_SUPPORTED, "product ID incorrect, expected 0x%04x got 0x%04x", dfu_firmware_get_pid (firmware), - dfu_device_get_runtime_pid (device)); + priv->runtime_pid); return FALSE; } - /* auto-open */ - if (!priv->device_open) { - if (!dfu_device_open (device, error)) - return FALSE; - auto_opened = TRUE; - } - /* APP -> DFU */ - /* detach and USB reset */ - target_default = dfu_device_get_target_default (device, error); - if (target_default == NULL) - return FALSE; - if (dfu_target_get_mode (target_default) == DFU_MODE_RUNTIME) { + if (priv->mode == DFU_MODE_RUNTIME) { if ((flags & DFU_TARGET_TRANSFER_FLAG_DETACH) == 0) { g_set_error (error, DFU_ERROR, @@ -838,8 +1411,9 @@ dfu_device_download (DfuDevice *device, progress_cb_data); } + /* detach and USB reset */ g_debug ("detaching"); - if (!dfu_target_detach (target_default, NULL, error)) + if (!dfu_device_detach (device, NULL, error)) return FALSE; if (!dfu_device_wait_for_replug (device, 5000, NULL, error)) return FALSE; @@ -866,9 +1440,6 @@ dfu_device_download (DfuDevice *device, return FALSE; if (flags & DFU_TARGET_TRANSFER_FLAG_VERIFY) flags_local = DFU_TARGET_TRANSFER_FLAG_VERIFY; - if (!dfu_target_open (target_tmp, DFU_TARGET_OPEN_FLAG_NONE, - cancellable, error)) - return FALSE; if (!dfu_target_download (target_tmp, image, flags_local, @@ -899,20 +1470,51 @@ dfu_device_download (DfuDevice *device, /* DFU -> APP */ if (!dfu_device_wait_for_replug (device, 2000, cancellable, error)) return FALSE; - target_default = dfu_device_get_target_default (device, error); - if (target_default == NULL) - return FALSE; - if (!dfu_target_open (target_default, - DFU_TARGET_OPEN_FLAG_NONE, - NULL, error)) - return FALSE; - } - - /* auto-close */ - if (auto_opened) { - if (!dfu_device_close (device, error)) - return FALSE; } return TRUE; } + +/** + * dfu_device_error_fixup: + **/ +void +dfu_device_error_fixup (DfuDevice *device, + GCancellable *cancellable, + GError **error) +{ + DfuDevicePrivate *priv = GET_PRIVATE (device); + + /* sad panda */ + if (error == NULL) + return; + + /* not the right error to query */ + if (!g_error_matches (*error, + G_USB_DEVICE_ERROR, + G_USB_DEVICE_ERROR_NOT_SUPPORTED)) + return; + + /* get the status */ + if (!dfu_device_refresh (device, cancellable, NULL)) + return; + + /* not in an error state */ + if (priv->state != DFU_STATE_DFU_ERROR) + return; + + /* prefix the error */ + switch (priv->status) { + case DFU_STATUS_OK: + /* ignore */ + break; + case DFU_STATUS_ERR_VENDOR: + g_prefix_error (error, "read protection is active: "); + break; + default: + g_prefix_error (error, "[%s,%s]: ", + dfu_state_to_string (priv->state), + dfu_status_to_string (priv->status)); + break; + } +} diff --git a/libdfu/dfu-device.h b/libdfu/dfu-device.h index 2a0ebced2..2687f8db8 100644 --- a/libdfu/dfu-device.h +++ b/libdfu/dfu-device.h @@ -34,6 +34,20 @@ G_BEGIN_DECLS #define DFU_TYPE_DEVICE (dfu_device_get_type ()) G_DECLARE_DERIVABLE_TYPE (DfuDevice, dfu_device, DFU, DEVICE, GObject) +/** + * DfuDeviceOpenFlags: + * @DFU_DEVICE_OPEN_FLAG_NONE: No flags set + * @DFU_DEVICE_OPEN_FLAG_NO_AUTO_REFRESH: Do not do the initial GET_STATUS + * + * The optional flags used for opening the target. + **/ +typedef enum { + DFU_DEVICE_OPEN_FLAG_NONE = 0, + DFU_DEVICE_OPEN_FLAG_NO_AUTO_REFRESH = (1 << 0), + /*< private >*/ + DFU_DEVICE_OPEN_FLAG_LAST, +} DfuDeviceOpenFlags; + struct _DfuDeviceClass { GObjectClass parent_class; @@ -41,12 +55,12 @@ struct _DfuDeviceClass DfuDevice *dfu_device_new (GUsbDevice *dev); gboolean dfu_device_open (DfuDevice *device, + DfuDeviceOpenFlags flags, + GCancellable *cancellable, GError **error); gboolean dfu_device_close (DfuDevice *device, GError **error); GPtrArray *dfu_device_get_targets (DfuDevice *device); -DfuTarget *dfu_device_get_target_default (DfuDevice *device, - GError **error); DfuTarget *dfu_device_get_target_by_alt_setting (DfuDevice *device, guint8 alt_setting, GError **error); @@ -74,6 +88,32 @@ gboolean dfu_device_download (DfuDevice *device, DfuProgressCallback progress_cb, gpointer progress_cb_data, GError **error); +gboolean dfu_device_refresh (DfuDevice *device, + GCancellable *cancellable, + GError **error); +gboolean dfu_device_detach (DfuDevice *device, + GCancellable *cancellable, + GError **error); +gboolean dfu_device_abort (DfuDevice *device, + GCancellable *cancellable, + GError **error); +gboolean dfu_device_clear_status (DfuDevice *device, + GCancellable *cancellable, + GError **error); + +guint8 dfu_device_get_interface (DfuDevice *device); +DfuMode dfu_device_get_mode (DfuDevice *device); +DfuState dfu_device_get_state (DfuDevice *device); +DfuStatus dfu_device_get_status (DfuDevice *device); +guint16 dfu_device_get_transfer_size (DfuDevice *device); +guint dfu_device_get_timeout (DfuDevice *device); +gboolean dfu_device_can_upload (DfuDevice *device); +gboolean dfu_device_can_download (DfuDevice *device); + +void dfu_device_set_transfer_size (DfuDevice *device, + guint16 transfer_size); +void dfu_device_set_timeout (DfuDevice *device, + guint timeout_ms); G_END_DECLS diff --git a/libdfu/dfu-element-private.h b/libdfu/dfu-element-private.h index 199ae889a..1f10180d2 100644 --- a/libdfu/dfu-element-private.h +++ b/libdfu/dfu-element-private.h @@ -26,11 +26,11 @@ G_BEGIN_DECLS -DfuElement *_dfu_element_from_dfuse (const guint8 *data, +DfuElement *dfu_element_from_dfuse (const guint8 *data, gsize length, guint32 *consumed, GError **error); -GBytes *_dfu_element_to_dfuse (DfuElement *element); +GBytes *dfu_element_to_dfuse (DfuElement *element); G_END_DECLS diff --git a/libdfu/dfu-element.c b/libdfu/dfu-element.c index ea024fc0c..e42bc27ee 100644 --- a/libdfu/dfu-element.c +++ b/libdfu/dfu-element.c @@ -261,7 +261,7 @@ typedef struct __attribute__((packed)) { } DfuSeElementPrefix; /** - * _dfu_element_from_dfuse: (skip) + * dfu_element_from_dfuse: (skip) * @data: data buffer * @length: length of @data we can access * @consumed: (out): the number of bytes we consued @@ -272,7 +272,7 @@ typedef struct __attribute__((packed)) { * Returns: a #DfuElement, or %NULL for error **/ DfuElement * -_dfu_element_from_dfuse (const guint8 *data, +dfu_element_from_dfuse (const guint8 *data, gsize length, guint32 *consumed, GError **error) @@ -298,7 +298,7 @@ _dfu_element_from_dfuse (const guint8 *data, } /** - * _dfu_element_to_dfuse: (skip) + * dfu_element_to_dfuse: (skip) * @element: a #DfuElement * * Packs a DfuSe element. @@ -306,7 +306,7 @@ _dfu_element_from_dfuse (const guint8 *data, * Returns: (transfer full): the packed data **/ GBytes * -_dfu_element_to_dfuse (DfuElement *element) +dfu_element_to_dfuse (DfuElement *element) { DfuElementPrivate *priv = GET_PRIVATE (element); DfuSeElementPrefix *el; diff --git a/libdfu/dfu-firmware.c b/libdfu/dfu-firmware.c index ac020e0d8..01cb50b84 100644 --- a/libdfu/dfu-firmware.c +++ b/libdfu/dfu-firmware.c @@ -662,7 +662,7 @@ dfu_firmware_add_dfuse (DfuFirmware *firmware, GBytes *bytes, GError **error) for (i = 0; i < prefix->targets; i++) { guint consumed; g_autoptr(DfuImage) image = NULL; - image = _dfu_image_from_dfuse (data + offset, + image = dfu_image_from_dfuse (data + offset, len - offset, &consumed, error); @@ -693,7 +693,7 @@ dfu_firmware_write_data_dfuse (DfuFirmware *firmware, GError **error) for (i = 0; i < priv->images->len; i++) { DfuImage *im = g_ptr_array_index (priv->images, i); GBytes *contents; - contents = _dfu_image_to_dfuse (im); + contents = dfu_image_to_dfuse (im); image_size_total += g_bytes_get_size (contents); g_ptr_array_add (dfuse_images, contents); } @@ -878,7 +878,7 @@ dfu_firmware_add_footer (DfuFirmware *firmware, GBytes *contents) ftr->crc = dfu_firmware_generate_crc32 (buf, length + 12); /* return all data */ - return g_bytes_new (buf, length + 0x10); + return g_bytes_new_take (buf, length + 0x10); } /** diff --git a/libdfu/dfu-image-private.h b/libdfu/dfu-image-private.h index c966dd464..aee7f34d7 100644 --- a/libdfu/dfu-image-private.h +++ b/libdfu/dfu-image-private.h @@ -26,11 +26,11 @@ G_BEGIN_DECLS -DfuImage *_dfu_image_from_dfuse (const guint8 *data, +DfuImage *dfu_image_from_dfuse (const guint8 *data, gsize length, guint32 *consumed, GError **error); -GBytes *_dfu_image_to_dfuse (DfuImage *image); +GBytes *dfu_image_to_dfuse (DfuImage *image); G_END_DECLS diff --git a/libdfu/dfu-image.c b/libdfu/dfu-image.c index da0d85255..5c5976a11 100644 --- a/libdfu/dfu-image.c +++ b/libdfu/dfu-image.c @@ -113,7 +113,7 @@ dfu_image_new (void) * * Gets the element data. * - * Return value: (transfer none): element data + * Return value: (transfer none) (element-type DfuElement): element data * * Since: 0.5.4 **/ @@ -318,7 +318,7 @@ typedef struct __attribute__((packed)) { } DfuSeImagePrefix; /** - * _dfu_image_from_dfuse: (skip) + * dfu_image_from_dfuse: (skip) * @data: data buffer * @length: length of @data we can access * @consumed: (out): the number of bytes we consued @@ -329,7 +329,7 @@ typedef struct __attribute__((packed)) { * Returns: a #DfuImage, or %NULL for error **/ DfuImage * -_dfu_image_from_dfuse (const guint8 *data, +dfu_image_from_dfuse (const guint8 *data, gsize length, guint32 *consumed, GError **error) @@ -339,7 +339,6 @@ _dfu_image_from_dfuse (const guint8 *data, DfuSeImagePrefix *im; guint32 offset = sizeof(DfuSeImagePrefix); guint j; - g_autoptr(GBytes) contents = NULL; g_assert_cmpint(sizeof(DfuSeImagePrefix), ==, 274); @@ -359,13 +358,12 @@ _dfu_image_from_dfuse (const guint8 *data, priv->alt_setting = im->alt_setting; if (im->target_named == 0x01) memcpy (priv->name, im->target_name, 255); - contents = g_bytes_new (data + offset, - GUINT32_FROM_LE (im->target_size)); + /* parse elements */ for (j = 0; j < im->elements; j++) { guint32 consumed_local; g_autoptr(DfuElement) element = NULL; - element = _dfu_element_from_dfuse (data + offset, length, + element = dfu_element_from_dfuse (data + offset, length, &consumed_local, error); if (element == NULL) return NULL; @@ -381,7 +379,7 @@ _dfu_image_from_dfuse (const guint8 *data, } /** - * _dfu_image_to_dfuse: (skip) + * dfu_image_to_dfuse: (skip) * @image: a #DfuImage * * Packs a DfuSe image @@ -389,7 +387,7 @@ _dfu_image_from_dfuse (const guint8 *data, * Returns: (transfer full): the packed data **/ GBytes * -_dfu_image_to_dfuse (DfuImage *image) +dfu_image_to_dfuse (DfuImage *image) { DfuImagePrivate *priv = GET_PRIVATE (image); DfuElement *element; @@ -405,7 +403,7 @@ _dfu_image_to_dfuse (DfuImage *image) element_array = g_ptr_array_new_with_free_func ((GDestroyNotify) g_bytes_unref); for (i = 0; i < priv->elements->len; i++) { element = g_ptr_array_index (priv->elements, i); - bytes = _dfu_element_to_dfuse (element); + bytes = dfu_element_to_dfuse (element); g_ptr_array_add (element_array, bytes); length_total += g_bytes_get_size (bytes); } diff --git a/libdfu/dfu-self-test.c b/libdfu/dfu-self-test.c index 12c9320d5..ffab7e589 100644 --- a/libdfu/dfu-self-test.c +++ b/libdfu/dfu-self-test.c @@ -103,24 +103,23 @@ dfu_firmware_raw_func (void) { DfuElement *element; DfuImage *image_tmp; + GBytes *no_suffix_contents; gchar buf[256]; guint i; gboolean ret; g_autoptr(DfuFirmware) firmware = NULL; g_autoptr(GBytes) fw = NULL; - g_autoptr(GBytes) no_suffix_contents = NULL; g_autoptr(GBytes) roundtrip_orig = NULL; g_autoptr(GBytes) roundtrip = NULL; g_autoptr(GError) error = NULL; - firmware = dfu_firmware_new (); - /* set up some dummy data */ for (i = 0; i < 256; i++) buf[i] = i; fw = g_bytes_new_static (buf, 256); /* load a non DFU firmware */ + firmware = dfu_firmware_new (); ret = dfu_firmware_parse_data (firmware, fw, DFU_FIRMWARE_PARSE_FLAG_NONE, &error); g_assert_no_error (error); g_assert (ret); @@ -163,8 +162,6 @@ dfu_firmware_dfu_func (void) g_autoptr(GError) error = NULL; g_autoptr(GFile) file = NULL; - firmware = dfu_firmware_new (); - /* set up some dummy data */ for (i = 0; i < 256; i++) buf[i] = i; @@ -305,7 +302,7 @@ dfu_device_func (void) g_assert (target1 != NULL); /* ensure open */ - ret = dfu_target_open (target1, DFU_TARGET_OPEN_FLAG_NONE, NULL, &error); + ret = dfu_device_open (device, DFU_DEVICE_OPEN_FLAG_NONE, NULL, &error); g_assert_no_error (error); g_assert (ret); @@ -315,7 +312,7 @@ dfu_device_func (void) g_assert (target2 != NULL); /* close */ - ret = dfu_target_close (target1, &error); + ret = dfu_device_close (device, &error); g_assert_no_error (error); g_assert (ret); } @@ -344,16 +341,13 @@ dfu_colorhug_plus_func (void) NULL); if (usb_device != NULL) { g_autoptr(DfuDevice) device2 = NULL; - g_autoptr(DfuTarget) target2 = NULL; device2 = dfu_device_new (usb_device); g_assert (device2 != NULL); - target2 = dfu_device_get_target_by_alt_setting (device2, 0, &error); - g_assert_no_error (error); - g_assert (target2 != NULL); - ret = dfu_target_open (target2, DFU_TARGET_OPEN_FLAG_NO_AUTO_REFRESH, NULL, &error); + + ret = dfu_device_open (device2, DFU_DEVICE_OPEN_FLAG_NONE, NULL, &error); g_assert_no_error (error); g_assert (ret); - ret = dfu_target_detach (target2, NULL, &error); + ret = dfu_device_detach (device2, NULL, &error); g_assert_no_error (error); g_assert (ret); @@ -363,9 +357,6 @@ dfu_colorhug_plus_func (void) g_assert (ret); /* close it */ - ret = dfu_target_close (target2, &error); - g_assert_no_error (error); - g_assert (ret); ret = dfu_device_close (device2, &error); g_assert_no_error (error); g_assert (ret); @@ -384,21 +375,18 @@ dfu_colorhug_plus_func (void) /* check it's DFU-capable */ device = dfu_device_new (usb_device); g_assert (device != NULL); - target = dfu_device_get_target_by_alt_setting (device, 0, &error); - g_assert_no_error (error); - g_assert (target != NULL); /* we don't know this yet */ g_assert_cmpint (dfu_device_get_runtime_vid (device), ==, 0xffff); g_assert_cmpint (dfu_device_get_runtime_pid (device), ==, 0xffff); /* open it */ - ret = dfu_target_open (target, DFU_TARGET_OPEN_FLAG_NONE, NULL, &error); + ret = dfu_device_open (device, DFU_DEVICE_OPEN_FLAG_NONE, NULL, &error); g_assert_no_error (error); g_assert (ret); /* is in dfuIDLE mode */ - g_assert_cmpstr (dfu_state_to_string (dfu_target_get_state (target)), ==, "dfuIDLE"); + g_assert_cmpstr (dfu_state_to_string (dfu_device_get_state (device)), ==, "dfuIDLE"); /* lets try and flash something inappropriate */ if (seen_app_idle) { @@ -427,12 +415,15 @@ dfu_colorhug_plus_func (void) } /* get a dump of the existing firmware */ + target = dfu_device_get_target_by_alt_setting (device, 0, &error); + g_assert_no_error (error); + g_assert (target != NULL); image = dfu_target_upload (target, DFU_TARGET_TRANSFER_FLAG_NONE, NULL, NULL, NULL, &error); g_assert_no_error (error); g_assert (DFU_IS_IMAGE (image)); elements = dfu_image_get_elements (image); - g_assert_nonnull (elements); + g_assert (elements != NULL); g_assert_cmpint (elements->len, ==, 1); /* download a new firmware */ @@ -453,23 +444,13 @@ dfu_colorhug_plus_func (void) /* we should know now */ g_assert_cmpint (dfu_device_get_runtime_vid (device), ==, 0x273f); g_assert_cmpint (dfu_device_get_runtime_pid (device), ==, 0x1002); - - /* close it */ - ret = dfu_target_close (target, &error); - g_assert_no_error (error); - g_assert (ret); - - /* close it again */ - ret = dfu_target_close (target, &error); - g_assert_no_error (error); - g_assert (ret); } /** - * _dfu_target_sectors_to_string: + * dfu_target_sectors_to_string: **/ static gchar * -_dfu_target_sectors_to_string (DfuTarget *target) +dfu_target_sectors_to_string (DfuTarget *target) { DfuSector *sector; GPtrArray *sectors; @@ -502,7 +483,7 @@ dfu_target_dfuse_func (void) ret = dfu_target_parse_sectors (target, NULL, &error); g_assert_no_error (error); g_assert (ret); - tmp = _dfu_target_sectors_to_string (target); + tmp = dfu_target_sectors_to_string (target); g_assert_cmpstr (tmp, ==, ""); g_free (tmp); @@ -510,7 +491,7 @@ dfu_target_dfuse_func (void) ret = dfu_target_parse_sectors (target, "@Flash3", &error); g_assert_no_error (error); g_assert (ret); - tmp = _dfu_target_sectors_to_string (target); + tmp = dfu_target_sectors_to_string (target); g_assert_cmpstr (tmp, ==, ""); g_free (tmp); @@ -518,7 +499,7 @@ dfu_target_dfuse_func (void) ret = dfu_target_parse_sectors (target, "@Internal Flash /0x08000000/2*001Ka", &error); g_assert_no_error (error); g_assert (ret); - tmp = _dfu_target_sectors_to_string (target); + tmp = dfu_target_sectors_to_string (target); g_assert_cmpstr (tmp, ==, "Zone:0, Sec#:0, Addr:0x08000000, Size:0x0400, Caps:0x1\n" "Zone:0, Sec#:0, Addr:0x08000400, Size:0x0400, Caps:0x1"); g_free (tmp); @@ -527,7 +508,7 @@ dfu_target_dfuse_func (void) ret = dfu_target_parse_sectors (target, "@Flash1 /0x08000000/2*001 Ka,4*001 Kg", &error); g_assert_no_error (error); g_assert (ret); - tmp = _dfu_target_sectors_to_string (target); + tmp = dfu_target_sectors_to_string (target); g_assert_cmpstr (tmp, ==, "Zone:0, Sec#:0, Addr:0x08000000, Size:0x0400, Caps:0x1\n" "Zone:0, Sec#:0, Addr:0x08000400, Size:0x0400, Caps:0x1\n" "Zone:0, Sec#:1, Addr:0x08000000, Size:0x0400, Caps:0x7\n" @@ -540,7 +521,7 @@ dfu_target_dfuse_func (void) ret = dfu_target_parse_sectors (target, "@Flash2 /0xF000/4*100Ba/0xE000/3*8Kg/0x80000/2*24Kg", &error); g_assert_no_error (error); g_assert (ret); - tmp = _dfu_target_sectors_to_string (target); + tmp = dfu_target_sectors_to_string (target); g_assert_cmpstr (tmp, ==, "Zone:0, Sec#:0, Addr:0x0000f000, Size:0x0064, Caps:0x1\n" "Zone:0, Sec#:0, Addr:0x0000f064, Size:0x0064, Caps:0x1\n" "Zone:0, Sec#:0, Addr:0x0000f0c8, Size:0x0064, Caps:0x1\n" diff --git a/libdfu/dfu-target-private.h b/libdfu/dfu-target-private.h index f0f05180b..756f5cdff 100644 --- a/libdfu/dfu-target-private.h +++ b/libdfu/dfu-target-private.h @@ -29,12 +29,8 @@ G_BEGIN_DECLS -DfuTarget *_dfu_target_new (DfuDevice *device, +DfuTarget *dfu_target_new (DfuDevice *device, GUsbInterface *iface); -gboolean _dfu_target_update (DfuTarget *target, - GUsbInterface *iface, - GCancellable *cancellable, - GError **error); /* export this just for the self tests */ gboolean dfu_target_parse_sectors (DfuTarget *target, diff --git a/libdfu/dfu-target.c b/libdfu/dfu-target.c index 2e32a4ccd..947015bca 100644 --- a/libdfu/dfu-target.c +++ b/libdfu/dfu-target.c @@ -46,25 +46,6 @@ static void dfu_target_finalize (GObject *object); -typedef enum { - DFU_ATTRIBUTE_NONE = 0, - DFU_ATTRIBUTE_CAN_DOWNLOAD = (1 << 0), - DFU_ATTRIBUTE_CAN_UPLOAD = (1 << 1), - DFU_ATTRIBUTE_MANIFEST_TOL = (1 << 2), - DFU_ATTRIBUTE_WILL_DETACH = (1 << 3), - DFU_ATTRIBUTE_CAN_ACCELERATE = (1 << 7), - DFU_ATTRIBUTE_LAST -} DfuAttributes; - -typedef enum { - DFU_QUIRK_NONE = 0, - DFU_QUIRK_IGNORE_POLLTIMEOUT = (1 << 0), - DFU_QUIRK_FORCE_DFU_MODE = (1 << 1), - DFU_QUIRK_IGNORE_INVALID_VERSION = (1 << 2), - DFU_QUIRK_USE_PROTOCOL_ZERO = (1 << 3), - DFU_QUIRK_LAST -} DfuQuirks; - typedef enum { DFU_CMD_DFUSE_GET_COMMAND = 0x00, DFU_CMD_DFUSE_SET_ADDRESS_POINTER = 0x21, @@ -78,23 +59,13 @@ typedef enum { * Private #DfuTarget data **/ typedef struct { - DfuMode mode; - DfuState state; - DfuStatus status; DfuDevice *device; - gboolean interface_claimed; - gboolean dfuse_supported; - guint16 transfer_size; - guint8 iface_number; - guint8 iface_alt_setting; - guint8 iface_alt_setting_idx; - gchar *iface_alt_setting_name; - guint dnload_timeout; - guint timeout_ms; - DfuAttributes attributes; - DfuQuirks quirks; - GPtrArray *sectors; /* of DfuSector */ - GHashTable *sectors_erased; /* of DfuSector:1 */ + gboolean done_setup; + guint8 alt_setting; + guint8 alt_idx; + gchar *alt_name; + GPtrArray *sectors; /* of DfuSector */ + GHashTable *sectors_erased; /* of DfuSector:1 */ } DfuTargetPrivate; G_DEFINE_TYPE_WITH_PRIVATE (DfuTarget, dfu_target, G_TYPE_OBJECT) @@ -117,10 +88,6 @@ static void dfu_target_init (DfuTarget *target) { DfuTargetPrivate *priv = GET_PRIVATE (target); - priv->state = DFU_STATE_APP_IDLE; - priv->status = DFU_STATUS_OK; - priv->timeout_ms = 500; - priv->transfer_size = 64; priv->sectors = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref); priv->sectors_erased = g_hash_table_new (g_direct_hash, g_direct_equal); } @@ -134,7 +101,7 @@ dfu_target_finalize (GObject *object) DfuTarget *target = DFU_TARGET (object); DfuTargetPrivate *priv = GET_PRIVATE (target); - g_free (priv->iface_alt_setting_name); + g_free (priv->alt_name); g_ptr_array_unref (priv->sectors); g_hash_table_unref (priv->sectors_erased); if (priv->device != NULL) @@ -327,6 +294,7 @@ gboolean dfu_target_parse_sectors (DfuTarget *target, const gchar *alt_name, GError **error) { DfuTargetPrivate *priv = GET_PRIVATE (target); + guint64 addr; guint i; guint j; g_autofree gchar *str_debug = NULL; @@ -336,6 +304,23 @@ dfu_target_parse_sectors (DfuTarget *target, const gchar *alt_name, GError **err if (alt_name == NULL) return TRUE; + /* From the Neo Freerunner */ + if (g_str_has_prefix (alt_name, "RAM 0x")) { + DfuSector *sector; + addr = g_ascii_strtoull (alt_name + 6, NULL, 16); + if (addr == 0 && addr > G_MAXUINT32) + return FALSE; + g_debug ("RAM descripton, so parsing"); + sector = dfu_sector_new (addr, /* addr */ + 0x0, /* size */ + 0x0, /* size_left */ + 0x0, /* zone */ + 0x0, /* number */ + DFU_SECTOR_CAP_READABLE | + DFU_SECTOR_CAP_WRITEABLE); + g_ptr_array_add (priv->sectors, sector); + } + /* not a DfuSe alternative name */ if (alt_name[0] != '@') return TRUE; @@ -347,7 +332,6 @@ dfu_target_parse_sectors (DfuTarget *target, const gchar *alt_name, GError **err zones = g_strsplit (alt_name, "/", -1); g_debug ("DfuSe nice alt-name: %s", g_strchomp (zones[0] + 1)); for (i = 1; zones[i] != NULL; i += 2) { - guint64 addr; g_auto(GStrv) sectors = NULL; /* parse address */ @@ -384,165 +368,8 @@ dfu_target_parse_sectors (DfuTarget *target, const gchar *alt_name, GError **err return TRUE; } -typedef struct __attribute__((packed)) { - guint8 bLength; - guint8 bDescriptorType; - guint8 bmAttributes; - guint16 wDetachTimeOut; - guint16 wTransferSize; - guint16 bcdDFUVersion; -} DfuFuncDescriptor; - /** - * dfu_target_get_quirks: - **/ -static DfuQuirks -dfu_target_get_quirks (GUsbDevice *dev) -{ - DfuQuirks quirks = DFU_QUIRK_NONE; - guint16 vid, pid, release; - - vid = g_usb_device_get_vid (dev); - pid = g_usb_device_get_pid (dev); - release = g_usb_device_get_release (dev); - - /* Openmoko Freerunner / GTA02 */ - if ((vid == 0x1d50 || vid == 0x1457) && - pid >= 0x5117 && pid <= 0x5126) - quirks |= DFU_QUIRK_IGNORE_POLLTIMEOUT; - - /* OpenPCD Reader */ - if (vid == 0x16c0 && pid == 0x076b) - quirks |= DFU_QUIRK_IGNORE_POLLTIMEOUT; - - /* Siemens AG, PXM 40 & PXM 50 */ - if (vid == 0x0908 && (pid == 0x02c4 || pid == 0x02c5) && release == 0x0) - quirks |= DFU_QUIRK_IGNORE_POLLTIMEOUT; - - /* Midiman M-Audio Transit */ - if (vid == 0x0763 && pid == 0x2806) - quirks |= DFU_QUIRK_IGNORE_POLLTIMEOUT; - - /* the LPC DFU bootloader uses the wrong mode */ - if (vid == 0x1fc9 && pid == 0x000c) - quirks |= DFU_QUIRK_FORCE_DFU_MODE; - - /* the Leaflabs Maple3 is known broken */ - if (vid == 0x1eaf && pid == 0x0003 && release == 0x0200) - quirks |= DFU_QUIRK_IGNORE_INVALID_VERSION; - - /* the DSO Nano has uses 0 instead of 2 when in DFU mode */ -// quirks |= DFU_QUIRK_USE_PROTOCOL_ZERO; - - return quirks; -} - -/** - * dfu_target_update_from_iface: - **/ -static gboolean -dfu_target_update_from_iface (DfuTarget *target, GUsbInterface *iface) -{ - DfuMode mode = DFU_MODE_UNKNOWN; - DfuQuirks quirks; - DfuTargetPrivate *priv = GET_PRIVATE (target); - GBytes *iface_data = NULL; - GUsbDevice *dev; - const DfuFuncDescriptor *desc; - gsize iface_data_length; - - /* runtime */ - if (g_usb_interface_get_protocol (iface) == 0x01) - mode = DFU_MODE_RUNTIME; - - /* DFU */ - if (g_usb_interface_get_protocol (iface) == 0x02) - mode = DFU_MODE_DFU; - - /* the DSO Nano has uses 0 instead of 2 when in DFU mode */ - dev = _dfu_device_get_usb_dev (priv->device); - quirks = dfu_target_get_quirks (dev); - if ((quirks & DFU_QUIRK_USE_PROTOCOL_ZERO) && - g_usb_interface_get_protocol (iface) == 0x00) - mode = DFU_MODE_DFU; - - /* nothing found */ - if (mode == DFU_MODE_UNKNOWN) - return FALSE; - - /* in DFU mode, the interface is supposed to be 0 */ - if (mode == DFU_MODE_DFU && g_usb_interface_get_number (iface) != 0) - g_warning ("iface has to be 0 in DFU mode, got 0x%02i", - g_usb_interface_get_number (iface)); - - /* some devices set the wrong mode */ - if (quirks & DFU_QUIRK_FORCE_DFU_MODE) - mode = DFU_MODE_DFU; - - /* save for reset */ - if (mode == DFU_MODE_RUNTIME) { - _dfu_device_set_runtime_vid (priv->device, g_usb_device_get_vid (dev)); - _dfu_device_set_runtime_pid (priv->device, g_usb_device_get_pid (dev)); - } - - /* update */ - priv->iface_number = g_usb_interface_get_number (iface); - priv->iface_alt_setting = g_usb_interface_get_alternate (iface); - priv->iface_alt_setting_idx = g_usb_interface_get_index (iface); - priv->quirks = quirks; - priv->mode = mode; - - /* parse the functional descriptor */ - iface_data = g_usb_interface_get_extra (iface); - desc = g_bytes_get_data (iface_data, &iface_data_length); - if (iface_data_length != 0x09) { - g_warning ("interface found, but no interface data"); - return FALSE; - } - - /* check sanity */ - if (desc->bLength != 0x09) { - g_warning ("DFU interface data has incorrect length: 0x%02x", - desc->bLength); - } - - /* check transfer size */ - priv->transfer_size = desc->wTransferSize; - if (priv->transfer_size == 0x0000) { - g_warning ("DFU transfer size invalid, using default: 0x%04x", - desc->wTransferSize); - priv->transfer_size = 64; - } - - /* check DFU version */ - if (quirks & DFU_QUIRK_IGNORE_INVALID_VERSION) { - g_debug ("ignoring quirked DFU version"); - } else { - if (desc->bcdDFUVersion == 0x0100 || - desc->bcdDFUVersion == 0x0101) { - g_debug ("basic DFU, no DfuSe support"); - priv->dfuse_supported = FALSE; - } else if (desc->bcdDFUVersion == 0x011a) { - g_debug ("DfuSe support"); - priv->dfuse_supported = TRUE; - } else { - g_warning ("DFU version is invalid: 0x%04x", - desc->bcdDFUVersion); - } - } - - /* ST-specific */ - if (priv->dfuse_supported && - desc->bmAttributes & DFU_ATTRIBUTE_CAN_ACCELERATE) - priv->transfer_size = 0x1000; - - /* get attributes about the DFU operation */ - priv->attributes = desc->bmAttributes; - return TRUE; -} - -/** - * _dfu_target_new: + * dfu_target_new: (skip) * @device: a #DfuDevice * @iface: a #GUsbInterface * @@ -554,17 +381,15 @@ dfu_target_update_from_iface (DfuTarget *target, GUsbInterface *iface) * Since: 0.5.4 **/ DfuTarget * -_dfu_target_new (DfuDevice *device, GUsbInterface *iface) +dfu_target_new (DfuDevice *device, GUsbInterface *iface) { DfuTargetPrivate *priv; DfuTarget *target; target = g_object_new (DFU_TYPE_TARGET, NULL); priv = GET_PRIVATE (target); priv->device = g_object_ref (device); - if (!dfu_target_update_from_iface (target, iface)) { - g_object_unref (target); - return NULL; - } + priv->alt_idx = g_usb_interface_get_index (iface); + priv->alt_setting = g_usb_interface_get_alternate (iface); return target; } @@ -586,175 +411,6 @@ dfu_target_get_sectors (DfuTarget *target) return priv->sectors; } -/** - * dfu_target_get_mode: - * @target: a #GUsbDevice - * - * Gets the target mode. - * - * Return value: enumerated mode, e.g. %DFU_MODE_RUNTIME - * - * Since: 0.5.4 - **/ -DfuMode -dfu_target_get_mode (DfuTarget *target) -{ - DfuTargetPrivate *priv = GET_PRIVATE (target); - g_return_val_if_fail (DFU_IS_TARGET (target), 0); - return priv->mode; -} - -/** - * dfu_target_get_state: - * @target: a #GUsbDevice - * - * Gets the target state. - * - * Return value: enumerated state, e.g. %DFU_STATE_DFU_UPLOAD_IDLE - * - * Since: 0.5.4 - **/ -DfuState -dfu_target_get_state (DfuTarget *target) -{ - DfuTargetPrivate *priv = GET_PRIVATE (target); - g_return_val_if_fail (DFU_IS_TARGET (target), 0); - return priv->state; -} - -/** - * dfu_target_get_status: - * @target: a #GUsbDevice - * - * Gets the target status. - * - * Return value: enumerated status, e.g. %DFU_STATUS_ERR_ADDRESS - * - * Since: 0.5.4 - **/ -DfuStatus -dfu_target_get_status (DfuTarget *target) -{ - DfuTargetPrivate *priv = GET_PRIVATE (target); - g_return_val_if_fail (DFU_IS_TARGET (target), 0); - return priv->status; -} - -/** - * dfu_target_can_upload: - * @target: a #GUsbDevice - * - * Gets if the target can upload. - * - * Return value: %TRUE if the target can upload from target to host - * - * Since: 0.5.4 - **/ -gboolean -dfu_target_can_upload (DfuTarget *target) -{ - DfuTargetPrivate *priv = GET_PRIVATE (target); - g_return_val_if_fail (DFU_IS_TARGET (target), FALSE); - return (priv->attributes & DFU_ATTRIBUTE_CAN_UPLOAD) > 0; -} - -/** - * dfu_target_can_download: - * @target: a #GUsbDevice - * - * Gets if the target can download. - * - * Return value: %TRUE if the target can download from host to target - * - * Since: 0.5.4 - **/ -gboolean -dfu_target_can_download (DfuTarget *target) -{ - DfuTargetPrivate *priv = GET_PRIVATE (target); - g_return_val_if_fail (DFU_IS_TARGET (target), FALSE); - return (priv->attributes & DFU_ATTRIBUTE_CAN_DOWNLOAD) > 0; -} - -/** - * dfu_target_get_transfer_size: - * @target: a #GUsbDevice - * - * Gets the transfer size in bytes. - * - * Return value: packet size, or 0 for unknown - * - * Since: 0.5.4 - **/ -guint16 -dfu_target_get_transfer_size (DfuTarget *target) -{ - DfuTargetPrivate *priv = GET_PRIVATE (target); - g_return_val_if_fail (DFU_IS_TARGET (target), 0xffff); - return priv->transfer_size; -} - -/** - * dfu_target_set_transfer_size: - * @target: a #GUsbDevice - * @transfer_size: maximum packet size - * - * Sets the transfer size in bytes. - * - * Since: 0.5.4 - **/ -void -dfu_target_set_transfer_size (DfuTarget *target, guint16 transfer_size) -{ - DfuTargetPrivate *priv = GET_PRIVATE (target); - g_return_if_fail (DFU_IS_TARGET (target)); - priv->transfer_size = transfer_size; -} - -/** - * dfu_target_error_fixup: - **/ -static void -dfu_target_error_fixup (DfuTarget *target, - GCancellable *cancellable, - GError **error) -{ - DfuTargetPrivate *priv = GET_PRIVATE (target); - - /* sad panda */ - if (error == NULL) - return; - - /* not the right error to query */ - if (!g_error_matches (*error, - G_USB_DEVICE_ERROR, - G_USB_DEVICE_ERROR_NOT_SUPPORTED)) - return; - - /* get the status */ - if (!dfu_target_refresh (target, cancellable, NULL)) - return; - - /* not in an error state */ - if (priv->state != DFU_STATE_DFU_ERROR) - return; - - /* prefix the error */ - switch (priv->status) { - case DFU_STATUS_OK: - /* ignore */ - break; - case DFU_STATUS_ERR_VENDOR: - g_prefix_error (error, "read protection is active: "); - break; - default: - g_prefix_error (error, "[%s,%s]: ", - dfu_state_to_string (priv->state), - dfu_status_to_string (priv->status)); - break; - } -} - /** * dfu_target_check_status: **/ @@ -766,23 +422,23 @@ dfu_target_check_status (DfuTarget *target, DfuTargetPrivate *priv = GET_PRIVATE (target); /* get the status */ - if (!dfu_target_refresh (target, cancellable, error)) + if (!dfu_device_refresh (priv->device, cancellable, error)) return FALSE; /* not in an error state */ - if (priv->state != DFU_STATE_DFU_ERROR) + if (dfu_device_get_state (priv->device) != DFU_STATE_DFU_ERROR) return TRUE; /* read protection */ - if (priv->dfuse_supported) { - if (priv->status == DFU_STATUS_ERR_VENDOR) { + if (dfu_device_has_dfuse_support (priv->device)) { + if (dfu_device_get_status (priv->device) == DFU_STATUS_ERR_VENDOR) { g_set_error (error, DFU_ERROR, DFU_ERROR_NOT_SUPPORTED, "failed, read protection is active"); return FALSE; } - if (priv->status == DFU_STATUS_ERR_TARGET) { + if (dfu_device_get_status (priv->device) == DFU_STATUS_ERR_TARGET) { g_set_error (error, DFU_ERROR, DFU_ERROR_NOT_SUPPORTED, @@ -796,16 +452,14 @@ dfu_target_check_status (DfuTarget *target, DFU_ERROR, DFU_ERROR_NOT_SUPPORTED, "failed, state:%s status:%s]: ", - dfu_state_to_string (priv->state), - dfu_status_to_string (priv->status)); + dfu_state_to_string (dfu_device_get_state (priv->device)), + dfu_status_to_string (dfu_device_get_status (priv->device))); return FALSE; } /** - * dfu_target_open: + * dfu_target_use_alt_setting: * @target: a #DfuTarget - * @flags: #DfuTargetOpenFlags, e.g. %DFU_TARGET_OPEN_FLAG_NONE - * @cancellable: a #GCancellable, or %NULL * @error: a #GError, or %NULL * * Opens a DFU-capable target. @@ -814,9 +468,8 @@ dfu_target_check_status (DfuTarget *target, * * Since: 0.5.4 **/ -gboolean -dfu_target_open (DfuTarget *target, DfuTargetOpenFlags flags, - GCancellable *cancellable, GError **error) +static gboolean +dfu_target_use_alt_setting (DfuTarget *target, GError **error) { DfuTargetPrivate *priv = GET_PRIVATE (target); GUsbDevice *dev; @@ -825,76 +478,64 @@ dfu_target_open (DfuTarget *target, DfuTargetOpenFlags flags, g_return_val_if_fail (DFU_IS_TARGET (target), FALSE); g_return_val_if_fail (error == NULL || *error == NULL, FALSE); - /* already done */ - if (priv->interface_claimed) - return TRUE; - - /* ensure parent device is open */ - if (!dfu_device_open (priv->device, error)) - return FALSE; - - /* claim the correct interface */ - dev = _dfu_device_get_usb_dev (priv->device); - if (!g_usb_device_claim_interface (dev, (gint) priv->iface_number, 0, &error_local)) { - g_set_error (error, - DFU_ERROR, - DFU_ERROR_INVALID_DEVICE, - "cannot claim interface %i: %s", - priv->iface_number, error_local->message); - return FALSE; - } - priv->interface_claimed = TRUE; - /* use the correct setting */ - if (priv->mode == DFU_MODE_DFU) { + dev = dfu_device_get_usb_dev (priv->device); + if (dfu_device_get_mode (priv->device) == DFU_MODE_DFU) { if (!g_usb_device_set_interface_alt (dev, - (gint) priv->iface_number, - (gint) priv->iface_alt_setting, + (gint) dfu_device_get_interface (priv->device), + (gint) priv->alt_setting, &error_local)) { g_set_error (error, DFU_ERROR, DFU_ERROR_NOT_SUPPORTED, "cannot set alternate setting 0x%02x on interface %i: %s", - priv->iface_alt_setting, - priv->iface_number, + priv->alt_setting, + dfu_device_get_interface (priv->device), error_local->message); return FALSE; } } - /* get string */ - if (priv->iface_alt_setting_idx != 0x00) { - priv->iface_alt_setting_name = - g_usb_device_get_string_descriptor (dev, - priv->iface_alt_setting_idx, - NULL); - } + return TRUE; +} - /* automatically abort any uploads or downloads */ - if ((flags & DFU_TARGET_OPEN_FLAG_NO_AUTO_REFRESH) == 0) { - if (!dfu_target_refresh (target, cancellable, error)) - return FALSE; - switch (priv->state) { - case DFU_STATE_DFU_UPLOAD_IDLE: - case DFU_STATE_DFU_DNLOAD_IDLE: - case DFU_STATE_DFU_DNLOAD_SYNC: - g_debug ("aborting transfer %s", dfu_status_to_string (priv->status)); - if (!dfu_target_abort (target, cancellable, error)) - return FALSE; - break; - case DFU_STATE_DFU_ERROR: - g_debug ("clearing error %s", dfu_status_to_string (priv->status)); - if (!dfu_target_clear_status (target, cancellable, error)) - return FALSE; - break; - default: - break; - } +/** + * dfu_target_setup: + * @target: a #DfuTarget + * @error: a #GError, or %NULL + * + * Opens a DFU-capable target. + * + * Return value: %TRUE for success + * + * Since: 0.5.4 + **/ +static gboolean +dfu_target_setup (DfuTarget *target, GError **error) +{ + DfuTargetPrivate *priv = GET_PRIVATE (target); + g_autoptr(GError) error_local = NULL; + + g_return_val_if_fail (DFU_IS_TARGET (target), FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + /* already done */ + if (priv->done_setup) + return TRUE; + + /* get string */ + if (priv->alt_idx != 0x00) { + GUsbDevice *dev; + dev = dfu_device_get_usb_dev (priv->device); + priv->alt_name = + g_usb_device_get_string_descriptor (dev, + priv->alt_idx, + NULL); } /* parse the DfuSe format according to UM0424 */ if (!dfu_target_parse_sectors (target, - priv->iface_alt_setting_name, + priv->alt_name, error)) return FALSE; @@ -908,304 +549,11 @@ dfu_target_open (DfuTarget *target, DfuTargetOpenFlags flags, 0x0, /* number */ DFU_SECTOR_CAP_READABLE | DFU_SECTOR_CAP_WRITEABLE); - g_debug ("no UM0424 sector descripton, so adding dummy"); + g_debug ("no UM0424 sector descripton in %s", priv->alt_name); g_ptr_array_add (priv->sectors, sector); } - return TRUE; -} - -/** - * dfu_target_close: - * @target: a #DfuTarget - * @error: a #GError, or %NULL - * - * Closes a DFU-capable target. - * - * Return value: %TRUE for success - * - * Since: 0.5.4 - **/ -gboolean -dfu_target_close (DfuTarget *target, GError **error) -{ - DfuTargetPrivate *priv = GET_PRIVATE (target); - GUsbDevice *dev; - g_autoptr(GError) error_local = NULL; - - g_return_val_if_fail (DFU_IS_TARGET (target), FALSE); - g_return_val_if_fail (error == NULL || *error == NULL, FALSE); - - /* this is our intention; the release might fail if the USB device - * has been disconnected already */ - priv->interface_claimed = FALSE; - - /* only release if claimed */ - if (priv->interface_claimed) { - dev = _dfu_device_get_usb_dev (priv->device); - if (!g_usb_device_release_interface (dev, - (gint) priv->iface_number, - 0, - &error_local)) { - g_set_error (error, - DFU_ERROR, - DFU_ERROR_INTERNAL, - "cannot release interface %i: %s", - priv->iface_number, error_local->message); - return FALSE; - } - } - - /* success */ - return TRUE; -} - -/** - * dfu_target_refresh: - * @target: a #DfuTarget - * @cancellable: a #GCancellable, or %NULL - * @error: a #GError, or %NULL - * - * Refreshes the cached properties on the DFU target. - * - * Return value: %TRUE for success - * - * Since: 0.5.4 - **/ -gboolean -dfu_target_refresh (DfuTarget *target, GCancellable *cancellable, GError **error) -{ - DfuTargetPrivate *priv = GET_PRIVATE (target); - gsize actual_length = 0; - guint8 buf[6]; - g_autoptr(GError) error_local = NULL; - - g_return_val_if_fail (DFU_IS_TARGET (target), FALSE); - g_return_val_if_fail (error == NULL || *error == NULL, FALSE); - - if (!g_usb_device_control_transfer (_dfu_device_get_usb_dev (priv->device), - G_USB_DEVICE_DIRECTION_DEVICE_TO_HOST, - G_USB_DEVICE_REQUEST_TYPE_CLASS, - G_USB_DEVICE_RECIPIENT_INTERFACE, - DFU_REQUEST_GETSTATUS, - 0, - priv->iface_number, - buf, sizeof(buf), &actual_length, - priv->timeout_ms, - cancellable, - &error_local)) { - g_set_error (error, - DFU_ERROR, - DFU_ERROR_NOT_SUPPORTED, - "cannot get target state: %s", - error_local->message); - return FALSE; - } - if (actual_length != 6) { - g_set_error (error, - DFU_ERROR, - DFU_ERROR_INTERNAL, - "cannot get target status, invalid size: %04x", - (guint) actual_length); - } - priv->status = buf[0]; - if (priv->quirks & DFU_QUIRK_IGNORE_POLLTIMEOUT) { - priv->dnload_timeout = 5; - } else { - priv->dnload_timeout = buf[1] + - (((guint32) buf[2]) << 8) + - (((guint32) buf[3]) << 16); - } - priv->state = buf[4]; - g_debug ("refreshed status=%s and state=%s", - dfu_status_to_string (priv->status), - dfu_state_to_string (priv->state)); - return TRUE; -} - -/** - * dfu_target_detach: - * @target: a #DfuTarget - * @cancellable: a #GCancellable, or %NULL - * @error: a #GError, or %NULL - * - * Detaches the target putting it into DFU-mode. - * - * Return value: %TRUE for success - * - * Since: 0.5.4 - **/ -gboolean -dfu_target_detach (DfuTarget *target, GCancellable *cancellable, GError **error) -{ - DfuTargetPrivate *priv = GET_PRIVATE (target); - g_autoptr(GError) error_local = NULL; - - g_return_val_if_fail (DFU_IS_TARGET (target), FALSE); - g_return_val_if_fail (error == NULL || *error == NULL, FALSE); - - if (!g_usb_device_control_transfer (_dfu_device_get_usb_dev (priv->device), - G_USB_DEVICE_DIRECTION_HOST_TO_DEVICE, - G_USB_DEVICE_REQUEST_TYPE_CLASS, - G_USB_DEVICE_RECIPIENT_INTERFACE, - DFU_REQUEST_DETACH, - 0, - priv->iface_number, - NULL, 0, NULL, - priv->timeout_ms, - cancellable, - &error_local)) { - /* refresh the error code */ - dfu_target_error_fixup (target, cancellable, &error_local); - g_set_error (error, - DFU_ERROR, - DFU_ERROR_NOT_SUPPORTED, - "cannot detach target: %s", - error_local->message); - return FALSE; - } - - /* do a host reset */ - if ((priv->attributes & DFU_ATTRIBUTE_WILL_DETACH) == 0) { - g_debug ("doing target reset as host will not self-reset"); - if (!dfu_device_reset (priv->device, error)) - return FALSE; - } - return TRUE; -} - -/** - * dfu_target_abort: - * @target: a #DfuTarget - * @cancellable: a #GCancellable, or %NULL - * @error: a #GError, or %NULL - * - * Aborts any upload or download in progress. - * - * Return value: %TRUE for success - * - * Since: 0.5.4 - **/ -gboolean -dfu_target_abort (DfuTarget *target, GCancellable *cancellable, GError **error) -{ - DfuTargetPrivate *priv = GET_PRIVATE (target); - g_autoptr(GError) error_local = NULL; - - g_return_val_if_fail (DFU_IS_TARGET (target), FALSE); - g_return_val_if_fail (error == NULL || *error == NULL, FALSE); - - if (!g_usb_device_control_transfer (_dfu_device_get_usb_dev (priv->device), - G_USB_DEVICE_DIRECTION_HOST_TO_DEVICE, - G_USB_DEVICE_REQUEST_TYPE_CLASS, - G_USB_DEVICE_RECIPIENT_INTERFACE, - DFU_REQUEST_ABORT, - 0, - priv->iface_number, - NULL, 0, NULL, - priv->timeout_ms, - cancellable, - &error_local)) { - /* refresh the error code */ - dfu_target_error_fixup (target, cancellable, &error_local); - g_set_error (error, - DFU_ERROR, - DFU_ERROR_NOT_SUPPORTED, - "cannot abort target: %s", - error_local->message); - return FALSE; - } - - return TRUE; -} - -/** - * _dfu_target_update: - * @target: a #DfuTarget - * @iface: a #GUsbInterface - * @cancellable: a #GCancellable, or %NULL - * @error: a #GError, or %NULL - * - * Updates the target with new interface data. This only needs to be - * done after the device has been reset. - * - * Returns: %TRUE for success - **/ -gboolean -_dfu_target_update (DfuTarget *target, GUsbInterface *iface, - GCancellable *cancellable, GError **error) -{ - DfuTargetPrivate *priv = GET_PRIVATE (target); - gboolean reclaim_interface = FALSE; - - /* close */ - if (priv->interface_claimed) { - if (!dfu_target_close (target, error)) - return FALSE; - reclaim_interface = TRUE; - } - - /* check this is _still_ a DFU-capable target */ - if (!dfu_target_update_from_iface (target, iface)) { - g_set_error_literal (error, - DFU_ERROR, - DFU_ERROR_NOT_SUPPORTED, - "replugged target is not DFU-capable"); - return FALSE; - } - - /* reclaim */ - if (reclaim_interface) { - if (!dfu_device_open (priv->device, error)) - return FALSE; - if (!dfu_target_open (target, DFU_TARGET_OPEN_FLAG_NONE, - cancellable, error)) - return FALSE; - } - return TRUE; -} - -/** - * dfu_target_clear_status: - * @target: a #DfuTarget - * @cancellable: a #GCancellable, or %NULL - * @error: a #GError, or %NULL - * - * Clears any error status on the DFU target. - * - * Return value: %TRUE for success - * - * Since: 0.5.4 - **/ -gboolean -dfu_target_clear_status (DfuTarget *target, GCancellable *cancellable, GError **error) -{ - DfuTargetPrivate *priv = GET_PRIVATE (target); - g_autoptr(GError) error_local = NULL; - - g_return_val_if_fail (DFU_IS_TARGET (target), FALSE); - g_return_val_if_fail (error == NULL || *error == NULL, FALSE); - - if (!g_usb_device_control_transfer (_dfu_device_get_usb_dev (priv->device), - G_USB_DEVICE_DIRECTION_HOST_TO_DEVICE, - G_USB_DEVICE_REQUEST_TYPE_CLASS, - G_USB_DEVICE_RECIPIENT_INTERFACE, - DFU_REQUEST_CLRSTATUS, - 0, - priv->iface_number, - NULL, 0, NULL, - priv->timeout_ms, - cancellable, - &error_local)) { - /* refresh the error code */ - dfu_target_error_fixup (target, cancellable, &error_local); - g_set_error (error, - DFU_ERROR, - DFU_ERROR_NOT_SUPPORTED, - "cannot clear status on the target: %s", - error_local->message); - return FALSE; - } + priv->done_setup = TRUE; return TRUE; } @@ -1220,21 +568,21 @@ dfu_target_download_chunk (DfuTarget *target, guint8 index, GBytes *bytes, g_autoptr(GError) error_local = NULL; gsize actual_length; - if (!g_usb_device_control_transfer (_dfu_device_get_usb_dev (priv->device), + if (!g_usb_device_control_transfer (dfu_device_get_usb_dev (priv->device), G_USB_DEVICE_DIRECTION_HOST_TO_DEVICE, G_USB_DEVICE_REQUEST_TYPE_CLASS, G_USB_DEVICE_RECIPIENT_INTERFACE, DFU_REQUEST_DNLOAD, index, - priv->iface_number, + dfu_device_get_interface (priv->device), (guint8 *) g_bytes_get_data (bytes, NULL), g_bytes_get_size (bytes), &actual_length, - priv->timeout_ms, + dfu_device_get_timeout (priv->device), cancellable, &error_local)) { /* refresh the error code */ - dfu_target_error_fixup (target, cancellable, &error_local); + dfu_device_error_fixup (priv->device, cancellable, &error_local); g_set_error (error, DFU_ERROR, DFU_ERROR_NOT_SUPPORTED, @@ -1277,7 +625,7 @@ dfu_target_set_address (DfuTarget *target, guint8 buf[5]; /* invalid */ - if (!priv->dfuse_supported) { + if (!dfu_device_has_dfuse_support (priv->device)) { g_set_error_literal (error, DFU_ERROR, DFU_ERROR_NOT_SUPPORTED, @@ -1324,7 +672,7 @@ dfu_target_erase_address (DfuTarget *target, guint8 buf[5]; /* invalid */ - if (!priv->dfuse_supported) { + if (!dfu_device_has_dfuse_support (priv->device)) { g_set_error_literal (error, DFU_ERROR, DFU_ERROR_NOT_SUPPORTED, @@ -1376,7 +724,7 @@ dfu_target_mass_erase (DfuTarget *target, guint8 buf[1]; /* invalid */ - if (!priv->dfuse_supported) { + if (!dfu_device_has_dfuse_support (priv->device)) { g_set_error_literal (error, DFU_ERROR, DFU_ERROR_NOT_SUPPORTED, @@ -1422,7 +770,7 @@ dfu_target_read_unprotect (DfuTarget *target, guint8 buf[5]; /* invalid */ - if (!priv->dfuse_supported) { + if (!dfu_device_has_dfuse_support (priv->device)) { g_set_error_literal (error, DFU_ERROR, DFU_ERROR_NOT_SUPPORTED, @@ -1454,22 +802,23 @@ dfu_target_upload_chunk (DfuTarget *target, guint8 index, g_autoptr(GError) error_local = NULL; guint8 *buf; gsize actual_length; + guint16 transfer_size = dfu_device_get_transfer_size (priv->device); - buf = g_new0 (guint8, priv->transfer_size); - if (!g_usb_device_control_transfer (_dfu_device_get_usb_dev (priv->device), + buf = g_new0 (guint8, transfer_size); + if (!g_usb_device_control_transfer (dfu_device_get_usb_dev (priv->device), G_USB_DEVICE_DIRECTION_DEVICE_TO_HOST, G_USB_DEVICE_REQUEST_TYPE_CLASS, G_USB_DEVICE_RECIPIENT_INTERFACE, DFU_REQUEST_UPLOAD, index, - priv->iface_number, - buf, (gsize) priv->transfer_size, + dfu_device_get_interface (priv->device), + buf, (gsize) transfer_size, &actual_length, - priv->timeout_ms, + dfu_device_get_timeout (priv->device), cancellable, &error_local)) { /* refresh the error code */ - dfu_target_error_fixup (target, cancellable, &error_local); + dfu_device_error_fixup (priv->device, cancellable, &error_local); g_set_error (error, DFU_ERROR, DFU_ERROR_NOT_SUPPORTED, @@ -1479,8 +828,10 @@ dfu_target_upload_chunk (DfuTarget *target, guint8 index, } /* for ST devices, the action only occurs when we do GetStatus */ - if (!dfu_target_check_status (target, cancellable, error)) - return FALSE; + if (!dfu_device_has_quirk (priv->device, DFU_DEVICE_QUIRK_NO_GET_STATUS_UPLOAD)) { + if (!dfu_target_check_status (target, cancellable, error)) + return FALSE; + } return g_bytes_new_take (buf, actual_length); } @@ -1504,6 +855,7 @@ dfu_target_upload_element (DfuTarget *target, gsize chunk_size; gsize offset = 0; gsize total_size = 0; + guint16 transfer_size = dfu_device_get_transfer_size (priv->device); guint8 *buffer; guint32 last_sector_id = G_MAXUINT; guint dfuse_sector_offset = 0; @@ -1512,7 +864,7 @@ dfu_target_upload_element (DfuTarget *target, g_autoptr(GPtrArray) chunks = NULL; /* ST uses wBlockNum=0 for DfuSe commands and wBlockNum=1 is reserved */ - if (priv->dfuse_supported) { + if (dfu_device_has_dfuse_support (priv->device)) { offset += address; dfuse_sector_offset = 2; } @@ -1522,7 +874,7 @@ dfu_target_upload_element (DfuTarget *target, for (i = 0; i < 0xffff; i++) { /* for DfuSe devices we need to handle the address manually */ - if (priv->dfuse_supported) { + if (dfu_device_has_dfuse_support (priv->device)) { /* check the sector with this element address is suitable */ sector = dfu_target_get_sector_for_addr (target, offset); @@ -1581,7 +933,7 @@ dfu_target_upload_element (DfuTarget *target, } /* detect short write as EOF */ - if (chunk_size < priv->transfer_size) + if (chunk_size < transfer_size) break; } @@ -1620,7 +972,7 @@ dfu_target_upload_element (DfuTarget *target, * @target: a #DfuTarget * @flags: flags to use, e.g. %DFU_TARGET_TRANSFER_FLAG_VERIFY * @cancellable: a #GCancellable, or %NULL - * @progress_cb: a #GFileProgressCallback, or %NULL + * @progress_cb: (scope call): a #GFileProgressCallback, or %NULL * @progress_cb_data: user data to pass to @progress_cb * @error: a #GError, or %NULL * @@ -1647,8 +999,12 @@ dfu_target_upload (DfuTarget *target, g_return_val_if_fail (DFU_IS_TARGET (target), NULL); g_return_val_if_fail (error == NULL || *error == NULL, NULL); + /* ensure populated */ + if (!dfu_target_setup (target, error)) + return NULL; + /* can the target do this? */ - if (!dfu_target_can_upload (target)) { + if (!dfu_device_can_upload (priv->device)) { g_set_error_literal (error, DFU_ERROR, DFU_ERROR_NOT_SUPPORTED, @@ -1656,6 +1012,10 @@ dfu_target_upload (DfuTarget *target, return NULL; } + /* use correct alt */ + if (!dfu_target_use_alt_setting (target, error)) + return NULL; + /* no open?! */ if (priv->sectors->len == 0) { g_set_error_literal (error, @@ -1667,8 +1027,8 @@ dfu_target_upload (DfuTarget *target, /* create a new image */ image = dfu_image_new (); - dfu_image_set_name (image, priv->iface_alt_setting_name); - dfu_image_set_alt_setting (image, priv->iface_alt_setting); + dfu_image_set_name (image, priv->alt_name); + dfu_image_set_alt_setting (image, priv->alt_setting); /* get all the sectors for the device */ for (i = 0; i < priv->sectors->len; i++) { @@ -1768,16 +1128,17 @@ dfu_target_download_element (DfuTarget *target, guint nr_chunks; guint dfuse_sector_offset = 0; guint last_sector_id = G_MAXUINT; + guint16 transfer_size = dfu_device_get_transfer_size (priv->device); g_autoptr(GError) error_local = NULL; /* ST uses wBlockNum=0 for DfuSe commands and wBlockNum=1 is reserved */ - if (priv->dfuse_supported) + if (dfu_device_has_dfuse_support (priv->device)) dfuse_sector_offset = 2; /* round up as we have to transfer incomplete blocks */ bytes = dfu_element_get_contents (element); nr_chunks = ceil ((gdouble) g_bytes_get_size (bytes) / - (gdouble) priv->transfer_size); + (gdouble) transfer_size); if (nr_chunks == 0) { g_set_error_literal (error, DFU_ERROR, @@ -1791,11 +1152,11 @@ dfu_target_download_element (DfuTarget *target, g_autoptr(GBytes) bytes_tmp = NULL; /* caclulate the offset into the element data */ - offset = i * priv->transfer_size; + offset = i * transfer_size; /* for DfuSe devices we need to handle the erase and setting * the address manually */ - if (priv->dfuse_supported) { + if (dfu_device_has_dfuse_support (priv->device)) { /* check the sector with this element address is suitable */ sector = dfu_target_get_sector_for_addr (target, offset); @@ -1845,8 +1206,8 @@ dfu_target_download_element (DfuTarget *target, /* we have to write one final zero-sized chunk for EOF */ if (i < nr_chunks) { length = g_bytes_get_size (bytes) - offset; - if (length > priv->transfer_size) - length = priv->transfer_size; + if (length > transfer_size) + length = transfer_size; bytes_tmp = g_bytes_new_from_bytes (bytes, offset, length); } else { bytes_tmp = g_bytes_new (NULL, 0); @@ -1869,10 +1230,10 @@ dfu_target_download_element (DfuTarget *target, } /* give the target a chance to update */ - g_usleep (priv->dnload_timeout * 1000); + g_usleep (dfu_device_get_download_timeout (priv->device) * 1000); /* getting the status moves the state machine to DNLOAD-IDLE */ - if (!dfu_target_refresh (target, cancellable, error)) + if (!dfu_device_refresh (priv->device, cancellable, error)) return FALSE; } @@ -1911,7 +1272,7 @@ dfu_target_download_element (DfuTarget *target, * @image: a #DfuImage * @flags: flags to use, e.g. %DFU_TARGET_TRANSFER_FLAG_VERIFY * @cancellable: a #GCancellable, or %NULL - * @progress_cb: a #GFileProgressCallback, or %NULL + * @progress_cb: (scope call): a #GFileProgressCallback, or %NULL * @progress_cb_data: user data to pass to @progress_cb * @error: a #GError, or %NULL * @@ -1940,8 +1301,12 @@ dfu_target_download (DfuTarget *target, DfuImage *image, g_return_val_if_fail (DFU_IS_IMAGE (image), FALSE); g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + /* ensure populated */ + if (!dfu_target_setup (target, error)) + return NULL; + /* can the target do this? */ - if (!dfu_target_can_download (target)) { + if (!dfu_device_can_download (priv->device)) { g_set_error_literal (error, DFU_ERROR, DFU_ERROR_NOT_SUPPORTED, @@ -1949,8 +1314,12 @@ dfu_target_download (DfuTarget *target, DfuImage *image, return FALSE; } + /* use correct alt */ + if (!dfu_target_use_alt_setting (target, error)) + return FALSE; + /* mark these as all erased */ - if (priv->dfuse_supported) + if (dfu_device_has_dfuse_support (priv->device)) g_hash_table_remove_all (priv->sectors_erased); /* download all elements in the image to the device */ @@ -2009,7 +1378,7 @@ dfu_target_get_commands (DfuTarget *target, guint8 buf[1]; /* invalid */ - if (!priv->dfuse_supported) { + if (!dfu_device_has_dfuse_support (priv->device)) { g_set_error_literal (error, DFU_ERROR, DFU_ERROR_NOT_SUPPORTED, @@ -2037,40 +1406,7 @@ dfu_target_get_commands (DfuTarget *target, #endif /** - * dfu_target_set_timeout: - * @target: a #DfuTarget - * @timeout_ms: the timeout in ms - * - * Sets the USB timeout to use when contacting the USB target. - * - * Since: 0.5.4 - **/ -void -dfu_target_set_timeout (DfuTarget *target, guint timeout_ms) -{ - DfuTargetPrivate *priv = GET_PRIVATE (target); - g_return_if_fail (DFU_IS_TARGET (target)); - priv->timeout_ms = timeout_ms; -} - -/** - * dfu_target_get_interface_number: - * @target: a #DfuTarget - * - * Gets the interface number. - * - * Since: 0.5.4 - **/ -guint8 -dfu_target_get_interface_number (DfuTarget *target) -{ - DfuTargetPrivate *priv = GET_PRIVATE (target); - g_return_val_if_fail (DFU_IS_TARGET (target), 0xff); - return priv->iface_number; -} - -/** - * dfu_target_get_interface_alt_setting: + * dfu_target_get_alt_setting: * @target: a #DfuTarget * * Gets the alternate setting to use for this interface. @@ -2080,15 +1416,15 @@ dfu_target_get_interface_number (DfuTarget *target) * Since: 0.5.4 **/ guint8 -dfu_target_get_interface_alt_setting (DfuTarget *target) +dfu_target_get_alt_setting (DfuTarget *target) { DfuTargetPrivate *priv = GET_PRIVATE (target); g_return_val_if_fail (DFU_IS_TARGET (target), 0xff); - return priv->iface_alt_setting; + return priv->alt_setting; } /** - * dfu_target_get_interface_alt_name: + * dfu_target_get_alt_name: * @target: a #DfuTarget * * Gets the alternate setting name to use for this interface. @@ -2098,9 +1434,23 @@ dfu_target_get_interface_alt_setting (DfuTarget *target) * Since: 0.5.4 **/ const gchar * -dfu_target_get_interface_alt_name (DfuTarget *target) +dfu_target_get_alt_name (DfuTarget *target, GError **error) { DfuTargetPrivate *priv = GET_PRIVATE (target); g_return_val_if_fail (DFU_IS_TARGET (target), NULL); - return priv->iface_alt_setting_name; + + /* ensure populated */ + if (!dfu_target_setup (target, error)) + return NULL; + + /* nothing */ + if (priv->alt_name == NULL) { + g_set_error_literal (error, + DFU_ERROR, + DFU_ERROR_NOT_FOUND, + "no alt-name"); + return FALSE; + } + + return priv->alt_name; } diff --git a/libdfu/dfu-target.h b/libdfu/dfu-target.h index 6f7ecde2a..0cf5a0235 100644 --- a/libdfu/dfu-target.h +++ b/libdfu/dfu-target.h @@ -38,20 +38,6 @@ struct _DfuTargetClass GUsbDeviceClass parent_class; }; -/** - * DfuTargetOpenFlags: - * @DFU_TARGET_OPEN_FLAG_NONE: No flags set - * @DFU_TARGET_OPEN_FLAG_NO_AUTO_REFRESH: Do not do the initial GET_STATUS - * - * The optional flags used for opening the target. - **/ -typedef enum { - DFU_TARGET_OPEN_FLAG_NONE = 0, - DFU_TARGET_OPEN_FLAG_NO_AUTO_REFRESH = (1 << 0), - /*< private >*/ - DFU_TARGET_OPEN_FLAG_LAST, -} DfuTargetOpenFlags; - /** * DfuTargetTransferFlags: * @DFU_TARGET_TRANSFER_FLAG_NONE: No flags set @@ -81,29 +67,9 @@ typedef void (*DfuProgressCallback) (DfuState state, goffset total, gpointer user_data); -gboolean dfu_target_open (DfuTarget *target, - DfuTargetOpenFlags flags, - GCancellable *cancellable, - GError **error); -gboolean dfu_target_close (DfuTarget *target, - GError **error); -DfuMode dfu_target_get_mode (DfuTarget *target); -DfuState dfu_target_get_state (DfuTarget *target); -DfuStatus dfu_target_get_status (DfuTarget *target); GPtrArray *dfu_target_get_sectors (DfuTarget *target); -gboolean dfu_target_can_upload (DfuTarget *target); -gboolean dfu_target_can_download (DfuTarget *target); -gboolean dfu_target_refresh (DfuTarget *target, - GCancellable *cancellable, - GError **error); -gboolean dfu_target_detach (DfuTarget *target, - GCancellable *cancellable, - GError **error); -gboolean dfu_target_abort (DfuTarget *target, - GCancellable *cancellable, - GError **error); -gboolean dfu_target_clear_status (DfuTarget *target, - GCancellable *cancellable, +guint8 dfu_target_get_alt_setting (DfuTarget *target); +const gchar *dfu_target_get_alt_name (DfuTarget *target, GError **error); DfuImage *dfu_target_upload (DfuTarget *target, DfuTargetTransferFlags flags, @@ -118,14 +84,6 @@ gboolean dfu_target_download (DfuTarget *target, DfuProgressCallback progress_cb, gpointer progress_cb_data, GError **error); -void dfu_target_set_timeout (DfuTarget *target, - guint timeout_ms); -guint8 dfu_target_get_interface_number (DfuTarget *target); -guint8 dfu_target_get_interface_alt_setting (DfuTarget *target); -const gchar *dfu_target_get_interface_alt_name (DfuTarget *target); -guint16 dfu_target_get_transfer_size (DfuTarget *target); -void dfu_target_set_transfer_size (DfuTarget *target, - guint16 transfer_size); G_END_DECLS diff --git a/libdfu/dfu-tool.c b/libdfu/dfu-tool.c index 2bdb8d077..39934eada 100644 --- a/libdfu/dfu-tool.c +++ b/libdfu/dfu-tool.c @@ -208,12 +208,29 @@ dfu_tool_get_defalt_device (DfuToolPrivate *priv, GError **error) /* we specified it manually */ if (priv->device_vid_pid != NULL) { DfuDevice *device; - g_auto(GStrv) vid_pid = NULL; + gchar *tmp; + guint64 pid; + guint64 vid; g_autoptr(GUsbDevice) usb_device = NULL; - /* split up */ - vid_pid = g_strsplit (priv->device_vid_pid, ":", -1); - if (g_strv_length (vid_pid) != 2) { + /* parse */ + vid = g_ascii_strtoull (priv->device_vid_pid, &tmp, 16); + if (vid == 0 || vid > G_MAXUINT16) { + g_set_error_literal (error, + DFU_ERROR, + DFU_ERROR_INTERNAL, + "Invalid format of VID:PID"); + return NULL; + } + if (tmp[0] != ':') { + g_set_error_literal (error, + DFU_ERROR, + DFU_ERROR_INTERNAL, + "Invalid format of VID:PID"); + return NULL; + } + pid = g_ascii_strtoull (tmp + 1, NULL, 16); + if (vid == 0 || vid > G_MAXUINT16) { g_set_error_literal (error, DFU_ERROR, DFU_ERROR_INTERNAL, @@ -223,8 +240,7 @@ dfu_tool_get_defalt_device (DfuToolPrivate *priv, GError **error) /* find device */ usb_device = g_usb_context_find_by_vid_pid (usb_ctx, - atoi (vid_pid[0]), - atoi (vid_pid[1]), + vid, pid, error); if (usb_device == NULL) return NULL; @@ -724,22 +740,16 @@ static gboolean dfu_tool_reset (DfuToolPrivate *priv, gchar **values, GError **error) { g_autoptr(DfuDevice) device = NULL; - g_autoptr(DfuTarget) target = NULL; device = dfu_tool_get_defalt_device (priv, error); if (device == NULL) return FALSE; - target = dfu_device_get_target_by_alt_setting (device, priv->alt_setting, error); - if (target == NULL) - return FALSE; - if (!dfu_target_open (target, - DFU_TARGET_OPEN_FLAG_NO_AUTO_REFRESH, + if (!dfu_device_open (device, + DFU_DEVICE_OPEN_FLAG_NO_AUTO_REFRESH, NULL, error)) return FALSE; - if (!dfu_device_reset (device, error)) return FALSE; - return TRUE; } @@ -841,23 +851,24 @@ dfu_tool_upload_target (DfuToolPrivate *priv, gchar **values, GError **error) device = dfu_tool_get_defalt_device (priv, error); if (device == NULL) return FALSE; - target = dfu_device_get_target_by_alt_setting (device, priv->alt_setting, error); - if (target == NULL) - return FALSE; - if (priv->transfer_size > 0) - dfu_target_set_transfer_size (target, priv->transfer_size); - if (!dfu_target_open (target, DFU_TARGET_OPEN_FLAG_NONE, NULL, error)) + dfu_device_set_transfer_size (device, priv->transfer_size); + if (!dfu_device_open (device, DFU_DEVICE_OPEN_FLAG_NONE, NULL, error)) return FALSE; /* APP -> DFU */ - if (dfu_target_get_mode (target) == DFU_MODE_RUNTIME) { + if (dfu_device_get_mode (device) == DFU_MODE_RUNTIME) { + g_debug ("detaching"); + if (!dfu_device_detach (device, NULL, error)) + return FALSE; if (!dfu_device_wait_for_replug (device, 5000, NULL, error)) return FALSE; - flags |= DFU_TARGET_TRANSFER_FLAG_BOOT_RUNTIME; } /* transfer */ + target = dfu_device_get_target_by_alt_setting (device, priv->alt_setting, error); + if (target == NULL) + return FALSE; helper.last_state = DFU_STATE_DFU_ERROR; helper.marks_total = 30; helper.marks_shown = 0; @@ -916,6 +927,8 @@ dfu_tool_upload (DfuToolPrivate *priv, gchar **values, GError **error) device = dfu_tool_get_defalt_device (priv, error); if (device == NULL) return FALSE; + if (!dfu_device_open (device, DFU_DEVICE_OPEN_FLAG_NONE, NULL, error)) + return FALSE; /* optional reset */ if (priv->reset) { @@ -1017,19 +1030,15 @@ dfu_tool_download_target (DfuToolPrivate *priv, gchar **values, GError **error) device = dfu_tool_get_defalt_device (priv, error); if (device == NULL) return FALSE; - target = dfu_device_get_target_by_alt_setting (device, priv->alt_setting, error); - if (target == NULL) - return FALSE; - if (priv->transfer_size > 0) - dfu_target_set_transfer_size (target, priv->transfer_size); - if (!dfu_target_open (target, DFU_TARGET_OPEN_FLAG_NONE, NULL, error)) + dfu_device_set_transfer_size (device, priv->transfer_size); + if (!dfu_device_open (device, DFU_DEVICE_OPEN_FLAG_NONE, NULL, error)) return FALSE; /* APP -> DFU */ - if (dfu_target_get_mode (target) == DFU_MODE_RUNTIME) { + if (dfu_device_get_mode (device) == DFU_MODE_RUNTIME) { g_debug ("detaching"); - if (!dfu_target_detach (target, NULL, error)) + if (!dfu_device_detach (device, NULL, error)) return FALSE; if (!dfu_device_wait_for_replug (device, 5000, NULL, error)) return FALSE; @@ -1077,6 +1086,9 @@ dfu_tool_download_target (DfuToolPrivate *priv, gchar **values, GError **error) } /* transfer */ + target = dfu_device_get_target_by_alt_setting (device, priv->alt_setting, error); + if (target == NULL) + return FALSE; helper.last_state = DFU_STATE_DFU_ERROR; helper.marks_total = 30; helper.marks_shown = 0; @@ -1125,6 +1137,8 @@ dfu_tool_download (DfuToolPrivate *priv, gchar **values, GError **error) device = dfu_tool_get_defalt_device (priv, error); if (device == NULL) return FALSE; + if (!dfu_device_open (device, DFU_DEVICE_OPEN_FLAG_NONE, NULL, error)) + return FALSE; /* print the new object */ str_debug = dfu_firmware_to_string (firmware); @@ -1179,61 +1193,35 @@ dfu_tool_print_indent (const gchar *title, const gchar *message, guint indent) static void dfu_tool_list_target (DfuTarget *target) { + GPtrArray *sectors; const gchar *tmp; - gboolean ret; guint i; g_autofree gchar *alt_id = NULL; g_autoptr(GError) error_local = NULL; /* TRANSLATORS: the identifier name please */ - alt_id = g_strdup_printf ("%i", dfu_target_get_interface_alt_setting (target)); + alt_id = g_strdup_printf ("%i", dfu_target_get_alt_setting (target)); dfu_tool_print_indent (_("ID"), alt_id, 1); - /* TRANSLATORS: device mode, e.g. runtime or DFU */ - dfu_tool_print_indent (_("Mode"), dfu_mode_to_string (dfu_target_get_mode (target)), 2); - tmp = dfu_target_get_interface_alt_name (target); + /* this is optional */ + tmp = dfu_target_get_alt_name (target, NULL); if (tmp != NULL) { /* TRANSLATORS: interface name, e.g. "Flash" */ dfu_tool_print_indent (_("Name"), tmp, 2); } - ret = dfu_target_open (target, - DFU_TARGET_OPEN_FLAG_NONE, - NULL, &error_local); - if (ret) { - GPtrArray *sectors; - tmp = dfu_status_to_string (dfu_target_get_status (target)); - /* TRANSLATORS: device status, e.g. "OK" */ - dfu_tool_print_indent (_("Status"), tmp, 2); - tmp = dfu_state_to_string (dfu_target_get_state (target)); - /* TRANSLATORS: device state, i.e. appIDLE */ - dfu_tool_print_indent (_("State"), tmp, 2); - - /* print sector information */ - sectors = dfu_target_get_sectors (target); - for (i = 0; i < sectors->len; i++) { - DfuSector *sector; - g_autofree gchar *msg = NULL; - g_autofree gchar *title = NULL; - sector = g_ptr_array_index (sectors, i); - msg = dfu_sector_to_string (sector); - /* TRANSLATORS: these are areas of memory on the chip */ - title = g_strdup_printf ("%s 0x%02x", _("Region"), i); - dfu_tool_print_indent (title, msg, 3); - } - } else { - if (g_error_matches (error_local, - DFU_ERROR, - DFU_ERROR_PERMISSION_DENIED)) { - /* TRANSLATORS: probably not run as root... */ - dfu_tool_print_indent (_("Status"), _("Unknown: permission denied"), 2); - } else { - /* TRANSLATORS: device has failed to report status */ - dfu_tool_print_indent (_("Status"), error_local->message, 2); - } + /* print sector information */ + sectors = dfu_target_get_sectors (target); + for (i = 0; i < sectors->len; i++) { + DfuSector *sector; + g_autofree gchar *msg = NULL; + g_autofree gchar *title = NULL; + sector = g_ptr_array_index (sectors, i); + msg = dfu_sector_to_string (sector); + /* TRANSLATORS: these are areas of memory on the chip */ + title = g_strdup_printf ("%s 0x%02x", _("Region"), i); + dfu_tool_print_indent (title, msg, 2); } - - dfu_target_close (target, NULL); } /** @@ -1252,11 +1240,13 @@ dfu_tool_list (DfuToolPrivate *priv, gchar **values, GError **error) g_usb_context_enumerate (usb_ctx); devices = g_usb_context_get_devices (usb_ctx); for (i = 0; i < devices->len; i++) { - g_autoptr(DfuDevice) device = NULL; GPtrArray *dfu_targets; DfuTarget *target; + const gchar *tmp; guint j; g_autofree gchar *version = NULL; + g_autoptr(DfuDevice) device = NULL; + g_autoptr(GError) error_local = NULL; usb_device = g_ptr_array_index (devices, i); g_debug ("PROBING [%04x:%04x]", @@ -1276,6 +1266,34 @@ dfu_tool_list (DfuToolPrivate *priv, gchar **values, GError **error) g_usb_device_get_pid (usb_device), version); + /* open */ + if (!dfu_device_open (device, + DFU_DEVICE_OPEN_FLAG_NONE, + NULL, &error_local)) { + if (g_error_matches (error_local, + DFU_ERROR, + DFU_ERROR_PERMISSION_DENIED)) { + /* TRANSLATORS: probably not run as root... */ + dfu_tool_print_indent (_("Status"), _("Unknown: permission denied"), 2); + } else { + /* TRANSLATORS: device has failed to report status */ + dfu_tool_print_indent (_("Status"), error_local->message, 2); + } + continue; + } + + tmp = dfu_mode_to_string (dfu_device_get_mode (device)); + /* TRANSLATORS: device mode, e.g. runtime or DFU */ + dfu_tool_print_indent (_("Mode"), tmp, 1); + + tmp = dfu_status_to_string (dfu_device_get_status (device)); + /* TRANSLATORS: device status, e.g. "OK" */ + dfu_tool_print_indent (_("Status"), tmp, 1); + + tmp = dfu_state_to_string (dfu_device_get_state (device)); + /* TRANSLATORS: device state, i.e. appIDLE */ + dfu_tool_print_indent (_("State"), tmp, 1); + /* list targets */ dfu_targets = dfu_device_get_targets (device); for (j = 0; j < dfu_targets->len; j++) { @@ -1293,22 +1311,18 @@ static gboolean dfu_tool_detach (DfuToolPrivate *priv, gchar **values, GError **error) { g_autoptr(DfuDevice) device = NULL; - g_autoptr(DfuTarget) target = NULL; /* open correct device */ device = dfu_tool_get_defalt_device (priv, error); if (device == NULL) return FALSE; - target = dfu_device_get_target_by_alt_setting (device, priv->alt_setting, error); - if (target == NULL) - return FALSE; if (priv->transfer_size > 0) - dfu_target_set_transfer_size (target, priv->transfer_size); - if (!dfu_target_open (target, DFU_TARGET_OPEN_FLAG_NONE, NULL, error)) - return FALSE; + dfu_device_set_transfer_size (device, priv->transfer_size); /* detatch */ - if (!dfu_target_detach (target, NULL, error)) + if (!dfu_device_open (device, DFU_DEVICE_OPEN_FLAG_NONE, NULL, error)) + return FALSE; + if (!dfu_device_detach (device, NULL, error)) return FALSE; return TRUE; } diff --git a/src/Makefile.am b/src/Makefile.am index 4b894dd6c..cf5c9f96c 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -34,7 +34,7 @@ FWUPD_LIBS = \ $(top_builddir)/libfwupd/libfwupd.la DFU_LIBS = \ - $(top_builddir)/libdfu/libdfu-private.la + $(top_builddir)/libdfu/libdfu.la bin_PROGRAMS = fwupdmgr diff --git a/src/fu-provider-usb.c b/src/fu-provider-usb.c index 49fde70a3..17661b06a 100644 --- a/src/fu-provider-usb.c +++ b/src/fu-provider-usb.c @@ -83,7 +83,6 @@ fu_provider_usb_device_add (FuProviderUsb *provider_usb, const gchar *id, GUsbDe g_autoptr(AsProfile) profile = as_profile_new (); g_autoptr(AsProfileTask) ptask = NULL; g_autoptr(DfuDevice) dfu_device = NULL; - g_autoptr(DfuTarget) dfu_target = NULL; /* get product */ idx = g_usb_device_get_product_index (device); @@ -135,20 +134,12 @@ fu_provider_usb_device_add (FuProviderUsb *provider_usb, const gchar *id, GUsbDe /* is there a DFU interface */ dfu_device = dfu_device_new (device); if (dfu_device != NULL) { - dfu_target = dfu_device_get_target_by_alt_setting (dfu_device, 0, NULL); - if (dfu_target != NULL) { - if (dfu_target_can_download (dfu_target)) { - fu_device_add_flag (dev, FU_DEVICE_FLAG_ALLOW_ONLINE); - fu_device_add_flag (dev, FU_DEVICE_FLAG_ALLOW_OFFLINE); - } else { - g_debug ("DFU device does not support downloading?!"); - /* FIXME: is the CH+ correct here? */ - fu_device_add_flag (dev, FU_DEVICE_FLAG_ALLOW_ONLINE); - fu_device_add_flag (dev, FU_DEVICE_FLAG_ALLOW_OFFLINE); - } - } else { - g_debug ("not a DFU device"); + if (dfu_device_can_download (dfu_device)) { + fu_device_add_flag (dev, FU_DEVICE_FLAG_ALLOW_ONLINE); + fu_device_add_flag (dev, FU_DEVICE_FLAG_ALLOW_OFFLINE); } + } else { + g_debug ("not a DFU device"); } /* good to go */ @@ -310,6 +301,15 @@ fu_provider_usb_update (FuProvider *provider, platform_id); return FALSE; } + if (!dfu_device_open (dfu_device, DFU_DEVICE_OPEN_FLAG_NONE, + NULL, &error_local)) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "failed to open DFU device %s: %s", + platform_id, error_local->message); + return FALSE; + } /* hit hardware */ dfu_firmware = dfu_firmware_new (); @@ -414,6 +414,15 @@ fu_provider_usb_verify (FuProvider *provider, platform_id); return FALSE; } + if (!dfu_device_open (dfu_device, DFU_DEVICE_OPEN_FLAG_NONE, + NULL, &error_local)) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "failed to open DFU device %s: %s", + platform_id, error_local->message); + return FALSE; + } /* get data from hardware */ dfu_firmware = dfu_device_upload (dfu_device,