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.
This commit is contained in:
Richard Hughes 2016-10-14 08:04:35 +01:00
parent 8a3c18b4a3
commit 73b63c87e8
3 changed files with 177 additions and 66 deletions

View File

@ -1708,7 +1708,23 @@ dfu_device_attach (DfuDevice *device, GError **error)
/* there's a a special command for ST devices */ /* there's a a special command for ST devices */
if (priv->dfuse_supported) { 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); dfu_device_set_action (device, DFU_ACTION_IDLE);
return TRUE; return TRUE;
} }

View File

@ -36,6 +36,11 @@ GBytes *dfu_target_upload_chunk (DfuTarget *target,
guint8 index, guint8 index,
GCancellable *cancellable, GCancellable *cancellable,
GError **error); GError **error);
gboolean dfu_target_download_chunk (DfuTarget *target,
guint8 index,
GBytes *bytes,
GCancellable *cancellable,
GError **error);
/* export this just for the self tests */ /* export this just for the self tests */
gboolean dfu_target_parse_sectors (DfuTarget *target, gboolean dfu_target_parse_sectors (DfuTarget *target,

View File

@ -648,7 +648,7 @@ dfu_target_setup (DfuTarget *target, GError **error)
return TRUE; return TRUE;
} }
static gboolean gboolean
dfu_target_download_chunk (DfuTarget *target, guint8 index, GBytes *bytes, dfu_target_download_chunk (DfuTarget *target, guint8 index, GBytes *bytes,
GCancellable *cancellable, GError **error) GCancellable *cancellable, GError **error)
{ {
@ -1242,26 +1242,19 @@ _g_bytes_compare_verbose (GBytes *bytes1, GBytes *bytes2)
} }
static gboolean static gboolean
dfu_target_download_element (DfuTarget *target, dfu_target_download_element_dfu (DfuTarget *target,
DfuElement *element, DfuElement *element,
DfuTargetTransferFlags flags, DfuTargetTransferFlags flags,
GCancellable *cancellable, GCancellable *cancellable,
GError **error) GError **error)
{ {
DfuTargetPrivate *priv = GET_PRIVATE (target); DfuTargetPrivate *priv = GET_PRIVATE (target);
DfuSector *sector;
GBytes *bytes; GBytes *bytes;
guint i; guint i;
guint nr_chunks; guint nr_chunks;
guint dfuse_sector_offset = 0;
guint last_sector_id = G_MAXUINT;
guint16 transfer_size = dfu_device_get_transfer_size (priv->device); guint16 transfer_size = dfu_device_get_transfer_size (priv->device);
g_autoptr(GError) error_local = NULL; 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 */ /* round up as we have to transfer incomplete blocks */
bytes = dfu_element_get_contents (element); bytes = dfu_element_get_contents (element);
nr_chunks = (guint) ceil ((gdouble) g_bytes_get_size (bytes) / 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++) { for (i = 0; i < nr_chunks + 1; i++) {
gsize length; gsize length;
guint32 offset; guint32 offset;
guint32 offset_dev;
g_autoptr(GBytes) bytes_tmp = NULL; g_autoptr(GBytes) bytes_tmp = NULL;
/* caclulate the offset into the element data */ /* caclulate the offset into the element data */
offset = i * transfer_size; 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 */ /* we have to write one final zero-sized chunk for EOF */
if (i < nr_chunks) { if (i < nr_chunks) {
@ -1344,7 +1286,7 @@ dfu_target_download_element (DfuTarget *target,
g_debug ("writing #%04x chunk of size %" G_GSIZE_FORMAT, g_debug ("writing #%04x chunk of size %" G_GSIZE_FORMAT,
i, g_bytes_get_size (bytes_tmp)); i, g_bytes_get_size (bytes_tmp));
if (!dfu_target_download_chunk (target, if (!dfu_target_download_chunk (target,
(guint8) (i + dfuse_sector_offset), (guint8) i,
bytes_tmp, bytes_tmp,
cancellable, cancellable,
error)) error))
@ -1365,11 +1307,159 @@ dfu_target_download_element (DfuTarget *target,
dfu_target_set_percentage_raw (target, 100); dfu_target_set_percentage_raw (target, 100);
dfu_target_set_action (target, DFU_ACTION_IDLE); 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 */ /* verify */
if (flags & DFU_TARGET_TRANSFER_FLAG_VERIFY) { if (flags & DFU_TARGET_TRANSFER_FLAG_VERIFY) {
GBytes *bytes;
GBytes *bytes_tmp; GBytes *bytes_tmp;
g_autoptr(DfuElement) element_tmp = NULL; g_autoptr(DfuElement) element_tmp = NULL;
dfu_target_set_action (target, DFU_ACTION_VERIFY); dfu_target_set_action (target, DFU_ACTION_VERIFY);
bytes = dfu_element_get_contents (element);
element_tmp = dfu_target_upload_element (target, element_tmp = dfu_target_upload_element (target,
dfu_element_get_address (element), dfu_element_get_address (element),
g_bytes_get_size (bytes), g_bytes_get_size (bytes),