diff --git a/docs/libdfu/Makefile.am b/docs/libdfu/Makefile.am
index ef538d552..11fef2956 100644
--- a/docs/libdfu/Makefile.am
+++ b/docs/libdfu/Makefile.am
@@ -52,6 +52,7 @@ IGNORE_HFILES= \
dfu-device-private.h \
dfu-element-private.h \
dfu-image-private.h \
+ dfu-sector-private.h \
dfu-target-private.h
# Images to copy into HTML directory.
diff --git a/docs/libdfu/libdfu-docs.xml b/docs/libdfu/libdfu-docs.xml
index b36fecffa..1b92df966 100644
--- a/docs/libdfu/libdfu-docs.xml
+++ b/docs/libdfu/libdfu-docs.xml
@@ -80,6 +80,7 @@ if (!dfu_device_download (dfu_device, dfu_firmware,
+
diff --git a/libdfu/Makefile.am b/libdfu/Makefile.am
index e202f4760..16cb65288 100644
--- a/libdfu/Makefile.am
+++ b/libdfu/Makefile.am
@@ -34,6 +34,9 @@ libdfu_private_la_SOURCES = \
dfu-image.c \
dfu-image.h \
dfu-image-private.h \
+ dfu-sector.c \
+ dfu-sector.h \
+ dfu-sector-private.h \
dfu-target.c \
dfu-target.h \
dfu-target-private.h
diff --git a/libdfu/dfu-device.c b/libdfu/dfu-device.c
index 164a1dcd2..2b03a2118 100644
--- a/libdfu/dfu-device.c
+++ b/libdfu/dfu-device.c
@@ -603,7 +603,6 @@ dfu_device_reset (DfuDevice *device, GError **error)
/**
* dfu_device_upload:
* @device: a #DfuDevice
- * @expected_size: the expected size of the firmware, or 0 for unknown
* @flags: flags to use, e.g. %DFU_TARGET_TRANSFER_FLAG_VERIFY
* @cancellable: a #GCancellable, or %NULL
* @progress_cb: a #GFileProgressCallback, or %NULL
@@ -618,7 +617,6 @@ dfu_device_reset (DfuDevice *device, GError **error)
**/
DfuFirmware *
dfu_device_upload (DfuDevice *device,
- gsize expected_size,
DfuTargetTransferFlags flags,
GCancellable *cancellable,
DfuProgressCallback progress_cb,
@@ -646,25 +644,30 @@ dfu_device_upload (DfuDevice *device,
dfu_firmware_set_release (firmware, 0xffff);
/* APP -> DFU */
- if (flags & DFU_TARGET_TRANSFER_FLAG_DETACH) {
- 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) {
- g_debug ("detaching");
-
- /* inform UI there's going to be a detach:attach */
- if (progress_cb != NULL) {
- progress_cb (DFU_STATE_APP_DETACH, 0, 0,
- progress_cb_data);
- }
-
- /* detach and USB reset */
- if (!dfu_target_detach (target_default, NULL, error))
- return NULL;
- if (!dfu_device_wait_for_replug (device, 5000, NULL, error))
- return NULL;
+ 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 ((flags & DFU_TARGET_TRANSFER_FLAG_DETACH) == 0) {
+ g_set_error (error,
+ DFU_ERROR,
+ DFU_ERROR_NOT_SUPPORTED,
+ "device is not in DFU mode");
+ return FALSE;
}
+ g_debug ("detaching");
+
+ /* inform UI there's going to be a detach:attach */
+ if (progress_cb != NULL) {
+ progress_cb (DFU_STATE_APP_DETACH, 0, 0,
+ progress_cb_data);
+ }
+
+ /* detach and USB reset */
+ if (!dfu_target_detach (target_default, NULL, error))
+ return NULL;
+ if (!dfu_device_wait_for_replug (device, 5000, NULL, error))
+ return NULL;
}
/* upload from each target */
@@ -673,7 +676,12 @@ dfu_device_upload (DfuDevice *device,
DfuTarget *target;
g_autoptr(DfuImage) image = NULL;
target = g_ptr_array_index (targets, i);
- image = dfu_target_upload (target, 0,
+ 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,
progress_cb,
@@ -811,7 +819,18 @@ dfu_device_download (DfuDevice *device,
}
/* APP -> DFU */
- if (flags & DFU_TARGET_TRANSFER_FLAG_DETACH) {
+ /* 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 ((flags & DFU_TARGET_TRANSFER_FLAG_DETACH) == 0) {
+ g_set_error (error,
+ DFU_ERROR,
+ DFU_ERROR_NOT_SUPPORTED,
+ "device is not in DFU mode");
+ return FALSE;
+ }
/* inform UI there's going to be a detach:attach */
if (progress_cb != NULL) {
@@ -819,17 +838,11 @@ dfu_device_download (DfuDevice *device,
progress_cb_data);
}
- /* detach and USB reset */
- target_default = dfu_device_get_target_default (device, error);
- if (target_default == NULL)
+ g_debug ("detaching");
+ if (!dfu_target_detach (target_default, NULL, error))
+ return FALSE;
+ if (!dfu_device_wait_for_replug (device, 5000, NULL, error))
return FALSE;
- if (dfu_target_get_mode (target_default) == DFU_MODE_RUNTIME) {
- g_debug ("detaching");
- if (!dfu_target_detach (target_default, NULL, error))
- return FALSE;
- if (!dfu_device_wait_for_replug (device, 5000, NULL, error))
- return FALSE;
- }
}
/* download each target */
diff --git a/libdfu/dfu-device.h b/libdfu/dfu-device.h
index a862031ee..2a0ebced2 100644
--- a/libdfu/dfu-device.h
+++ b/libdfu/dfu-device.h
@@ -62,7 +62,6 @@ gboolean dfu_device_wait_for_replug (DfuDevice *device,
GCancellable *cancellable,
GError **error);
DfuFirmware *dfu_device_upload (DfuDevice *device,
- gsize expected_size,
DfuTargetTransferFlags flags,
GCancellable *cancellable,
DfuProgressCallback progress_cb,
diff --git a/libdfu/dfu-firmware.c b/libdfu/dfu-firmware.c
index 01827aefc..ac020e0d8 100644
--- a/libdfu/dfu-firmware.c
+++ b/libdfu/dfu-firmware.c
@@ -928,7 +928,13 @@ dfu_firmware_write_data (DfuFirmware *firmware, GError **error)
image = dfu_firmware_get_image_default (firmware);
g_assert (image != NULL);
element = dfu_image_get_element (image, 0);
- g_assert (element != NULL);
+ if (element == NULL) {
+ g_set_error (error,
+ DFU_ERROR,
+ DFU_ERROR_NOT_FOUND,
+ "no firmware element data to write");
+ return NULL;
+ }
contents = dfu_element_get_contents (element);
return g_bytes_ref (contents);
}
@@ -940,7 +946,13 @@ dfu_firmware_write_data (DfuFirmware *firmware, GError **error)
image = dfu_firmware_get_image_default (firmware);
g_assert (image != NULL);
element = dfu_image_get_element (image, 0);
- g_assert (element != NULL);
+ if (element == NULL) {
+ g_set_error (error,
+ DFU_ERROR,
+ DFU_ERROR_NOT_FOUND,
+ "no firmware element data to write");
+ return NULL;
+ }
contents = dfu_element_get_contents (element);
g_assert (contents != NULL);
return dfu_firmware_add_footer (firmware, contents);
diff --git a/libdfu/dfu-sector-private.h b/libdfu/dfu-sector-private.h
new file mode 100644
index 000000000..fdd6e74be
--- /dev/null
+++ b/libdfu/dfu-sector-private.h
@@ -0,0 +1,38 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2015 Richard Hughes
+ *
+ * Licensed under the GNU Lesser General Public License Version 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef __DFU_SECTOR_PRIVATE_H
+#define __DFU_SECTOR_PRIVATE_H
+
+#include "dfu-sector.h"
+
+G_BEGIN_DECLS
+
+DfuSector *dfu_sector_new (guint32 address,
+ guint32 size,
+ guint32 size_left,
+ guint16 zone,
+ guint16 number,
+ DfuSectorCap cap);
+
+G_END_DECLS
+
+#endif /* __DFU_SECTOR_PRIVATE_H */
diff --git a/libdfu/dfu-sector.c b/libdfu/dfu-sector.c
new file mode 100644
index 000000000..064794e8b
--- /dev/null
+++ b/libdfu/dfu-sector.c
@@ -0,0 +1,229 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2015 Richard Hughes
+ *
+ * Licensed under the GNU Lesser General Public License Version 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/**
+ * SECTION:dfu-sector
+ * @short_description: Object representing a sector on a chip
+ *
+ * This object represents an sector of memory at a specific address on the
+ * device itself.
+ *
+ * This allows relocatable data segments to be stored in different
+ * locations on the device itself.
+ *
+ * You can think of these objects as flash segments on devices, where a
+ * complete block can be erased and then written to.
+ *
+ * See also: #DfuElement
+ */
+
+#include "config.h"
+
+#include
+#include
+
+#include "dfu-common.h"
+#include "dfu-sector-private.h"
+
+/**
+ * DfuSectorPrivate:
+ *
+ * Private #DfuSector data
+ **/
+typedef struct {
+ guint32 address;
+ guint32 size;
+ guint32 size_left;
+ guint16 zone;
+ guint16 number;
+ DfuSectorCap cap;
+} DfuSectorPrivate;
+
+G_DEFINE_TYPE_WITH_PRIVATE (DfuSector, dfu_sector, G_TYPE_OBJECT)
+#define GET_PRIVATE(o) (dfu_sector_get_instance_private (o))
+
+/**
+ * dfu_sector_class_init:
+ **/
+static void
+dfu_sector_class_init (DfuSectorClass *klass)
+{
+}
+
+/**
+ * dfu_sector_init:
+ **/
+static void
+dfu_sector_init (DfuSector *sector)
+{
+}
+
+/**
+ * dfu_sector_new: (skip)
+ * address: the address for the sector
+ * size: the size of this sector
+ * size_left: the size of the rest of the sector
+ * zone: the zone of memory the setor belongs
+ * number: the sector number in the zone
+ * cap: the #DfuSectorCap
+ *
+ * Creates a new DFU sector object.
+ *
+ * Return value: a new #DfuSector
+ *
+ * Since: 0.5.4
+ **/
+DfuSector *
+dfu_sector_new (guint32 address, guint32 size, guint32 size_left,
+ guint16 zone, guint16 number, DfuSectorCap cap)
+{
+ DfuSectorPrivate *priv;
+ DfuSector *sector;
+ sector = g_object_new (DFU_TYPE_SECTOR, NULL);
+ priv = GET_PRIVATE (sector);
+ priv->address = address;
+ priv->size = size;
+ priv->size_left = size_left;
+ priv->zone = zone;
+ priv->number = number;
+ priv->cap = cap;
+ return sector;
+}
+
+/**
+ * dfu_sector_get_address:
+ * @sector: a #DfuSector
+ *
+ * Gets the alternate setting.
+ *
+ * Return value: integer, or 0x00 for unset
+ *
+ * Since: 0.5.4
+ **/
+guint32
+dfu_sector_get_address (DfuSector *sector)
+{
+ DfuSectorPrivate *priv = GET_PRIVATE (sector);
+ g_return_val_if_fail (DFU_IS_SECTOR (sector), 0x00);
+ return priv->address;
+}
+
+/**
+ * dfu_sector_get_size:
+ * @sector: a #DfuSector
+ *
+ * Gets the alternate setting.
+ *
+ * Return value: integer, or 0x00 for unset
+ *
+ * Since: 0.5.4
+ **/
+guint32
+dfu_sector_get_size (DfuSector *sector)
+{
+ DfuSectorPrivate *priv = GET_PRIVATE (sector);
+ g_return_val_if_fail (DFU_IS_SECTOR (sector), 0x00);
+ return priv->size;
+}
+
+/**
+ * dfu_sector_get_size_left:
+ * @sector: a #DfuSector
+ *
+ * Gets the alternate setting.
+ *
+ * Return value: integer, or 0x00 for unset
+ *
+ * Since: 0.5.4
+ **/
+guint32
+dfu_sector_get_size_left (DfuSector *sector)
+{
+ DfuSectorPrivate *priv = GET_PRIVATE (sector);
+ g_return_val_if_fail (DFU_IS_SECTOR (sector), 0x00);
+ return priv->size_left;
+}
+
+/**
+ * dfu_sector_get_id:
+ * @sector: a #DfuSector
+ *
+ * Gets the sector ID which is a combination of the zone and sector number.
+ * You can use this number to check if the segment is the 'same' as the last
+ * written or read sector.
+ *
+ * Return value: integer ID, or 0x00 for unset
+ *
+ * Since: 0.5.4
+ **/
+guint32
+dfu_sector_get_id (DfuSector *sector)
+{
+ DfuSectorPrivate *priv = GET_PRIVATE (sector);
+ g_return_val_if_fail (DFU_IS_SECTOR (sector), 0x00);
+ return (((guint32) priv->zone) << 16) | priv->number;
+}
+
+/**
+ * dfu_sector_has_cap:
+ * @sector: a #DfuSector
+ * @cap: a #DfuSectorCap, e.g. %DFU_SECTOR_CAP_ERASEABLE
+ *
+ * Finds out if the sector has the required capability.
+ *
+ * Return value: %TRUE if the sector has the capabilily
+ *
+ * Since: 0.5.4
+ **/
+gboolean
+dfu_sector_has_cap (DfuSector *sector, DfuSectorCap cap)
+{
+ DfuSectorPrivate *priv = GET_PRIVATE (sector);
+ g_return_val_if_fail (DFU_IS_SECTOR (sector), FALSE);
+ return priv->cap & cap;
+}
+
+/**
+ * dfu_sector_to_string:
+ * @sector: a #DfuSector
+ *
+ * Returns a string representaiton of the object.
+ *
+ * Return value: NULL terminated string, or %NULL for invalid
+ *
+ * Since: 0.5.4
+ **/
+gchar *
+dfu_sector_to_string (DfuSector *sector)
+{
+ DfuSectorPrivate *priv = GET_PRIVATE (sector);
+ GString *str;
+
+ g_return_val_if_fail (DFU_IS_SECTOR (sector), NULL);
+
+ str = g_string_new ("");
+ g_string_append_printf (str,
+ "Zone:%i, Sec#:%i, Addr:0x%08x, "
+ "Size:0x%04x, Caps:0x%01x",
+ priv->zone, priv->number, priv->address,
+ priv->size, priv->cap);
+ return g_string_free (str, FALSE);
+}
diff --git a/libdfu/dfu-sector.h b/libdfu/dfu-sector.h
new file mode 100644
index 000000000..794e8ad14
--- /dev/null
+++ b/libdfu/dfu-sector.h
@@ -0,0 +1,66 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2015 Richard Hughes
+ *
+ * Licensed under the GNU Lesser General Public License Version 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef __DFU_SECTOR_H
+#define __DFU_SECTOR_H
+
+#include
+#include
+
+G_BEGIN_DECLS
+
+#define DFU_TYPE_SECTOR (dfu_sector_get_type ())
+G_DECLARE_DERIVABLE_TYPE (DfuSector, dfu_sector, DFU, SECTOR, GObject)
+
+struct _DfuSectorClass
+{
+ GObjectClass parent_class;
+};
+
+/**
+ * DfuSectorCap:
+ * @DFU_SECTOR_CAP_NONE: No operations possible
+ * @DFU_SECTOR_CAP_READABLE: Sector can be read
+ * @DFU_SECTOR_CAP_WRITEABLE: Sector can be written
+ * @DFU_SECTOR_CAP_ERASEABLE: Sector can be erased
+ *
+ * The flags indicating what the sector can do.
+ **/
+typedef enum {
+ DFU_SECTOR_CAP_NONE = 0,
+ DFU_SECTOR_CAP_READABLE = 1 << 0,
+ DFU_SECTOR_CAP_WRITEABLE = 1 << 1,
+ DFU_SECTOR_CAP_ERASEABLE = 1 << 2,
+ /*< private >*/
+ DFU_SECTOR_CAP_LAST
+} DfuSectorCap;
+
+guint32 dfu_sector_get_id (DfuSector *sector);
+guint32 dfu_sector_get_address (DfuSector *sector);
+guint32 dfu_sector_get_size (DfuSector *sector);
+guint32 dfu_sector_get_size_left (DfuSector *sector);
+gboolean dfu_sector_has_cap (DfuSector *sector,
+ DfuSectorCap cap);
+gchar *dfu_sector_to_string (DfuSector *sector);
+
+G_END_DECLS
+
+#endif /* __DFU_SECTOR_H */
diff --git a/libdfu/dfu-self-test.c b/libdfu/dfu-self-test.c
index f1e99b6d5..12c9320d5 100644
--- a/libdfu/dfu-self-test.c
+++ b/libdfu/dfu-self-test.c
@@ -28,7 +28,8 @@
#include "dfu-device.h"
#include "dfu-error.h"
#include "dfu-firmware.h"
-#include "dfu-target.h"
+#include "dfu-sector-private.h"
+#include "dfu-target-private.h"
/**
* dfu_test_get_filename:
@@ -322,6 +323,7 @@ dfu_device_func (void)
static void
dfu_colorhug_plus_func (void)
{
+ GPtrArray *elements;
gboolean ret;
gboolean seen_app_idle = FALSE;
g_autoptr(DfuDevice) device = NULL;
@@ -425,10 +427,13 @@ dfu_colorhug_plus_func (void)
}
/* get a dump of the existing firmware */
- image = dfu_target_upload (target, 0, DFU_TARGET_TRANSFER_FLAG_NONE,
+ 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_cmpint (elements->len, ==, 1);
/* download a new firmware */
ret = dfu_target_download (target, image,
@@ -460,6 +465,102 @@ dfu_colorhug_plus_func (void)
g_assert (ret);
}
+/**
+ * _dfu_target_sectors_to_string:
+ **/
+static gchar *
+_dfu_target_sectors_to_string (DfuTarget *target)
+{
+ DfuSector *sector;
+ GPtrArray *sectors;
+ GString *str;
+ guint i;
+
+ str = g_string_new ("");
+ sectors = dfu_target_get_sectors (target);
+ for (i = 0; i < sectors->len; i++) {
+ g_autofree gchar *tmp = NULL;
+ sector = g_ptr_array_index (sectors, i);
+ tmp = dfu_sector_to_string (sector);
+ g_string_append_printf (str, "%s\n", tmp);
+ }
+ if (str->len > 0)
+ g_string_truncate (str, str->len - 1);
+ return g_string_free (str, FALSE);
+}
+
+static void
+dfu_target_dfuse_func (void)
+{
+ gboolean ret;
+ gchar *tmp;
+ g_autoptr(DfuTarget) target = NULL;
+ g_autoptr(GError) error = NULL;
+
+ /* NULL */
+ target = g_object_new (DFU_TYPE_TARGET, NULL);
+ ret = dfu_target_parse_sectors (target, NULL, &error);
+ g_assert_no_error (error);
+ g_assert (ret);
+ tmp = _dfu_target_sectors_to_string (target);
+ g_assert_cmpstr (tmp, ==, "");
+ g_free (tmp);
+
+ /* no addresses */
+ ret = dfu_target_parse_sectors (target, "@Flash3", &error);
+ g_assert_no_error (error);
+ g_assert (ret);
+ tmp = _dfu_target_sectors_to_string (target);
+ g_assert_cmpstr (tmp, ==, "");
+ g_free (tmp);
+
+ /* one sector, no space */
+ 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);
+ 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);
+
+ /* multiple sectors */
+ 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);
+ 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"
+ "Zone:0, Sec#:1, Addr:0x08000400, Size:0x0400, Caps:0x7\n"
+ "Zone:0, Sec#:1, Addr:0x08000800, Size:0x0400, Caps:0x7\n"
+ "Zone:0, Sec#:1, Addr:0x08000c00, Size:0x0400, Caps:0x7");
+ g_free (tmp);
+
+ /* non-contiguous */
+ 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);
+ 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"
+ "Zone:0, Sec#:0, Addr:0x0000f12c, Size:0x0064, Caps:0x1\n"
+ "Zone:1, Sec#:0, Addr:0x0000e000, Size:0x2000, Caps:0x7\n"
+ "Zone:1, Sec#:0, Addr:0x00010000, Size:0x2000, Caps:0x7\n"
+ "Zone:1, Sec#:0, Addr:0x00012000, Size:0x2000, Caps:0x7\n"
+ "Zone:2, Sec#:0, Addr:0x00080000, Size:0x6000, Caps:0x7\n"
+ "Zone:2, Sec#:0, Addr:0x00086000, Size:0x6000, Caps:0x7");
+ g_free (tmp);
+
+ /* invalid */
+ ret = dfu_target_parse_sectors (target, "Flash", NULL);
+ g_assert (ret);
+ ret = dfu_target_parse_sectors (target, "@Internal Flash /0x08000000", NULL);
+ g_assert (!ret);
+ ret = dfu_target_parse_sectors (target, "@Internal Flash /0x08000000/12*001a", NULL);
+ g_assert (!ret);
+}
+
int
main (int argc, char **argv)
{
@@ -470,6 +571,7 @@ main (int argc, char **argv)
/* tests go here */
g_test_add_func ("/libdfu/enums", dfu_enums_func);
+ g_test_add_func ("/libdfu/target(DfuSe}", dfu_target_dfuse_func);
g_test_add_func ("/libdfu/firmware{raw}", dfu_firmware_raw_func);
g_test_add_func ("/libdfu/firmware{dfu}", dfu_firmware_dfu_func);
g_test_add_func ("/libdfu/firmware{dfuse}", dfu_firmware_dfuse_func);
diff --git a/libdfu/dfu-target-private.h b/libdfu/dfu-target-private.h
index 46ee19e93..f0f05180b 100644
--- a/libdfu/dfu-target-private.h
+++ b/libdfu/dfu-target-private.h
@@ -36,6 +36,11 @@ gboolean _dfu_target_update (DfuTarget *target,
GCancellable *cancellable,
GError **error);
+/* export this just for the self tests */
+gboolean dfu_target_parse_sectors (DfuTarget *target,
+ const gchar *alt_name,
+ GError **error);
+
G_END_DECLS
#endif /* __DFU_TARGET_PRIVATE_H */
diff --git a/libdfu/dfu-target.c b/libdfu/dfu-target.c
index 68fd2aa93..2e32a4ccd 100644
--- a/libdfu/dfu-target.c
+++ b/libdfu/dfu-target.c
@@ -41,6 +41,7 @@
#include "dfu-common.h"
#include "dfu-device-private.h"
#include "dfu-error.h"
+#include "dfu-sector-private.h"
#include "dfu-target-private.h"
static void dfu_target_finalize (GObject *object);
@@ -64,6 +65,13 @@ typedef enum {
DFU_QUIRK_LAST
} DfuQuirks;
+typedef enum {
+ DFU_CMD_DFUSE_GET_COMMAND = 0x00,
+ DFU_CMD_DFUSE_SET_ADDRESS_POINTER = 0x21,
+ DFU_CMD_DFUSE_ERASE = 0x41,
+ DFU_CMD_DFUSE_READ_UNPROTECT = 0x92,
+} DfuCmdDfuse;
+
/**
* DfuTargetPrivate:
*
@@ -85,6 +93,8 @@ typedef struct {
guint timeout_ms;
DfuAttributes attributes;
DfuQuirks quirks;
+ GPtrArray *sectors; /* of DfuSector */
+ GHashTable *sectors_erased; /* of DfuSector:1 */
} DfuTargetPrivate;
G_DEFINE_TYPE_WITH_PRIVATE (DfuTarget, dfu_target, G_TYPE_OBJECT)
@@ -111,6 +121,8 @@ dfu_target_init (DfuTarget *target)
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);
}
/**
@@ -123,12 +135,255 @@ dfu_target_finalize (GObject *object)
DfuTargetPrivate *priv = GET_PRIVATE (target);
g_free (priv->iface_alt_setting_name);
+ g_ptr_array_unref (priv->sectors);
+ g_hash_table_unref (priv->sectors_erased);
if (priv->device != NULL)
g_object_unref (priv->device);
G_OBJECT_CLASS (dfu_target_parent_class)->finalize (object);
}
+/**
+ * dfu_target_sectors_to_string:
+ **/
+static gchar *
+dfu_target_sectors_to_string (DfuTarget *target)
+{
+ DfuTargetPrivate *priv = GET_PRIVATE (target);
+ DfuSector *sector;
+ GString *str;
+ guint i;
+
+ str = g_string_new ("");
+ for (i = 0; i < priv->sectors->len; i++) {
+ g_autofree gchar *tmp = NULL;
+ sector = g_ptr_array_index (priv->sectors, i);
+ tmp = dfu_sector_to_string (sector);
+ g_string_append_printf (str, "%s\n", tmp);
+ }
+ if (str->len > 0)
+ g_string_truncate (str, str->len - 1);
+ return g_string_free (str, FALSE);
+}
+
+/**
+ * dfu_target_get_sector_for_addr:
+ *
+ * Returns: the sector that should be used for a specific address, or %NULL
+ **/
+static DfuSector *
+dfu_target_get_sector_for_addr (DfuTarget *target, guint32 addr)
+{
+ DfuTargetPrivate *priv = GET_PRIVATE (target);
+ DfuSector *sector;
+ guint i;
+
+ for (i = 0; i < priv->sectors->len; i++) {
+ sector = g_ptr_array_index (priv->sectors, i);
+ if (addr < dfu_sector_get_address (sector))
+ continue;
+ if (addr > dfu_sector_get_address (sector) +
+ dfu_sector_get_size (sector))
+ continue;
+ return sector;
+ }
+ return NULL;
+}
+
+/**
+ * dfu_target_parse_sector:
+ *
+ * Parse the DfuSe sector format according to UM0424
+ **/
+static gboolean
+dfu_target_parse_sector (DfuTarget *target,
+ const gchar *dfuse_sector_id,
+ guint32 addr,
+ guint zone,
+ guint number,
+ GError **error)
+{
+ DfuTargetPrivate *priv = GET_PRIVATE (target);
+ DfuSectorCap cap = DFU_SECTOR_CAP_NONE;
+ gchar *tmp;
+ guint32 addr_offset = 0;
+ guint64 nr_sectors;
+ guint64 sector_size;
+ guint i;
+
+ /* parse # of sectors */
+ nr_sectors = g_ascii_strtoull (dfuse_sector_id, &tmp, 10);
+ if (nr_sectors > 999) {
+ g_set_error (error,
+ DFU_ERROR,
+ DFU_ERROR_NOT_SUPPORTED,
+ "Invalid number of sectors: %s",
+ dfuse_sector_id);
+ return FALSE;
+ }
+
+ /* check this is the delimiter */
+ if (tmp[0] != '*') {
+ g_set_error (error,
+ DFU_ERROR,
+ DFU_ERROR_NOT_SUPPORTED,
+ "Invalid sector ID: %s",
+ dfuse_sector_id);
+ return FALSE;
+ }
+
+ /* parse sector size */
+ sector_size = g_ascii_strtoull (tmp + 1, &tmp, 10);
+ if (sector_size > 999) {
+ g_set_error (error,
+ DFU_ERROR,
+ DFU_ERROR_NOT_SUPPORTED,
+ "Invalid sector size: %s",
+ dfuse_sector_id);
+ return FALSE;
+ }
+
+ /* optional space */
+ if (tmp[0] == ' ')
+ tmp++;
+
+ /* get multiplier */
+ switch (tmp[0]) {
+ case 'B': /* byte */
+ break;
+ case 'K': /* Kilo */
+ sector_size *= 0x400;
+ break;
+ case 'M': /* Mega */
+ sector_size *= 0x100000 ;
+ break;
+ default:
+ g_set_error (error,
+ DFU_ERROR,
+ DFU_ERROR_NOT_SUPPORTED,
+ "Invalid sector multiplier: %s",
+ tmp);
+ return FALSE;
+ }
+
+ /* get sector type */
+ switch (tmp[1]) {
+ case 'a':
+ cap = DFU_SECTOR_CAP_READABLE;
+ break;
+ case 'b':
+ cap = DFU_SECTOR_CAP_ERASEABLE;
+ break;
+ case 'c':
+ cap = DFU_SECTOR_CAP_READABLE |
+ DFU_SECTOR_CAP_ERASEABLE;
+ break;
+ case 'd':
+ cap = DFU_SECTOR_CAP_WRITEABLE;
+ break;
+ case 'e':
+ cap = DFU_SECTOR_CAP_READABLE |
+ DFU_SECTOR_CAP_WRITEABLE;
+ break;
+ case 'f':
+ cap = DFU_SECTOR_CAP_ERASEABLE |
+ DFU_SECTOR_CAP_WRITEABLE;
+ break;
+ case 'g':
+ cap = DFU_SECTOR_CAP_READABLE |
+ DFU_SECTOR_CAP_ERASEABLE |
+ DFU_SECTOR_CAP_WRITEABLE;
+ break;
+ default:
+ g_set_error (error,
+ DFU_ERROR,
+ DFU_ERROR_NOT_SUPPORTED,
+ "Invalid sector type: %s",
+ tmp);
+ return FALSE;
+ }
+
+ /* add all the sectors */
+ for (i = 0; i < nr_sectors; i++) {
+ DfuSector *sector;
+ sector = dfu_sector_new (addr + addr_offset,
+ sector_size,
+ (nr_sectors * sector_size)- addr_offset,
+ zone,
+ number,
+ cap);
+ g_ptr_array_add (priv->sectors, sector);
+ addr_offset += dfu_sector_get_size (sector);
+ }
+ return TRUE;
+}
+
+/**
+ * dfu_target_parse_sectors: (skip)
+ *
+ * Parse the DfuSe format according to UM0424
+ **/
+gboolean
+dfu_target_parse_sectors (DfuTarget *target, const gchar *alt_name, GError **error)
+{
+ DfuTargetPrivate *priv = GET_PRIVATE (target);
+ guint i;
+ guint j;
+ g_autofree gchar *str_debug = NULL;
+ g_auto(GStrv) zones = NULL;
+
+ /* not set */
+ if (alt_name == NULL)
+ return TRUE;
+
+ /* not a DfuSe alternative name */
+ if (alt_name[0] != '@')
+ return TRUE;
+
+ /* clear any existing zones */
+ g_ptr_array_set_size (priv->sectors, 0);
+
+ /* parse zones */
+ zones = g_strsplit (alt_name, "/", -1);
+ 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 */
+ if (!g_str_has_prefix (zones[i], "0x"))
+ return FALSE;
+ addr = g_ascii_strtoull (zones[i] + 2, NULL, 16);
+ if (addr > G_MAXUINT32)
+ return FALSE;
+
+ /* no sectors?! */
+ if (zones[i+1] == NULL) {
+ g_set_error_literal (error,
+ DFU_ERROR,
+ DFU_ERROR_NOT_SUPPORTED,
+ "No sector section");
+ return FALSE;
+ }
+
+ /* parse sectors */
+ sectors = g_strsplit (zones[i+1], ",", -1);
+ for (j = 0; sectors[j] != NULL; j++) {
+ if (!dfu_target_parse_sector (target,
+ sectors[j],
+ addr,
+ (i - 1) / 2, j,
+ error))
+ return FALSE;
+ }
+ }
+
+ /* success */
+ str_debug = dfu_target_sectors_to_string (target);
+ g_debug ("%s", str_debug);
+ return TRUE;
+}
+
typedef struct __attribute__((packed)) {
guint8 bLength;
guint8 bDescriptorType;
@@ -313,6 +568,24 @@ _dfu_target_new (DfuDevice *device, GUsbInterface *iface)
return target;
}
+/**
+ * dfu_target_get_sectors:
+ * @target: a #GUsbDevice
+ *
+ * Gets the sectors exported by the device.
+ *
+ * Return value: (transfer none) (element-type DfuSector): sectors
+ *
+ * Since: 0.5.4
+ **/
+GPtrArray *
+dfu_target_get_sectors (DfuTarget *target)
+{
+ DfuTargetPrivate *priv = GET_PRIVATE (target);
+ g_return_val_if_fail (DFU_IS_TARGET (target), NULL);
+ return priv->sectors;
+}
+
/**
* dfu_target_get_mode:
* @target: a #GUsbDevice
@@ -438,6 +711,96 @@ dfu_target_set_transfer_size (DfuTarget *target, guint16 transfer_size)
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:
+ **/
+static gboolean
+dfu_target_check_status (DfuTarget *target,
+ GCancellable *cancellable,
+ GError **error)
+{
+ DfuTargetPrivate *priv = GET_PRIVATE (target);
+
+ /* get the status */
+ if (!dfu_target_refresh (target, cancellable, error))
+ return FALSE;
+
+ /* not in an error state */
+ if (priv->state != DFU_STATE_DFU_ERROR)
+ return TRUE;
+
+ /* read protection */
+ if (priv->dfuse_supported) {
+ if (priv->status == 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) {
+ g_set_error (error,
+ DFU_ERROR,
+ DFU_ERROR_NOT_SUPPORTED,
+ "failed, address is wrong or unsupported");
+ return FALSE;
+ }
+ }
+
+ /* prefix the error */
+ g_set_error (error,
+ DFU_ERROR,
+ DFU_ERROR_NOT_SUPPORTED,
+ "failed, state:%s status:%s]: ",
+ dfu_state_to_string (priv->state),
+ dfu_status_to_string (priv->status));
+ return FALSE;
+}
+
/**
* dfu_target_open:
* @target: a #DfuTarget
@@ -529,6 +892,26 @@ dfu_target_open (DfuTarget *target, DfuTargetOpenFlags flags,
}
}
+ /* parse the DfuSe format according to UM0424 */
+ if (!dfu_target_parse_sectors (target,
+ priv->iface_alt_setting_name,
+ error))
+ return FALSE;
+
+ /* add a dummy entry */
+ if (priv->sectors->len == 0) {
+ DfuSector *sector;
+ sector = dfu_sector_new (0x0, /* addr */
+ 0x0, /* size */
+ 0x0, /* size_left */
+ 0x0, /* zone */
+ 0x0, /* number */
+ DFU_SECTOR_CAP_READABLE |
+ DFU_SECTOR_CAP_WRITEABLE);
+ g_debug ("no UM0424 sector descripton, so adding dummy");
+ g_ptr_array_add (priv->sectors, sector);
+ }
+
return TRUE;
}
@@ -672,6 +1055,8 @@ dfu_target_detach (DfuTarget *target, GCancellable *cancellable, GError **error)
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,
@@ -721,6 +1106,8 @@ dfu_target_abort (DfuTarget *target, GCancellable *cancellable, GError **error)
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,
@@ -728,6 +1115,7 @@ dfu_target_abort (DfuTarget *target, GCancellable *cancellable, GError **error)
error_local->message);
return FALSE;
}
+
return TRUE;
}
@@ -740,7 +1128,7 @@ dfu_target_abort (DfuTarget *target, GCancellable *cancellable, GError **error)
*
* Updates the target with new interface data. This only needs to be
* done after the device has been reset.
- *
+ *
* Returns: %TRUE for success
**/
gboolean
@@ -809,6 +1197,8 @@ dfu_target_clear_status (DfuTarget *target, GCancellable *cancellable, GError **
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,
@@ -819,6 +1209,240 @@ dfu_target_clear_status (DfuTarget *target, GCancellable *cancellable, GError **
return TRUE;
}
+/**
+ * dfu_target_download_chunk:
+ **/
+static gboolean
+dfu_target_download_chunk (DfuTarget *target, guint8 index, GBytes *bytes,
+ GCancellable *cancellable, GError **error)
+{
+ DfuTargetPrivate *priv = GET_PRIVATE (target);
+ g_autoptr(GError) error_local = NULL;
+ gsize actual_length;
+
+ 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,
+ (guint8 *) g_bytes_get_data (bytes, NULL),
+ g_bytes_get_size (bytes),
+ &actual_length,
+ 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 download data: %s",
+ error_local->message);
+ return FALSE;
+ }
+
+ /* for ST devices, the action only occurs when we do GetStatus */
+ if (!dfu_target_check_status (target, cancellable, error))
+ return FALSE;
+
+ g_assert (actual_length == g_bytes_get_size (bytes));
+ return TRUE;
+}
+
+/**
+ * dfu_target_set_address:
+ * @target: a #DfuTarget
+ * @address: memory address
+ * @cancellable: a #GCancellable, or %NULL
+ * @error: a #GError, or %NULL
+ *
+ * Sets the address used for the next download or upload request.
+ *
+ * IMPORTANT: This only works on DfuSe-capable devices from ST.
+ *
+ * Return value: %TRUE for success
+ *
+ * Since: 0.5.4
+ **/
+static gboolean
+dfu_target_set_address (DfuTarget *target,
+ guint32 address,
+ GCancellable *cancellable,
+ GError **error)
+{
+ DfuTargetPrivate *priv = GET_PRIVATE (target);
+ GBytes *data_in;
+ guint8 buf[5];
+
+ /* invalid */
+ if (!priv->dfuse_supported) {
+ g_set_error_literal (error,
+ DFU_ERROR,
+ DFU_ERROR_NOT_SUPPORTED,
+ "only supported for DfuSe targets");
+ return FALSE;
+ }
+
+ /* format buffer */
+ buf[0] = DFU_CMD_DFUSE_SET_ADDRESS_POINTER;
+ memcpy (buf + 1, &address, 4);
+ data_in = g_bytes_new_static (buf, sizeof(buf));
+ if (!dfu_target_download_chunk (target, 0, data_in, cancellable, error))
+ return FALSE;
+
+ /* for ST devices, the action only occurs when we do GetStatus */
+ if (!dfu_target_check_status (target, cancellable, error))
+ return FALSE;
+ return TRUE;
+}
+
+/**
+ * dfu_target_erase_address:
+ * @target: a #DfuTarget
+ * @address: memory address
+ * @cancellable: a #GCancellable, or %NULL
+ * @error: a #GError, or %NULL
+ *
+ * Erases a memory sector at a given address.
+ *
+ * IMPORTANT: This only works on DfuSe-capable devices from ST.
+ *
+ * Return value: %TRUE for success
+ *
+ * Since: 0.5.4
+ **/
+static gboolean
+dfu_target_erase_address (DfuTarget *target,
+ guint32 address,
+ GCancellable *cancellable,
+ GError **error)
+{
+ DfuTargetPrivate *priv = GET_PRIVATE (target);
+ GBytes *data_in;
+ guint8 buf[5];
+
+ /* invalid */
+ if (!priv->dfuse_supported) {
+ g_set_error_literal (error,
+ DFU_ERROR,
+ DFU_ERROR_NOT_SUPPORTED,
+ "only supported for DfuSe targets");
+ return FALSE;
+ }
+
+ /* format buffer */
+ buf[0] = DFU_CMD_DFUSE_ERASE;
+ memcpy (buf + 1, &address, 4);
+ data_in = g_bytes_new_static (buf, sizeof(buf));
+ if (!dfu_target_download_chunk (target, 0, data_in, cancellable, error))
+ return FALSE;
+
+ /* for ST devices, the action only occurs when we do GetStatus */
+ if (!dfu_target_check_status (target, cancellable, error))
+ return FALSE;
+
+ /* 2nd check required to get error code */
+ return dfu_target_check_status (target, cancellable, error);
+}
+
+#if 0
+
+/**
+ * dfu_target_mass_erase:
+ * @target: a #DfuTarget
+ * @cancellable: a #GCancellable, or %NULL
+ * @error: a #GError, or %NULL
+ *
+ * Mass erases the device clearing all SRAM and EEPROM memory.
+ *
+ * This may not be supported on all devices, a better way of doing this action
+ * is to enable read protection and then doing dfu_target_read_unprotect().
+ *
+ * IMPORTANT: This only works on DfuSe-capable devices from ST.
+ *
+ * Return value: %TRUE for success
+ *
+ * Since: 0.5.4
+ **/
+static gboolean
+dfu_target_mass_erase (DfuTarget *target,
+ GCancellable *cancellable,
+ GError **error)
+{
+ DfuTargetPrivate *priv = GET_PRIVATE (target);
+ GBytes *data_in;
+ guint8 buf[1];
+
+ /* invalid */
+ if (!priv->dfuse_supported) {
+ g_set_error_literal (error,
+ DFU_ERROR,
+ DFU_ERROR_NOT_SUPPORTED,
+ "only supported for DfuSe targets");
+ return FALSE;
+ }
+
+ /* format buffer */
+ buf[0] = DFU_CMD_DFUSE_ERASE;
+ data_in = g_bytes_new_static (buf, sizeof(buf));
+ if (!dfu_target_download_chunk (target, 0, data_in, cancellable, error))
+ return FALSE;
+
+ /* for ST devices, the action only occurs when we do GetStatus */
+ if (!dfu_target_check_status (target, cancellable, error))
+ return FALSE;
+
+ /* 2nd check required to get error code */
+ return dfu_target_check_status (target, cancellable, error);
+}
+
+/**
+ * dfu_target_read_unprotect:
+ * @target: a #DfuTarget
+ * @cancellable: a #GCancellable, or %NULL
+ * @error: a #GError, or %NULL
+ *
+ * Turns of read protection on the device, clearing all SRAM and EEPROM memory.
+ *
+ * IMPORTANT: This only works on DfuSe-capable devices from ST.
+ *
+ * Return value: %TRUE for success
+ *
+ * Since: 0.5.4
+ **/
+static gboolean
+dfu_target_read_unprotect (DfuTarget *target,
+ GCancellable *cancellable,
+ GError **error)
+{
+ DfuTargetPrivate *priv = GET_PRIVATE (target);
+ GBytes *data_in;
+ guint8 buf[5];
+
+ /* invalid */
+ if (!priv->dfuse_supported) {
+ g_set_error_literal (error,
+ DFU_ERROR,
+ DFU_ERROR_NOT_SUPPORTED,
+ "only supported for DfuSe targets");
+ return FALSE;
+ }
+
+ /* format buffer */
+ buf[0] = DFU_CMD_DFUSE_READ_UNPROTECT;
+ memcpy (buf + 1, &address, 4);
+ data_in = g_bytes_new_static (buf, sizeof(buf));
+ if (!dfu_target_download_chunk (target, 0, data_in, cancellable, error))
+ return FALSE;
+
+ /* for ST devices, the action only occurs when we do GetStatus */
+ return dfu_target_check_status (target, cancellable, error);
+}
+
+#endif
+
/**
* dfu_target_upload_chunk:
**/
@@ -844,84 +1468,105 @@ dfu_target_upload_chunk (DfuTarget *target, guint8 index,
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",
+ "cannot upload data: %s",
error_local->message);
return NULL;
}
+
+ /* for ST devices, the action only occurs when we do GetStatus */
+ if (!dfu_target_check_status (target, cancellable, error))
+ return FALSE;
+
return g_bytes_new_take (buf, actual_length);
}
/**
- * dfu_target_upload:
- * @target: a #DfuTarget
- * @flags: flags to use, e.g. %DFU_TARGET_TRANSFER_FLAG_VERIFY
- * @expected_size: the expected size of the firmware, or 0 for unknown
- * @cancellable: a #GCancellable, or %NULL
- * @progress_cb: a #GFileProgressCallback, or %NULL
- * @progress_cb_data: user data to pass to @progress_cb
- * @error: a #GError, or %NULL
- *
- * Uploads firmware from the target to the host.
- *
- * Return value: (transfer full): the uploaded image, or %NULL for error
- *
- * Since: 0.5.4
+ * dfu_target_upload_element:
**/
-DfuImage *
-dfu_target_upload (DfuTarget *target,
- gsize expected_size,
- DfuTargetTransferFlags flags,
- GCancellable *cancellable,
- DfuProgressCallback progress_cb,
- gpointer progress_cb_data,
- GError **error)
+static DfuElement *
+dfu_target_upload_element (DfuTarget *target,
+ guint32 address,
+ gsize expected_size,
+ GCancellable *cancellable,
+ DfuProgressCallback progress_cb,
+ gpointer progress_cb_data,
+ GError **error)
{
DfuTargetPrivate *priv = GET_PRIVATE (target);
- DfuImage *image = NULL;
+ DfuSector *sector;
+ DfuElement *element = NULL;
GBytes *chunk_tmp;
gsize chunk_size;
gsize offset = 0;
gsize total_size = 0;
guint8 *buffer;
+ guint32 last_sector_id = G_MAXUINT;
+ guint dfuse_sector_offset = 0;
guint i;
- g_autoptr(DfuElement) element = NULL;
g_autoptr(GBytes) contents = NULL;
g_autoptr(GPtrArray) chunks = NULL;
- g_return_val_if_fail (DFU_IS_TARGET (target), NULL);
- g_return_val_if_fail (error == NULL || *error == NULL, NULL);
-
- /* can the target do this? */
- if (!dfu_target_can_upload (target)) {
- g_set_error_literal (error,
- DFU_ERROR,
- DFU_ERROR_NOT_SUPPORTED,
- "target cannot do uploading");
- return NULL;
- }
-
- /* net yet... */
+ /* ST uses wBlockNum=0 for DfuSe commands and wBlockNum=1 is reserved */
if (priv->dfuse_supported) {
- g_set_error_literal (error,
- DFU_ERROR,
- DFU_ERROR_NOT_SUPPORTED,
- "DfuSe support not complete");
- return NULL;
+ offset += address;
+ dfuse_sector_offset = 2;
}
/* get all the chunks from the hardware */
chunks = g_ptr_array_new_with_free_func ((GDestroyNotify) g_bytes_unref);
for (i = 0; i < 0xffff; i++) {
- chunk_tmp = dfu_target_upload_chunk (target, i, cancellable, error);
+
+ /* for DfuSe devices we need to handle the address manually */
+ if (priv->dfuse_supported) {
+
+ /* check the sector with this element address is suitable */
+ sector = dfu_target_get_sector_for_addr (target, offset);
+ if (sector == NULL) {
+ g_set_error (error,
+ DFU_ERROR,
+ DFU_ERROR_INVALID_DEVICE,
+ "no memory sector at 0x%04x",
+ (guint) offset);
+ return FALSE;
+ }
+ if (!dfu_sector_has_cap (sector, DFU_SECTOR_CAP_READABLE)) {
+ g_set_error (error,
+ DFU_ERROR,
+ DFU_ERROR_INVALID_DEVICE,
+ "memory sector at 0x%04x is not readble",
+ (guint) offset);
+ return FALSE;
+ }
+
+ /* manually set the sector address */
+ if (dfu_sector_get_id (sector) != last_sector_id) {
+ g_debug ("setting DfuSe address to 0x%04x", (guint) offset);
+ if (!dfu_target_set_address (target,
+ offset,
+ cancellable,
+ error))
+ return FALSE;
+ last_sector_id = dfu_sector_get_id (sector);
+ }
+ }
+
+ /* read chunk of data */
+ chunk_tmp = dfu_target_upload_chunk (target,
+ i + dfuse_sector_offset,
+ cancellable,
+ error);
if (chunk_tmp == NULL)
return NULL;
/* keep a sum of all the chunks */
chunk_size = g_bytes_get_size (chunk_tmp);
total_size += chunk_size;
+ offset += chunk_size;
/* add to array */
g_debug ("got #%04x chunk of size %li", i, chunk_size);
@@ -952,6 +1597,109 @@ dfu_target_upload (DfuTarget *target,
}
}
+ /* stitch them all together */
+ offset = 0;
+ buffer = g_malloc0 (total_size);
+ for (i = 0; i < chunks->len; i++) {
+ const guint8 *chunk_data;
+ chunk_tmp = g_ptr_array_index (chunks, i);
+ chunk_data = g_bytes_get_data (chunk_tmp, &chunk_size);
+ memcpy (buffer + offset, chunk_data, chunk_size);
+ offset += chunk_size;
+ }
+
+ /* create new image */
+ contents = g_bytes_new_take (buffer, total_size);
+ element = dfu_element_new ();
+ dfu_element_set_contents (element, contents);
+ return element;
+}
+
+/**
+ * dfu_target_upload:
+ * @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_data: user data to pass to @progress_cb
+ * @error: a #GError, or %NULL
+ *
+ * Uploads firmware from the target to the host.
+ *
+ * Return value: (transfer full): the uploaded image, or %NULL for error
+ *
+ * Since: 0.5.4
+ **/
+DfuImage *
+dfu_target_upload (DfuTarget *target,
+ DfuTargetTransferFlags flags,
+ GCancellable *cancellable,
+ DfuProgressCallback progress_cb,
+ gpointer progress_cb_data,
+ GError **error)
+{
+ DfuTargetPrivate *priv = GET_PRIVATE (target);
+ DfuSector *sector;
+ guint i;
+ guint32 last_sector_id = G_MAXUINT;
+ g_autoptr(DfuImage) image = NULL;
+
+ g_return_val_if_fail (DFU_IS_TARGET (target), NULL);
+ g_return_val_if_fail (error == NULL || *error == NULL, NULL);
+
+ /* can the target do this? */
+ if (!dfu_target_can_upload (target)) {
+ g_set_error_literal (error,
+ DFU_ERROR,
+ DFU_ERROR_NOT_SUPPORTED,
+ "target cannot do uploading");
+ return NULL;
+ }
+
+ /* no open?! */
+ if (priv->sectors->len == 0) {
+ g_set_error_literal (error,
+ DFU_ERROR,
+ DFU_ERROR_NOT_SUPPORTED,
+ "no sectors defined for target");
+ return NULL;
+ }
+
+ /* 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);
+
+ /* get all the sectors for the device */
+ for (i = 0; i < priv->sectors->len; i++) {
+ g_autoptr(DfuElement) element = NULL;
+
+ /* only upload to the start of any zone:sector */
+ sector = g_ptr_array_index (priv->sectors, i);
+ if (dfu_sector_get_id (sector) == last_sector_id)
+ continue;
+
+ /* get the first element from the hardware */
+ g_debug ("starting upload from 0x%08x (0x%04x)",
+ dfu_sector_get_address (sector),
+ dfu_sector_get_size_left (sector));
+ element = dfu_target_upload_element (target,
+ dfu_sector_get_address (sector),
+ dfu_sector_get_size_left (sector),
+ cancellable,
+ progress_cb,
+ progress_cb_data,
+ error);
+ if (element == NULL)
+ return NULL;
+
+ /* this element was uploaded okay */
+ dfu_image_add_element (image, element);
+
+ /* ignore sectors until one of these changes */
+ last_sector_id = dfu_sector_get_id (sector);
+ }
+
/* do host reset */
if ((flags & DFU_TARGET_TRANSFER_FLAG_HOST_RESET) > 0 ||
(flags & DFU_TARGET_TRANSFER_FLAG_BOOT_RUNTIME) > 0) {
@@ -966,61 +1714,8 @@ dfu_target_upload (DfuTarget *target,
return NULL;
}
- /* stitch them all together */
- buffer = g_malloc0 (total_size);
- for (i = 0; i < chunks->len; i++) {
- const guint8 *chunk_data;
- chunk_tmp = g_ptr_array_index (chunks, i);
- chunk_data = g_bytes_get_data (chunk_tmp, &chunk_size);
- memcpy (buffer + offset, chunk_data, chunk_size);
- offset += chunk_size;
- }
-
- /* create new image */
- contents = g_bytes_new_take (buffer, total_size);
- image = dfu_image_new ();
- dfu_image_set_name (image, priv->iface_alt_setting_name);
- dfu_image_set_alt_setting (image, priv->iface_alt_setting);
- element = dfu_element_new ();
- dfu_element_set_contents (element, contents);
- dfu_image_add_element (image, element);
- return image;
-}
-
-/**
- * dfu_target_download_chunk:
- **/
-static gboolean
-dfu_target_download_chunk (DfuTarget *target, guint8 index, GBytes *bytes,
- GCancellable *cancellable, GError **error)
-{
- DfuTargetPrivate *priv = GET_PRIVATE (target);
- g_autoptr(GError) error_local = NULL;
- gsize actual_length;
-
- 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,
- (guint8 *) g_bytes_get_data (bytes, NULL),
- g_bytes_get_size (bytes),
- &actual_length,
- priv->timeout_ms,
- cancellable,
- &error_local)) {
- g_set_error (error,
- DFU_ERROR,
- DFU_ERROR_NOT_SUPPORTED,
- "cannot download data to the target (state: %s): %s",
- dfu_state_to_string (priv->state),
- error_local->message);
- return FALSE;
- }
- g_assert (actual_length == g_bytes_get_size (bytes));
- return TRUE;
+ /* success */
+ return g_object_ref (image);
}
/**
@@ -1055,32 +1750,32 @@ _g_bytes_compare_verbose (GBytes *bytes1, GBytes *bytes2)
}
/**
- * dfu_target_download_bytes:
+ * dfu_target_download_element:
**/
static gboolean
-dfu_target_download_bytes (DfuTarget *target, GBytes *bytes,
- DfuTargetTransferFlags flags,
- GCancellable *cancellable,
- DfuProgressCallback progress_cb,
- gpointer progress_cb_data,
- GError **error)
+dfu_target_download_element (DfuTarget *target,
+ DfuElement *element,
+ DfuTargetTransferFlags flags,
+ GCancellable *cancellable,
+ DfuProgressCallback progress_cb,
+ gpointer progress_cb_data,
+ GError **error)
{
DfuTargetPrivate *priv = GET_PRIVATE (target);
- DfuElement *element;
+ DfuSector *sector;
+ GBytes *bytes;
guint i;
guint nr_chunks;
+ guint dfuse_sector_offset = 0;
+ guint last_sector_id = G_MAXUINT;
g_autoptr(GError) error_local = NULL;
- /* can the target do this? */
- if (!dfu_target_can_download (target)) {
- g_set_error_literal (error,
- DFU_ERROR,
- DFU_ERROR_NOT_SUPPORTED,
- "target cannot do downloading");
- return FALSE;
- }
+ /* ST uses wBlockNum=0 for DfuSe commands and wBlockNum=1 is reserved */
+ if (priv->dfuse_supported)
+ 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);
if (nr_chunks == 0) {
@@ -1095,8 +1790,59 @@ dfu_target_download_bytes (DfuTarget *target, GBytes *bytes,
gsize offset;
g_autoptr(GBytes) bytes_tmp = NULL;
- /* we have to write one final zero-sized chunk for EOF */
+ /* caclulate the offset into the element data */
offset = i * priv->transfer_size;
+
+ /* for DfuSe devices we need to handle the erase and setting
+ * the address manually */
+ if (priv->dfuse_supported) {
+
+ /* check the sector with this element address is suitable */
+ sector = dfu_target_get_sector_for_addr (target, offset);
+ if (sector == NULL) {
+ g_set_error (error,
+ DFU_ERROR,
+ DFU_ERROR_INVALID_DEVICE,
+ "no memory sector at 0x%04x",
+ (guint) offset);
+ return FALSE;
+ }
+ if (!dfu_sector_has_cap (sector, DFU_SECTOR_CAP_WRITEABLE)) {
+ g_set_error (error,
+ DFU_ERROR,
+ DFU_ERROR_INVALID_DEVICE,
+ "memory sector at 0x%04x is not writable",
+ (guint) offset);
+ return FALSE;
+ }
+
+ /* if it's erasable and not yet blanked */
+ if (!dfu_sector_has_cap (sector, DFU_SECTOR_CAP_ERASEABLE) &&
+ g_hash_table_lookup (priv->sectors_erased, sector) == NULL) {
+ g_debug ("erasing DfuSe address at 0x%04x", (guint) offset);
+ if (!dfu_target_erase_address (target,
+ offset,
+ cancellable,
+ error))
+ return FALSE;
+ g_hash_table_insert (priv->sectors_erased,
+ sector,
+ GINT_TO_POINTER (1));
+ }
+
+ /* manually set the sector address */
+ if (dfu_sector_get_id (sector) != last_sector_id) {
+ g_debug ("setting DfuSe address to 0x%04x", (guint) offset);
+ if (!dfu_target_set_address (target,
+ offset,
+ cancellable,
+ error))
+ return FALSE;
+ last_sector_id = dfu_sector_get_id (sector);
+ }
+ }
+
+ /* 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)
@@ -1107,13 +1853,12 @@ dfu_target_download_bytes (DfuTarget *target, GBytes *bytes,
}
g_debug ("writing #%04x chunk of size %li",
i, g_bytes_get_size (bytes_tmp));
- if (!dfu_target_download_chunk (target, i, bytes_tmp, cancellable, error)) {
- if (dfu_target_refresh (target, cancellable, NULL)) {
- g_prefix_error (error, "Device status %s: ",
- dfu_status_to_string (priv->status));
- }
+ if (!dfu_target_download_chunk (target,
+ i + dfuse_sector_offset,
+ bytes_tmp,
+ cancellable,
+ error))
return FALSE;
- }
/* update UI */
if (progress_cb != NULL) {
@@ -1134,18 +1879,17 @@ dfu_target_download_bytes (DfuTarget *target, GBytes *bytes,
/* verify */
if (flags & DFU_TARGET_TRANSFER_FLAG_VERIFY) {
GBytes *bytes_tmp;
- g_autoptr(DfuImage) image_tmp = NULL;
- image_tmp = dfu_target_upload (target,
- g_bytes_get_size (bytes),
- DFU_TARGET_TRANSFER_FLAG_NONE,
- cancellable,
- progress_cb,
- progress_cb_data,
- error);
- if (image_tmp == NULL)
+ g_autoptr(DfuElement) element_tmp = NULL;
+ element_tmp = dfu_target_upload_element (target,
+ dfu_element_get_address (element),
+ g_bytes_get_size (bytes),
+ cancellable,
+ progress_cb,
+ progress_cb_data,
+ error);
+ if (element_tmp == NULL)
return FALSE;
- element = dfu_image_get_element (image_tmp, 0);
- bytes_tmp = dfu_element_get_contents (element);
+ bytes_tmp = dfu_element_get_contents (element_tmp);
if (g_bytes_compare (bytes_tmp, bytes) != 0) {
g_autofree gchar *bytes_cmp_str = NULL;
bytes_cmp_str = _g_bytes_compare_verbose (bytes_tmp, bytes);
@@ -1158,20 +1902,6 @@ dfu_target_download_bytes (DfuTarget *target, GBytes *bytes,
}
}
- /* do a host reset */
- if ((flags & DFU_TARGET_TRANSFER_FLAG_HOST_RESET) > 0 ||
- (flags & DFU_TARGET_TRANSFER_FLAG_BOOT_RUNTIME) > 0) {
- if (!dfu_device_reset (priv->device, error))
- return FALSE;
- }
-
- /* boot to runtime */
- if (flags & DFU_TARGET_TRANSFER_FLAG_BOOT_RUNTIME) {
- g_debug ("booting to runtime to set auto-boot");
- if (!dfu_device_wait_for_replug (priv->device, 2000, cancellable, error))
- return FALSE;
- }
-
return TRUE;
}
@@ -1201,36 +1931,111 @@ dfu_target_download (DfuTarget *target, DfuImage *image,
GError **error)
{
DfuTargetPrivate *priv = GET_PRIVATE (target);
- GBytes *contents;
DfuElement *element;
+ GPtrArray *elements;
+ gboolean ret;
+ guint i;
g_return_val_if_fail (DFU_IS_TARGET (target), FALSE);
g_return_val_if_fail (DFU_IS_IMAGE (image), FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
- /* net yet... */
- if (priv->dfuse_supported) {
+ /* can the target do this? */
+ if (!dfu_target_can_download (target)) {
g_set_error_literal (error,
DFU_ERROR,
DFU_ERROR_NOT_SUPPORTED,
- "DfuSe support not complete");
+ "target cannot do downloading");
return FALSE;
}
- /* get data */
- element = dfu_image_get_element (image, 0);
- if (element == NULL) {
+ /* mark these as all erased */
+ if (priv->dfuse_supported)
+ g_hash_table_remove_all (priv->sectors_erased);
+
+ /* download all elements in the image to the device */
+ elements = dfu_image_get_elements (image);
+ if (elements->len == 0) {
g_set_error_literal (error,
DFU_ERROR,
DFU_ERROR_INVALID_FILE,
"no image elements");
return FALSE;
}
- contents = dfu_element_get_contents (element);
- return dfu_target_download_bytes (target, contents, flags, cancellable,
- progress_cb, progress_cb_data, error);
+ for (i = 0; i < elements->len; i++) {
+ element = dfu_image_get_element (image, i);
+ g_debug ("downloading element at 0x%04x",
+ dfu_element_get_address (element));
+ ret = dfu_target_download_element (target,
+ element,
+ flags,
+ cancellable,
+ progress_cb,
+ progress_cb_data,
+ error);
+ if (!ret)
+ return FALSE;
+ }
+
+ /* do a host reset */
+ if ((flags & DFU_TARGET_TRANSFER_FLAG_HOST_RESET) > 0 ||
+ (flags & DFU_TARGET_TRANSFER_FLAG_BOOT_RUNTIME) > 0) {
+ if (!dfu_device_reset (priv->device, error))
+ return FALSE;
+ }
+
+ /* boot to runtime */
+ if (flags & DFU_TARGET_TRANSFER_FLAG_BOOT_RUNTIME) {
+ g_debug ("booting to runtime to set auto-boot");
+ if (!dfu_device_wait_for_replug (priv->device, 2000, cancellable, error))
+ return FALSE;
+ }
+
+ /* success */
+ return TRUE;
}
+#if 0
+/**
+ * dfu_target_get_commands:
+ **/
+static gboolean
+dfu_target_get_commands (DfuTarget *target,
+ GCancellable *cancellable,
+ GError **error)
+{
+ GBytes *data_in;
+ GBytes *data_out;
+ guint8 buf[1];
+
+ /* invalid */
+ if (!priv->dfuse_supported) {
+ g_set_error_literal (error,
+ DFU_ERROR,
+ DFU_ERROR_NOT_SUPPORTED,
+ "only supported for DfuSe targets");
+ return FALSE;
+ }
+
+ /* format buffer */
+ buf[0] = DFU_CMD_DFUSE_GET_COMMAND;
+ data_in = g_bytes_new_static (buf, sizeof(buf));
+ if (!dfu_target_download_chunk (target, 0, data_in, cancellable, error))
+ return FALSE;
+
+ /* return results */
+ data_out = dfu_target_upload_chunk (target, 0, cancellable, error);
+ if (data_out == NULL)
+ return FALSE;
+
+ // N bytes,
+ // each byte is the command code
+
+ // FIXME: parse?
+ return TRUE;
+}
+#endif
+
/**
* dfu_target_set_timeout:
* @target: a #DfuTarget
diff --git a/libdfu/dfu-target.h b/libdfu/dfu-target.h
index a6c53a333..6f7ecde2a 100644
--- a/libdfu/dfu-target.h
+++ b/libdfu/dfu-target.h
@@ -90,6 +90,7 @@ gboolean dfu_target_close (DfuTarget *target,
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,
@@ -105,7 +106,6 @@ gboolean dfu_target_clear_status (DfuTarget *target,
GCancellable *cancellable,
GError **error);
DfuImage *dfu_target_upload (DfuTarget *target,
- gsize expected_size,
DfuTargetTransferFlags flags,
GCancellable *cancellable,
DfuProgressCallback progress_cb,
diff --git a/libdfu/dfu-tool.c b/libdfu/dfu-tool.c
index c3a1c01a0..2bdb8d077 100644
--- a/libdfu/dfu-tool.c
+++ b/libdfu/dfu-tool.c
@@ -861,7 +861,7 @@ dfu_tool_upload_target (DfuToolPrivate *priv, gchar **values, GError **error)
helper.last_state = DFU_STATE_DFU_ERROR;
helper.marks_total = 30;
helper.marks_shown = 0;
- image = dfu_target_upload (target, 0, flags, NULL,
+ image = dfu_target_upload (target, flags, NULL,
fu_tool_transfer_progress_cb, &helper,
error);
if (image == NULL)
@@ -928,7 +928,7 @@ dfu_tool_upload (DfuToolPrivate *priv, gchar **values, GError **error)
helper.last_state = DFU_STATE_DFU_ERROR;
helper.marks_total = 30;
helper.marks_shown = 0;
- firmware = dfu_device_upload (device, 0, flags, NULL,
+ firmware = dfu_device_upload (device, flags, NULL,
fu_tool_transfer_progress_cb, &helper,
error);
if (firmware == NULL)
@@ -1181,6 +1181,7 @@ dfu_tool_list_target (DfuTarget *target)
{
const gchar *tmp;
gboolean ret;
+ guint i;
g_autofree gchar *alt_id = NULL;
g_autoptr(GError) error_local = NULL;
@@ -1199,6 +1200,7 @@ dfu_tool_list_target (DfuTarget *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);
@@ -1206,6 +1208,19 @@ dfu_tool_list_target (DfuTarget *target)
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,
@@ -1217,6 +1232,7 @@ dfu_tool_list_target (DfuTarget *target)
dfu_tool_print_indent (_("Status"), error_local->message, 2);
}
}
+
dfu_target_close (target, NULL);
}
diff --git a/libdfu/dfu.h b/libdfu/dfu.h
index c0bca8825..1d1a92377 100644
--- a/libdfu/dfu.h
+++ b/libdfu/dfu.h
@@ -35,6 +35,7 @@
#include
#include
#include
+#include
#include
#undef __DFU_H_INSIDE__
diff --git a/src/fu-provider-usb.c b/src/fu-provider-usb.c
index eb179c647..49fde70a3 100644
--- a/src/fu-provider-usb.c
+++ b/src/fu-provider-usb.c
@@ -417,7 +417,6 @@ fu_provider_usb_verify (FuProvider *provider,
/* get data from hardware */
dfu_firmware = dfu_device_upload (dfu_device,
- 0,
DFU_TARGET_TRANSFER_FLAG_DETACH |
DFU_TARGET_TRANSFER_FLAG_BOOT_RUNTIME,
NULL,