Esempio n. 1
0
/**
 * dfu_element_to_dfuse: (skip)
 * @element: a #DfuElement
 *
 * Packs a DfuSe element.
 *
 * Returns: (transfer full): the packed data
 **/
static GBytes *
dfu_element_to_dfuse (DfuElement *element)
{
	DfuSeElementPrefix *el;
	const guint8 *data;
	gsize length;
	guint8 *buf;

	data = g_bytes_get_data (dfu_element_get_contents (element), &length);
	buf = g_malloc0 (length + sizeof (DfuSeElementPrefix));
	el = (DfuSeElementPrefix *) buf;
	el->address = GUINT32_TO_LE (dfu_element_get_address (element));
	el->size = GUINT32_TO_LE (length);

	memcpy (buf + sizeof (DfuSeElementPrefix), data, length);
	return g_bytes_new_take (buf, length + sizeof (DfuSeElementPrefix));
}
Esempio n. 2
0
/**
 * dfu_target_download:
 * @target: a #DfuTarget
 * @image: a #DfuImage
 * @flags: flags to use, e.g. %DFU_TARGET_TRANSFER_FLAG_VERIFY
 * @cancellable: a #GCancellable, or %NULL
 * @error: a #GError, or %NULL
 *
 * Downloads firmware from the host to the target, optionally verifying
 * the transfer.
 *
 * Return value: %TRUE for success
 *
 * Since: 0.5.4
 **/
gboolean
dfu_target_download (DfuTarget *target, DfuImage *image,
		     DfuTargetTransferFlags flags,
		     GCancellable *cancellable,
		     GError **error)
{
	DfuTargetPrivate *priv = GET_PRIVATE (target);
	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);

	/* ensure populated */
	if (!dfu_target_setup (target, error))
		return NULL;

	/* can the target do this? */
	if (!dfu_device_can_download (priv->device)) {
		g_set_error_literal (error,
				     DFU_ERROR,
				     DFU_ERROR_NOT_SUPPORTED,
				     "target cannot do downloading");
		return FALSE;
	}

	/* use correct alt */
	if (!dfu_target_use_alt_setting (target, error))
		return FALSE;

	/* mark these as all erased */
	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 */
	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;
	}
	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,
						   error);
		if (!ret)
			return FALSE;
	}

	/* attempt to switch back to runtime */
	if ((flags & DFU_TARGET_TRANSFER_FLAG_ATTACH) > 0 ||
	    (flags & DFU_TARGET_TRANSFER_FLAG_WAIT_RUNTIME) > 0) {
		if (!dfu_device_attach (priv->device, error))
			return FALSE;
	}

	/* boot to runtime */
	if (flags & DFU_TARGET_TRANSFER_FLAG_WAIT_RUNTIME) {
		g_debug ("booting to runtime to set auto-boot");
		if (!dfu_device_wait_for_replug (priv->device,
						 DFU_DEVICE_REPLUG_TIMEOUT,
						 cancellable,
						 error))
			return FALSE;
	}

	/* success */
	return TRUE;
}
Esempio n. 3
0
/**
 * dfu_target_download_element:
 **/
static gboolean
dfu_target_download_element (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;
	guint old_percentage = 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 = 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 + 1; i++) {
		gsize length;
		gsize offset;
		guint percentage;
		g_autoptr(GBytes) bytes_tmp = NULL;

		/* caclulate the offset into the element data */
		offset = i * transfer_size;

		/* 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);
			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 > 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,
						i + dfuse_sector_offset,
						bytes_tmp,
						cancellable,
						error))
			return FALSE;

		/* update UI */
		percentage = (offset * 100) / g_bytes_get_size (bytes);
		if (percentage != old_percentage) {
			g_signal_emit (target,
				       signals[SIGNAL_PERCENTAGE_CHANGED],
				       0, percentage);
		}

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

	/* verify */
	if (flags & DFU_TARGET_TRANSFER_FLAG_VERIFY) {
		GBytes *bytes_tmp;
		g_autoptr(DfuElement) element_tmp = NULL;
		element_tmp = dfu_target_upload_element (target,
							 dfu_element_get_address (element),
							 g_bytes_get_size (bytes),
							 cancellable,
							 error);
		if (element_tmp == NULL)
			return FALSE;
		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);
			g_set_error (error,
				     DFU_ERROR,
				     DFU_ERROR_VERIFY_FAILED,
				     "verify failed: %s",
				     bytes_cmp_str);
			return FALSE;
		}
	}

	return TRUE;
}