/** * dfu_firmware_from_raw: (skip) * @firmware: a #DfuFirmware * @bytes: data to parse * @flags: some #DfuFirmwareParseFlags * @error: a #GError, or %NULL * * Unpacks into a firmware object from raw data. * * Returns: %TRUE for success **/ gboolean dfu_firmware_from_raw (DfuFirmware *firmware, GBytes *bytes, DfuFirmwareParseFlags flags, GError **error) { g_autoptr(DfuElement) element = NULL; g_autoptr(DfuImage) image = NULL; image = dfu_image_new (); element = dfu_element_new (); dfu_element_set_contents (element, bytes); dfu_image_add_element (image, element); dfu_firmware_add_image (firmware, image); return TRUE; }
/** * dfu_element_from_dfuse: (skip) * @data: data buffer * @length: length of @data we can access * @consumed: (out): the number of bytes we consued * @error: a #GError, or %NULL * * Unpacks an element from DfuSe data. * * Returns: a #DfuElement, or %NULL for error **/ static DfuElement * dfu_element_from_dfuse (const guint8 *data, guint32 length, guint32 *consumed, GError **error) { DfuElement *element = NULL; DfuSeElementPrefix *el = (DfuSeElementPrefix *) data; guint32 size; g_autoptr(GBytes) contents = NULL; g_assert_cmpint(sizeof(DfuSeElementPrefix), ==, 8); /* check input buffer size */ if (length < sizeof(DfuSeElementPrefix)) { g_set_error (error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "invalid element data size %u", (guint32) length); return NULL; } /* check size */ size = GUINT32_FROM_LE (el->size); if (size + sizeof(DfuSeElementPrefix) > length) { g_set_error (error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "invalid element size %u, only %u bytes left", size, (guint32) (length - sizeof(DfuSeElementPrefix))); return NULL; } /* create new element */ element = dfu_element_new (); dfu_element_set_address (element, GUINT32_FROM_LE (el->address)); contents = g_bytes_new (data + sizeof(DfuSeElementPrefix), size); dfu_element_set_contents (element, contents); /* return size */ if (consumed != NULL) *consumed = (guint32) sizeof(DfuSeElementPrefix) + size; return element; }
/** * dfu_target_upload_element: **/ static DfuElement * dfu_target_upload_element (DfuTarget *target, guint32 address, gsize expected_size, GCancellable *cancellable, GError **error) { DfuTargetPrivate *priv = GET_PRIVATE (target); DfuSector *sector; DfuElement *element = NULL; GBytes *chunk_tmp; gsize chunk_size; gsize offset = 0; gsize total_size = 0; guint16 transfer_size = dfu_device_get_transfer_size (priv->device); guint8 *buffer; guint32 last_sector_id = G_MAXUINT; guint dfuse_sector_offset = 0; guint i; guint old_percentage = G_MAXUINT; g_autoptr(GBytes) contents = NULL; g_autoptr(GPtrArray) chunks = NULL; /* ST uses wBlockNum=0 for DfuSe commands and wBlockNum=1 is reserved */ if (dfu_device_has_dfuse_support (priv->device)) { 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++) { /* for DfuSe devices we need to handle 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_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 %" G_GSIZE_FORMAT, i, chunk_size); g_ptr_array_add (chunks, chunk_tmp); /* update UI */ if (chunk_size > 0) { guint percentage = (total_size * 100) / expected_size; if (percentage != old_percentage) { g_signal_emit (target, signals[SIGNAL_PERCENTAGE_CHANGED], 0, percentage); } } /* detect short write as EOF */ if (chunk_size < transfer_size) break; } /* check final size */ if (expected_size > 0) { if (total_size != expected_size) { g_set_error (error, DFU_ERROR, DFU_ERROR_INVALID_FILE, "invalid size, got %" G_GSIZE_FORMAT ", " "expected %" G_GSIZE_FORMAT , total_size, expected_size); return NULL; } } /* 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; }