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,