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 */
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;
}

View File

@ -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,

View File

@ -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,
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) /
@ -1274,6 +1267,78 @@ dfu_target_download_element (DfuTarget *target,
return FALSE;
}
for (i = 0; i < nr_chunks + 1; i++) {
gsize length;
guint32 offset;
g_autoptr(GBytes) bytes_tmp = NULL;
/* caclulate the offset into the element data */
offset = i * transfer_size;
/* we have to write one final zero-sized chunk for EOF */
if (i < nr_chunks) {
length = g_bytes_get_size (bytes) - offset;
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);
}
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,
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_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;
@ -1284,10 +1349,7 @@ dfu_target_download_element (DfuTarget *target,
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 */
* the sectory address manually */
sector = dfu_target_get_sector_for_addr (target, offset_dev);
if (sector == NULL) {
g_set_error (error,
@ -1330,21 +1392,17 @@ dfu_target_download_element (DfuTarget *target,
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 > transfer_size)
length = transfer_size;
bytes_tmp = g_bytes_new_from_bytes (bytes, offset, length);
} else {
bytes_tmp = g_bytes_new (NULL, 0);
}
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 + dfuse_sector_offset),
(guint8) (i + 2),
bytes_tmp,
cancellable,
error))
@ -1365,11 +1423,43 @@ 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 (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),