From 73b63c87e896b56c13d07064b18e2fdd87d2999b Mon Sep 17 00:00:00 2001 From: Richard Hughes Date: Fri, 14 Oct 2016 08:04:35 +0100 Subject: [PATCH] libdfu: Do not do a zero-byte download when in DfuSe mode For ST devices this means 'boot into APP mode' which we can also now populate. --- libdfu/dfu-device.c | 18 ++- libdfu/dfu-target-private.h | 5 + libdfu/dfu-target.c | 220 +++++++++++++++++++++++++----------- 3 files changed, 177 insertions(+), 66 deletions(-) diff --git a/libdfu/dfu-device.c b/libdfu/dfu-device.c index c967eb387..1739039ba 100644 --- a/libdfu/dfu-device.c +++ b/libdfu/dfu-device.c @@ -1708,7 +1708,23 @@ dfu_device_attach (DfuDevice *device, GError **error) /* there's a a special command for ST devices */ if (priv->dfuse_supported) { - //FIXME + g_autoptr(DfuTarget) target = NULL; + g_autoptr(GBytes) bytes_tmp = NULL; + + /* get default target */ + target = dfu_device_get_target_by_alt_setting (device, 0, error); + if (target == NULL) + return FALSE; + + /* do zero byte download */ + bytes_tmp = g_bytes_new (NULL, 0); + if (!dfu_target_download_chunk (target, + 0 + 2, + bytes_tmp, + NULL, + error)) + return FALSE; + dfu_device_set_action (device, DFU_ACTION_IDLE); return TRUE; } diff --git a/libdfu/dfu-target-private.h b/libdfu/dfu-target-private.h index 115868afc..551cdcf9f 100644 --- a/libdfu/dfu-target-private.h +++ b/libdfu/dfu-target-private.h @@ -36,6 +36,11 @@ GBytes *dfu_target_upload_chunk (DfuTarget *target, guint8 index, GCancellable *cancellable, GError **error); +gboolean dfu_target_download_chunk (DfuTarget *target, + guint8 index, + GBytes *bytes, + 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 5fb1add25..6c3ae1b51 100644 --- a/libdfu/dfu-target.c +++ b/libdfu/dfu-target.c @@ -648,7 +648,7 @@ dfu_target_setup (DfuTarget *target, GError **error) return TRUE; } -static gboolean +gboolean dfu_target_download_chunk (DfuTarget *target, guint8 index, GBytes *bytes, GCancellable *cancellable, GError **error) { @@ -1242,26 +1242,19 @@ _g_bytes_compare_verbose (GBytes *bytes1, GBytes *bytes2) } static gboolean -dfu_target_download_element (DfuTarget *target, - DfuElement *element, - DfuTargetTransferFlags flags, - GCancellable *cancellable, - GError **error) +dfu_target_download_element_dfu (DfuTarget *target, + DfuElement *element, + DfuTargetTransferFlags flags, + GCancellable *cancellable, + GError **error) { DfuTargetPrivate *priv = GET_PRIVATE (target); - DfuSector *sector; GBytes *bytes; guint i; 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 (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 = (guint) ceil ((gdouble) g_bytes_get_size (bytes) / @@ -1276,61 +1269,10 @@ dfu_target_download_element (DfuTarget *target, for (i = 0; i < nr_chunks + 1; i++) { gsize length; guint32 offset; - guint32 offset_dev; g_autoptr(GBytes) bytes_tmp = NULL; /* caclulate the offset into the element data */ offset = i * transfer_size; - offset_dev = dfu_element_get_address (element) + offset; - - /* for DfuSe devices we need to handle the erase and setting - * the address manually */ - 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_dev); - 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_dev); - 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", offset_dev); - if (!dfu_target_erase_address (target, - offset_dev, - 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, - (guint32) offset_dev, - 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) { @@ -1344,7 +1286,7 @@ dfu_target_download_element (DfuTarget *target, g_debug ("writing #%04x chunk of size %" G_GSIZE_FORMAT, i, g_bytes_get_size (bytes_tmp)); if (!dfu_target_download_chunk (target, - (guint8) (i + dfuse_sector_offset), + (guint8) i, bytes_tmp, cancellable, error)) @@ -1365,11 +1307,159 @@ dfu_target_download_element (DfuTarget *target, dfu_target_set_percentage_raw (target, 100); dfu_target_set_action (target, DFU_ACTION_IDLE); + /* success */ + return TRUE; +} + +static gboolean +dfu_target_download_element_dfuse (DfuTarget *target, + DfuElement *element, + DfuTargetTransferFlags flags, + GCancellable *cancellable, + GError **error) +{ + DfuTargetPrivate *priv = GET_PRIVATE (target); + DfuSector *sector; + GBytes *bytes; + guint i; + guint nr_chunks; + guint last_sector_id = G_MAXUINT; + guint16 transfer_size = dfu_device_get_transfer_size (priv->device); + g_autoptr(GError) error_local = NULL; + + /* round up as we have to transfer incomplete blocks */ + bytes = dfu_element_get_contents (element); + nr_chunks = (guint) ceil ((gdouble) g_bytes_get_size (bytes) / + (gdouble) transfer_size); + if (nr_chunks == 0) { + g_set_error_literal (error, + DFU_ERROR, + DFU_ERROR_INVALID_FILE, + "zero-length firmware"); + return FALSE; + } + for (i = 0; i < nr_chunks; i++) { + gsize length; + guint32 offset; + guint32 offset_dev; + g_autoptr(GBytes) bytes_tmp = NULL; + + /* caclulate the offset into the element data */ + offset = i * transfer_size; + offset_dev = dfu_element_get_address (element) + offset; + + /* for DfuSe devices we need to handle the erase and setting + * the sectory address manually */ + sector = dfu_target_get_sector_for_addr (target, offset_dev); + 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_dev); + 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", offset_dev); + if (!dfu_target_erase_address (target, + offset_dev, + 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, + (guint32) offset_dev, + cancellable, + error)) + return FALSE; + last_sector_id = dfu_sector_get_id (sector); + } + + /* we have to write one final zero-sized chunk for EOF */ + length = g_bytes_get_size (bytes) - offset; + if (length > transfer_size) + length = transfer_size; + bytes_tmp = g_bytes_new_from_bytes (bytes, offset, length); + g_debug ("writing #%04x chunk of size %" G_GSIZE_FORMAT, + i, g_bytes_get_size (bytes_tmp)); + /* ST uses wBlockNum=0 for DfuSe commands and wBlockNum=1 is reserved */ + if (!dfu_target_download_chunk (target, + (guint8) (i + 2), + bytes_tmp, + cancellable, + error)) + return FALSE; + + /* update UI */ + dfu_target_set_percentage (target, offset, g_bytes_get_size (bytes)); + + /* give the target a chance to update */ + g_usleep (dfu_device_get_download_timeout (priv->device) * 1000); + + /* getting the status moves the state machine to DNLOAD-IDLE */ + if (!dfu_device_refresh (priv->device, cancellable, error)) + return FALSE; + } + + /* done */ + dfu_target_set_percentage_raw (target, 100); + dfu_target_set_action (target, DFU_ACTION_IDLE); + + /* success */ + return TRUE; +} + +static gboolean +dfu_target_download_element (DfuTarget *target, + DfuElement *element, + DfuTargetTransferFlags flags, + GCancellable *cancellable, + GError **error) +{ + DfuTargetPrivate *priv = GET_PRIVATE (target); + + /* DfuSe specific */ + if (dfu_device_has_dfuse_support (priv->device)) { + if (!dfu_target_download_element_dfuse (target, + element, + flags, + cancellable, + error)) + return FALSE; + } else { + if (!dfu_target_download_element_dfu (target, + element, + flags, + cancellable, + error)) + return FALSE; + } + /* verify */ if (flags & DFU_TARGET_TRANSFER_FLAG_VERIFY) { + GBytes *bytes; GBytes *bytes_tmp; g_autoptr(DfuElement) element_tmp = NULL; dfu_target_set_action (target, DFU_ACTION_VERIFY); + bytes = dfu_element_get_contents (element); element_tmp = dfu_target_upload_element (target, dfu_element_get_address (element), g_bytes_get_size (bytes),