mirror of
https://git.proxmox.com/git/fwupd
synced 2025-08-14 14:22:13 +00:00
Split out the DFU file formats to separate files
This commit is contained in:
parent
830bc932f2
commit
e1e50dc2fd
@ -48,14 +48,22 @@ libdfu_la_SOURCES = \
|
||||
dfu-device-private.h \
|
||||
dfu-element.c \
|
||||
dfu-element.h \
|
||||
dfu-element-private.h \
|
||||
dfu-error.c \
|
||||
dfu-error.h \
|
||||
dfu-firmware.c \
|
||||
dfu-firmware.h \
|
||||
dfu-format-dfu.c \
|
||||
dfu-format-dfu.h \
|
||||
dfu-format-dfuse.c \
|
||||
dfu-format-dfuse.h \
|
||||
dfu-format-ihex.c \
|
||||
dfu-format-ihex.h \
|
||||
dfu-format-metadata.c \
|
||||
dfu-format-metadata.h \
|
||||
dfu-format-raw.c \
|
||||
dfu-format-raw.h \
|
||||
dfu-image.c \
|
||||
dfu-image.h \
|
||||
dfu-image-private.h \
|
||||
dfu-sector.c \
|
||||
dfu-sector.h \
|
||||
dfu-sector-private.h \
|
||||
|
@ -37,7 +37,7 @@
|
||||
#include <stdio.h>
|
||||
|
||||
#include "dfu-common.h"
|
||||
#include "dfu-element-private.h"
|
||||
#include "dfu-element.h"
|
||||
#include "dfu-error.h"
|
||||
|
||||
static void dfu_element_finalize (GObject *object);
|
||||
@ -240,95 +240,3 @@ dfu_element_set_target_size (DfuElement *element, guint32 target_size)
|
||||
g_bytes_unref (priv->contents);
|
||||
priv->contents = g_bytes_new_take (buf, target_size);
|
||||
}
|
||||
|
||||
/* DfuSe element header */
|
||||
typedef struct __attribute__((packed)) {
|
||||
guint32 address;
|
||||
guint32 size;
|
||||
} DfuSeElementPrefix;
|
||||
|
||||
/**
|
||||
* 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
|
||||
**/
|
||||
DfuElement *
|
||||
dfu_element_from_dfuse (const guint8 *data,
|
||||
guint32 length,
|
||||
guint32 *consumed,
|
||||
GError **error)
|
||||
{
|
||||
DfuElement *element = NULL;
|
||||
DfuElementPrivate *priv;
|
||||
DfuSeElementPrefix *el = (DfuSeElementPrefix *) data;
|
||||
guint32 size;
|
||||
|
||||
g_assert_cmpint(sizeof(DfuSeElementPrefix), ==, 8);
|
||||
|
||||
/* check input buffer size */
|
||||
if (length < sizeof(DfuSeElementPrefix)) {
|
||||
g_set_error (error,
|
||||
DFU_ERROR,
|
||||
DFU_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,
|
||||
DFU_ERROR,
|
||||
DFU_ERROR_INTERNAL,
|
||||
"invalid element size %u, only %u bytes left",
|
||||
size,
|
||||
(guint32) (length - sizeof(DfuSeElementPrefix)));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* create new element */
|
||||
element = dfu_element_new ();
|
||||
priv = GET_PRIVATE (element);
|
||||
priv->address = GUINT32_FROM_LE (el->address);
|
||||
priv->contents = g_bytes_new (data + sizeof(DfuSeElementPrefix), size);
|
||||
|
||||
/* return size */
|
||||
if (consumed != NULL)
|
||||
*consumed = (guint32) sizeof(DfuSeElementPrefix) + size;
|
||||
|
||||
return element;
|
||||
}
|
||||
|
||||
/**
|
||||
* dfu_element_to_dfuse: (skip)
|
||||
* @element: a #DfuElement
|
||||
*
|
||||
* Packs a DfuSe element.
|
||||
*
|
||||
* Returns: (transfer full): the packed data
|
||||
**/
|
||||
GBytes *
|
||||
dfu_element_to_dfuse (DfuElement *element)
|
||||
{
|
||||
DfuElementPrivate *priv = GET_PRIVATE (element);
|
||||
DfuSeElementPrefix *el;
|
||||
const guint8 *data;
|
||||
gsize length;
|
||||
guint8 *buf;
|
||||
|
||||
data = g_bytes_get_data (priv->contents, &length);
|
||||
buf = g_malloc0 (length + sizeof (DfuSeElementPrefix));
|
||||
el = (DfuSeElementPrefix *) buf;
|
||||
el->address = GUINT32_TO_LE (priv->address);
|
||||
el->size = GUINT32_TO_LE (length);
|
||||
|
||||
memcpy (buf + sizeof (DfuSeElementPrefix), data, length);
|
||||
return g_bytes_new_take (buf, length + sizeof (DfuSeElementPrefix));
|
||||
}
|
||||
|
@ -40,7 +40,10 @@
|
||||
#include "dfu-common.h"
|
||||
#include "dfu-error.h"
|
||||
#include "dfu-firmware.h"
|
||||
#include "dfu-image-private.h"
|
||||
#include "dfu-format-dfu.h"
|
||||
#include "dfu-format-ihex.h"
|
||||
#include "dfu-format-raw.h"
|
||||
#include "dfu-image.h"
|
||||
|
||||
static void dfu_firmware_finalize (GObject *object);
|
||||
|
||||
@ -50,7 +53,6 @@ typedef struct {
|
||||
guint16 vid;
|
||||
guint16 pid;
|
||||
guint16 release;
|
||||
guint32 crc;
|
||||
DfuCipherKind cipher_kind;
|
||||
DfuFirmwareFormat format;
|
||||
} DfuFirmwarePrivate;
|
||||
@ -386,528 +388,6 @@ dfu_firmware_set_format (DfuFirmware *firmware, DfuFirmwareFormat format)
|
||||
priv->format = format;
|
||||
}
|
||||
|
||||
typedef struct __attribute__((packed)) {
|
||||
guint16 release;
|
||||
guint16 pid;
|
||||
guint16 vid;
|
||||
guint16 ver;
|
||||
guint8 sig[3];
|
||||
guint8 len;
|
||||
guint32 crc;
|
||||
} DfuFirmwareFooter;
|
||||
|
||||
static guint32 _crctbl[] = {
|
||||
0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f,
|
||||
0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
|
||||
0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2,
|
||||
0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
|
||||
0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9,
|
||||
0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
|
||||
0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c,
|
||||
0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
|
||||
0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423,
|
||||
0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
|
||||
0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106,
|
||||
0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
|
||||
0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d,
|
||||
0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
|
||||
0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,
|
||||
0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
|
||||
0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7,
|
||||
0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
|
||||
0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa,
|
||||
0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
|
||||
0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81,
|
||||
0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
|
||||
0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84,
|
||||
0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
|
||||
0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,
|
||||
0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
|
||||
0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e,
|
||||
0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
|
||||
0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55,
|
||||
0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
|
||||
0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28,
|
||||
0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
|
||||
0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f,
|
||||
0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
|
||||
0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
|
||||
0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
|
||||
0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69,
|
||||
0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
|
||||
0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc,
|
||||
0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
|
||||
0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693,
|
||||
0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
|
||||
0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d };
|
||||
|
||||
static guint32
|
||||
dfu_firmware_generate_crc32 (const guint8 *data, gsize length)
|
||||
{
|
||||
guint i;
|
||||
guint32 accum = 0xffffffff;
|
||||
for (i = 0; i < length; i++)
|
||||
accum = _crctbl[(accum^data[i]) & 0xff] ^ (accum >> 8);
|
||||
return accum;
|
||||
}
|
||||
|
||||
static guint8
|
||||
dfu_firmware_ihex_parse_uint8 (const gchar *data, guint pos)
|
||||
{
|
||||
gchar buffer[3];
|
||||
memcpy (buffer, data + pos, 2);
|
||||
buffer[2] = '\0';
|
||||
return (guint8) g_ascii_strtoull (buffer, NULL, 16);
|
||||
}
|
||||
|
||||
static guint16
|
||||
dfu_firmware_ihex_parse_uint16 (const gchar *data, guint pos)
|
||||
{
|
||||
gchar buffer[5];
|
||||
memcpy (buffer, data + pos, 4);
|
||||
buffer[4] = '\0';
|
||||
return (guint16) g_ascii_strtoull (buffer, NULL, 16);
|
||||
}
|
||||
|
||||
#define DFU_INHX32_RECORD_TYPE_DATA 0
|
||||
#define DFU_INHX32_RECORD_TYPE_EOF 1
|
||||
#define DFU_INHX32_RECORD_TYPE_EXTENDED 4
|
||||
|
||||
static gboolean
|
||||
dfu_firmware_add_ihex (DfuFirmware *firmware, GBytes *bytes,
|
||||
DfuFirmwareParseFlags flags, GError **error)
|
||||
{
|
||||
const gchar *in_buffer;
|
||||
gsize len_in;
|
||||
guint16 addr_high = 0;
|
||||
guint16 addr_low = 0;
|
||||
guint32 addr32 = 0;
|
||||
guint32 addr32_last = 0;
|
||||
guint8 checksum;
|
||||
guint8 data_tmp;
|
||||
guint8 len_tmp;
|
||||
guint8 type;
|
||||
guint end;
|
||||
guint i;
|
||||
guint j;
|
||||
guint offset = 0;
|
||||
g_autoptr(DfuElement) element = NULL;
|
||||
g_autoptr(DfuImage) image = NULL;
|
||||
g_autoptr(GBytes) contents = NULL;
|
||||
g_autoptr(GString) string = NULL;
|
||||
|
||||
g_return_val_if_fail (bytes != NULL, FALSE);
|
||||
|
||||
/* create element */
|
||||
image = dfu_image_new ();
|
||||
dfu_image_set_name (image, "ihex");
|
||||
element = dfu_element_new ();
|
||||
|
||||
/* parse records */
|
||||
in_buffer = g_bytes_get_data (bytes, &len_in);
|
||||
string = g_string_new ("");
|
||||
while (offset < len_in) {
|
||||
|
||||
/* check starting token */
|
||||
if (in_buffer[offset] != ':') {
|
||||
g_set_error (error,
|
||||
DFU_ERROR,
|
||||
DFU_ERROR_INVALID_FILE,
|
||||
"invalid starting token, got %c at %x",
|
||||
in_buffer[offset], offset);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* check there's enough data for the smallest possible record */
|
||||
if (offset + 12 > (guint) len_in) {
|
||||
g_set_error (error,
|
||||
DFU_ERROR,
|
||||
DFU_ERROR_INVALID_FILE,
|
||||
"record incomplete at %u, length %u",
|
||||
offset, (guint) len_in);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* length, 16-bit address, type */
|
||||
len_tmp = dfu_firmware_ihex_parse_uint8 (in_buffer, offset+1);
|
||||
addr_low = dfu_firmware_ihex_parse_uint16 (in_buffer, offset+3);
|
||||
type = dfu_firmware_ihex_parse_uint8 (in_buffer, offset+7);
|
||||
|
||||
/* position of checksum */
|
||||
end = offset + 9 + len_tmp * 2;
|
||||
if (end > (guint) len_in) {
|
||||
g_set_error (error,
|
||||
DFU_ERROR,
|
||||
DFU_ERROR_INVALID_FILE,
|
||||
"checksum > file length: %u",
|
||||
end);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* verify checksum */
|
||||
if ((flags & DFU_FIRMWARE_PARSE_FLAG_NO_CRC_TEST) == 0) {
|
||||
checksum = 0;
|
||||
for (i = offset + 1; i < end + 2; i += 2) {
|
||||
data_tmp = dfu_firmware_ihex_parse_uint8 (in_buffer, i);
|
||||
checksum += data_tmp;
|
||||
}
|
||||
if (checksum != 0) {
|
||||
g_set_error_literal (error,
|
||||
DFU_ERROR,
|
||||
DFU_ERROR_INVALID_FILE,
|
||||
"invalid record checksum");
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
/* process different record types */
|
||||
switch (type) {
|
||||
case DFU_INHX32_RECORD_TYPE_DATA:
|
||||
/* if not contiguous with previous record */
|
||||
if ((addr_high + addr_low) != addr32) {
|
||||
if (addr32 == 0x0) {
|
||||
g_debug ("base address %04x", addr_low);
|
||||
dfu_element_set_address (element, addr_low);
|
||||
}
|
||||
// addr32 = addr_high + addr_low;
|
||||
addr32 = ((guint32) addr_high << 16) + addr_low;
|
||||
}
|
||||
|
||||
/* parse bytes from line */
|
||||
for (i = offset + 9; i < end; i += 2) {
|
||||
/* any holes in the hex record */
|
||||
guint32 len_hole = addr32 - addr32_last;
|
||||
if (addr32_last > 0x0 && len_hole > 1) {
|
||||
for (j = 1; j < len_hole; j++) {
|
||||
g_debug ("filling address 0x%04x",
|
||||
addr32_last + j);
|
||||
/* although 0xff might be clearer,
|
||||
* we can't write 0xffff to pic14 */
|
||||
g_string_append_c (string, 0x00);
|
||||
}
|
||||
}
|
||||
/* write into buf */
|
||||
data_tmp = dfu_firmware_ihex_parse_uint8 (in_buffer, i);
|
||||
g_string_append_c (string, (gchar) data_tmp);
|
||||
g_debug ("writing address 0x%04x", addr32);
|
||||
addr32_last = addr32++;
|
||||
}
|
||||
break;
|
||||
case DFU_INHX32_RECORD_TYPE_EOF:
|
||||
break;
|
||||
case DFU_INHX32_RECORD_TYPE_EXTENDED:
|
||||
addr_high = dfu_firmware_ihex_parse_uint16 (in_buffer, offset+9);
|
||||
g_error ("set base address %x", addr_high);
|
||||
addr32 = ((guint32) addr_high << 16) + addr_low;
|
||||
break;
|
||||
default:
|
||||
g_set_error (error,
|
||||
DFU_ERROR,
|
||||
DFU_ERROR_INVALID_FILE,
|
||||
"invalid ihex record type %i",
|
||||
type);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* ignore any line return */
|
||||
offset = end + 2;
|
||||
for (; offset < len_in; offset++) {
|
||||
if (in_buffer[offset] != '\n' &&
|
||||
in_buffer[offset] != '\r')
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* add single image */
|
||||
contents = g_bytes_new (string->str, string->len);
|
||||
dfu_element_set_contents (element, contents);
|
||||
dfu_image_add_element (image, element);
|
||||
dfu_firmware_add_image (firmware, image);
|
||||
dfu_firmware_set_format (firmware, DFU_FIRMWARE_FORMAT_INTEL_HEX);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
dfu_firmware_write_data_ihex_element (DfuElement *element,
|
||||
GString *str,
|
||||
GError **error)
|
||||
{
|
||||
GBytes *contents;
|
||||
const guint8 *data;
|
||||
const guint chunk_size = 16;
|
||||
gsize len;
|
||||
|
||||
/* get number of chunks */
|
||||
contents = dfu_element_get_contents (element);
|
||||
data = g_bytes_get_data (contents, &len);
|
||||
for (gsize i = 0; i < len; i += chunk_size) {
|
||||
guint8 checksum = 0;
|
||||
|
||||
/* length, 16-bit address, type */
|
||||
gsize chunk_len = MIN (len - i, 16);
|
||||
g_string_append_printf (str, ":%02X%04X%02X",
|
||||
(guint) chunk_len,
|
||||
(guint) (dfu_element_get_address (element) + i),
|
||||
(guint) DFU_INHX32_RECORD_TYPE_DATA);
|
||||
for (gsize j = 0; j < chunk_len; j++)
|
||||
g_string_append_printf (str, "%02X", data[i+j]);
|
||||
|
||||
/* add checksum */
|
||||
for (gsize j = 0; j < (chunk_len * 2) + 8; j++)
|
||||
checksum += (guint8) str->str[str->len - (j + 1)];
|
||||
g_string_append_printf (str, "%02X\n", checksum);
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static GBytes *
|
||||
dfu_firmware_write_data_ihex (DfuFirmware *firmware, GError **error)
|
||||
{
|
||||
DfuFirmwarePrivate *priv = GET_PRIVATE (firmware);
|
||||
DfuElement *element;
|
||||
DfuImage *image;
|
||||
GPtrArray *elements;
|
||||
guint i;
|
||||
guint j;
|
||||
g_autoptr(GString) str = NULL;
|
||||
|
||||
/* write all the element data */
|
||||
str = g_string_new ("");
|
||||
for (i = 0; i < priv->images->len; i++) {
|
||||
image = g_ptr_array_index (priv->images, i);
|
||||
elements = dfu_image_get_elements (image);
|
||||
for (j = 0; j < elements->len; j++) {
|
||||
element = g_ptr_array_index (elements, j);
|
||||
if (!dfu_firmware_write_data_ihex_element (element,
|
||||
str,
|
||||
error))
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* add EOF */
|
||||
g_string_append_printf (str, ":000000%02XFF\n",
|
||||
(guint) DFU_INHX32_RECORD_TYPE_EOF);
|
||||
return g_bytes_new (str->str, str->len);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
dfu_firmware_add_binary (DfuFirmware *firmware, GBytes *bytes, 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;
|
||||
}
|
||||
|
||||
/* DfuSe header */
|
||||
typedef struct __attribute__((packed)) {
|
||||
guint8 sig[5];
|
||||
guint8 ver;
|
||||
guint32 image_size;
|
||||
guint8 targets;
|
||||
} DfuSePrefix;
|
||||
|
||||
static gboolean
|
||||
dfu_firmware_add_dfuse (DfuFirmware *firmware, GBytes *bytes, GError **error)
|
||||
{
|
||||
DfuSePrefix *prefix;
|
||||
gsize len;
|
||||
guint32 offset = sizeof(DfuSePrefix);
|
||||
guint8 *data;
|
||||
guint i;
|
||||
|
||||
/* check the prefix (BE) */
|
||||
data = (guint8 *) g_bytes_get_data (bytes, &len);
|
||||
prefix = (DfuSePrefix *) data;
|
||||
if (memcmp (prefix->sig, "DfuSe", 5) != 0) {
|
||||
g_set_error_literal (error,
|
||||
DFU_ERROR,
|
||||
DFU_ERROR_INTERNAL,
|
||||
"invalid DfuSe prefix");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* check the version */
|
||||
if (prefix->ver != 0x01) {
|
||||
g_set_error (error,
|
||||
DFU_ERROR,
|
||||
DFU_ERROR_INTERNAL,
|
||||
"invalid DfuSe version, got %02x",
|
||||
prefix->ver);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* check image size */
|
||||
if (GUINT32_FROM_LE (prefix->image_size) != len) {
|
||||
g_set_error (error,
|
||||
DFU_ERROR,
|
||||
DFU_ERROR_INTERNAL,
|
||||
"invalid DfuSe image size, "
|
||||
"got %" G_GUINT32_FORMAT ", "
|
||||
"expected %" G_GSIZE_FORMAT,
|
||||
GUINT32_FROM_LE (prefix->image_size),
|
||||
len);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* parse the image targets */
|
||||
len -= sizeof(DfuSePrefix);
|
||||
for (i = 0; i < prefix->targets; i++) {
|
||||
guint consumed;
|
||||
g_autoptr(DfuImage) image = NULL;
|
||||
image = dfu_image_from_dfuse (data + offset, (guint32) len,
|
||||
&consumed, error);
|
||||
if (image == NULL)
|
||||
return FALSE;
|
||||
dfu_firmware_add_image (firmware, image);
|
||||
offset += consumed;
|
||||
len -= consumed;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static GBytes *
|
||||
dfu_firmware_write_data_dfuse (DfuFirmware *firmware, GError **error)
|
||||
{
|
||||
DfuFirmwarePrivate *priv = GET_PRIVATE (firmware);
|
||||
DfuSePrefix *prefix;
|
||||
guint i;
|
||||
guint32 image_size_total = 0;
|
||||
guint32 offset = sizeof (DfuSePrefix);
|
||||
guint8 *buf;
|
||||
g_autoptr(GPtrArray) dfuse_images = NULL;
|
||||
|
||||
/* get all the image data */
|
||||
dfuse_images = g_ptr_array_new_with_free_func ((GDestroyNotify) g_bytes_unref);
|
||||
for (i = 0; i < priv->images->len; i++) {
|
||||
DfuImage *im = g_ptr_array_index (priv->images, i);
|
||||
GBytes *contents;
|
||||
contents = dfu_image_to_dfuse (im);
|
||||
image_size_total += (guint32) g_bytes_get_size (contents);
|
||||
g_ptr_array_add (dfuse_images, contents);
|
||||
}
|
||||
g_debug ("image_size_total: %" G_GUINT32_FORMAT, image_size_total);
|
||||
|
||||
buf = g_malloc0 (sizeof (DfuSePrefix) + image_size_total);
|
||||
|
||||
/* DfuSe header */
|
||||
prefix = (DfuSePrefix *) buf;
|
||||
memcpy (prefix->sig, "DfuSe", 5);
|
||||
prefix->ver = 0x01;
|
||||
prefix->image_size = GUINT32_TO_LE (offset + image_size_total);
|
||||
if (priv->images->len > G_MAXUINT8) {
|
||||
g_set_error (error,
|
||||
DFU_ERROR,
|
||||
DFU_ERROR_INTERNAL,
|
||||
"too many (%u) images to write DfuSe file",
|
||||
priv->images->len);
|
||||
return FALSE;
|
||||
}
|
||||
prefix->targets = (guint8) priv->images->len;
|
||||
|
||||
/* copy images */
|
||||
for (i = 0; i < dfuse_images->len; i++) {
|
||||
GBytes *contents = g_ptr_array_index (dfuse_images, i);
|
||||
gsize length;
|
||||
const guint8 *data;
|
||||
data = g_bytes_get_data (contents, &length);
|
||||
memcpy (buf + offset, data, length);
|
||||
offset += (guint32) length;
|
||||
}
|
||||
|
||||
/* return blob */
|
||||
return g_bytes_new_take (buf, sizeof (DfuSePrefix) + image_size_total);
|
||||
}
|
||||
|
||||
/**
|
||||
* dfu_firmware_parse_metadata:
|
||||
*
|
||||
* The representation in memory is as follows:
|
||||
*
|
||||
* uint16 signature='MD'
|
||||
* uint8 number_of_keys
|
||||
* uint8 number_of_keys
|
||||
* uint8 key(n)_length
|
||||
* ... key(n) (no NUL)
|
||||
* uint8 value(n)_length
|
||||
* ... value(n) (no NUL)
|
||||
* <existing DFU footer>
|
||||
**/
|
||||
static gboolean
|
||||
dfu_firmware_parse_metadata (DfuFirmware *firmware,
|
||||
const guint8 *data,
|
||||
guint data_length,
|
||||
guint8 footer_size,
|
||||
GError **error)
|
||||
{
|
||||
guint i;
|
||||
guint idx = data_length - footer_size + 2;
|
||||
guint kvlen;
|
||||
guint number_keys;
|
||||
|
||||
/* not big enough */
|
||||
if (footer_size <= 0x10)
|
||||
return TRUE;
|
||||
|
||||
/* signature invalid */
|
||||
if (memcmp (&data[data_length - footer_size], "MD", 2) != 0)
|
||||
return TRUE;
|
||||
|
||||
/* parse key=value store */
|
||||
number_keys = data[idx++];
|
||||
for (i = 0; i < number_keys; i++) {
|
||||
g_autofree gchar *key = NULL;
|
||||
g_autofree gchar *value = NULL;
|
||||
|
||||
/* parse key */
|
||||
kvlen = data[idx++];
|
||||
if (kvlen > 233) {
|
||||
g_set_error (error,
|
||||
DFU_ERROR,
|
||||
DFU_ERROR_INTERNAL,
|
||||
"metadata table corrupt, key=%u",
|
||||
kvlen);
|
||||
return FALSE;
|
||||
}
|
||||
if (idx + kvlen + 0x10 > data_length) {
|
||||
g_set_error_literal (error,
|
||||
DFU_ERROR,
|
||||
DFU_ERROR_INTERNAL,
|
||||
"metadata table corrupt");
|
||||
return FALSE;
|
||||
}
|
||||
key = g_strndup ((const gchar *) data + idx, kvlen);
|
||||
idx += kvlen;
|
||||
|
||||
/* parse value */
|
||||
kvlen = data[idx++];
|
||||
if (kvlen > 233) {
|
||||
g_set_error (error,
|
||||
DFU_ERROR,
|
||||
DFU_ERROR_INTERNAL,
|
||||
"metadata table corrupt, value=%u",
|
||||
kvlen);
|
||||
return FALSE;
|
||||
}
|
||||
if (idx + kvlen + 0x10 > data_length) {
|
||||
g_set_error_literal (error,
|
||||
DFU_ERROR,
|
||||
DFU_ERROR_INTERNAL,
|
||||
"metadata table corrupt");
|
||||
return FALSE;
|
||||
}
|
||||
value = g_strndup ((const gchar *) data + idx, kvlen);
|
||||
idx += kvlen;
|
||||
dfu_firmware_set_metadata (firmware, key, value);
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* dfu_firmware_parse_data:
|
||||
* @firmware: a #DfuFirmware
|
||||
@ -925,112 +405,32 @@ gboolean
|
||||
dfu_firmware_parse_data (DfuFirmware *firmware, GBytes *bytes,
|
||||
DfuFirmwareParseFlags flags, GError **error)
|
||||
{
|
||||
DfuFirmwareFooter *ftr;
|
||||
DfuFirmwarePrivate *priv = GET_PRIVATE (firmware);
|
||||
const gchar *cipher_str;
|
||||
gsize len;
|
||||
guint32 crc_new;
|
||||
guint8 *data;
|
||||
g_autoptr(GBytes) contents = NULL;
|
||||
|
||||
g_return_val_if_fail (DFU_IS_FIRMWARE (firmware), FALSE);
|
||||
g_return_val_if_fail (bytes != NULL, FALSE);
|
||||
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
|
||||
|
||||
/* sanity check */
|
||||
g_assert_cmpint (sizeof(DfuFirmwareFooter), ==, 16);
|
||||
g_assert_cmpint (sizeof(DfuSePrefix), ==, 11);
|
||||
|
||||
/* set defaults */
|
||||
priv->vid = 0xffff;
|
||||
priv->pid = 0xffff;
|
||||
priv->release = 0xffff;
|
||||
|
||||
/* this is ihex */
|
||||
data = (guint8 *) g_bytes_get_data (bytes, &len);
|
||||
if (data[0] == ':')
|
||||
return dfu_firmware_add_ihex (firmware, bytes, flags, error);
|
||||
/* try to get format if not already set */
|
||||
if (priv->format == DFU_FIRMWARE_FORMAT_UNKNOWN)
|
||||
priv->format = dfu_firmware_detect_ihex (bytes);
|
||||
if (priv->format == DFU_FIRMWARE_FORMAT_UNKNOWN)
|
||||
priv->format = dfu_firmware_detect_dfu (bytes);
|
||||
if (priv->format == DFU_FIRMWARE_FORMAT_UNKNOWN)
|
||||
priv->format = dfu_firmware_detect_raw (bytes);
|
||||
|
||||
/* too small to be a DFU file */
|
||||
if (len < 16) {
|
||||
priv->format = DFU_FIRMWARE_FORMAT_RAW;
|
||||
return dfu_firmware_add_binary (firmware, bytes, error);
|
||||
}
|
||||
|
||||
/* check for DFU signature */
|
||||
ftr = (DfuFirmwareFooter *) &data[len - sizeof(DfuFirmwareFooter)];
|
||||
if (memcmp (ftr->sig, "UFD", 3) != 0) {
|
||||
priv->format = DFU_FIRMWARE_FORMAT_RAW;
|
||||
return dfu_firmware_add_binary (firmware, bytes, error);
|
||||
}
|
||||
|
||||
/* check version */
|
||||
priv->format = GUINT16_FROM_LE (ftr->ver);
|
||||
if ((flags & DFU_FIRMWARE_PARSE_FLAG_NO_VERSION_TEST) == 0) {
|
||||
if (priv->format != DFU_FIRMWARE_FORMAT_DFU_1_0 &&
|
||||
priv->format != DFU_FIRMWARE_FORMAT_DFUSE) {
|
||||
g_set_error (error,
|
||||
DFU_ERROR,
|
||||
DFU_ERROR_INTERNAL,
|
||||
"version check failed, got %04x",
|
||||
priv->format);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
/* verify the checksum */
|
||||
priv->crc = GUINT32_FROM_LE (ftr->crc);
|
||||
if ((flags & DFU_FIRMWARE_PARSE_FLAG_NO_CRC_TEST) == 0) {
|
||||
crc_new = dfu_firmware_generate_crc32 (data, len - 4);
|
||||
if (priv->crc != crc_new) {
|
||||
g_set_error (error,
|
||||
DFU_ERROR,
|
||||
DFU_ERROR_INTERNAL,
|
||||
"CRC failed, expected %04x, got %04x",
|
||||
crc_new, GUINT32_FROM_LE (ftr->crc));
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
/* set from footer */
|
||||
dfu_firmware_set_vid (firmware, GUINT16_FROM_LE (ftr->vid));
|
||||
dfu_firmware_set_pid (firmware, GUINT16_FROM_LE (ftr->pid));
|
||||
dfu_firmware_set_release (firmware, GUINT16_FROM_LE (ftr->release));
|
||||
|
||||
/* check reported length */
|
||||
if (ftr->len > len) {
|
||||
g_set_error (error,
|
||||
DFU_ERROR,
|
||||
DFU_ERROR_INTERNAL,
|
||||
"reported firmware size %04x larger than file %04x",
|
||||
(guint) ftr->len, (guint) len);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* parse the optional metadata segment */
|
||||
if ((flags & DFU_FIRMWARE_PARSE_FLAG_NO_METADATA) == 0) {
|
||||
if (!dfu_firmware_parse_metadata (firmware, data,
|
||||
(guint) len,
|
||||
(guint) ftr->len, error))
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* set this automatically */
|
||||
cipher_str = dfu_firmware_get_metadata (firmware, DFU_METADATA_KEY_CIPHER_KIND);
|
||||
if (cipher_str != NULL) {
|
||||
if (g_strcmp0 (cipher_str, "XTEA") == 0)
|
||||
priv->cipher_kind = DFU_CIPHER_KIND_XTEA;
|
||||
else
|
||||
g_warning ("Unknown CipherKind: %s", cipher_str);
|
||||
}
|
||||
|
||||
/* parse DfuSe prefix */
|
||||
contents = g_bytes_new_from_bytes (bytes, 0, len - ftr->len);
|
||||
if (priv->format == DFU_FIRMWARE_FORMAT_DFUSE)
|
||||
return dfu_firmware_add_dfuse (firmware, contents, error);
|
||||
|
||||
/* just copy old-plain DFU file */
|
||||
return dfu_firmware_add_binary (firmware, contents, error);
|
||||
/* handled easily */
|
||||
if (priv->format == DFU_FIRMWARE_FORMAT_INTEL_HEX)
|
||||
return dfu_firmware_from_ihex (firmware, bytes, flags, error);
|
||||
if (priv->format == DFU_FIRMWARE_FORMAT_DFU_1_0 ||
|
||||
priv->format == DFU_FIRMWARE_FORMAT_DFUSE)
|
||||
return dfu_firmware_from_dfu (firmware, bytes, flags, error);
|
||||
return dfu_firmware_from_raw (firmware, bytes, flags, error);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1092,6 +492,23 @@ dfu_firmware_get_metadata (DfuFirmware *firmware, const gchar *key)
|
||||
return g_hash_table_lookup (priv->metadata, key);
|
||||
}
|
||||
|
||||
/**
|
||||
* dfu_firmware_get_metadata_table:
|
||||
* @firmware: a #DfuFirmware
|
||||
*
|
||||
* Gets all metadata from the store.
|
||||
*
|
||||
* Return value: (transfer none): the metadata hash table
|
||||
*
|
||||
* Since: 0.6.3
|
||||
**/
|
||||
GHashTable *
|
||||
dfu_firmware_get_metadata_table (DfuFirmware *firmware)
|
||||
{
|
||||
DfuFirmwarePrivate *priv = GET_PRIVATE (firmware);
|
||||
return priv->metadata;
|
||||
}
|
||||
|
||||
/**
|
||||
* dfu_firmware_set_metadata:
|
||||
* @firmware: a #DfuFirmware
|
||||
@ -1127,131 +544,6 @@ dfu_firmware_remove_metadata (DfuFirmware *firmware, const gchar *key)
|
||||
g_hash_table_remove (priv->metadata, key);
|
||||
}
|
||||
|
||||
static GBytes *
|
||||
dfu_firmware_build_metadata_table (DfuFirmware *firmware, GError **error)
|
||||
{
|
||||
DfuFirmwarePrivate *priv = GET_PRIVATE (firmware);
|
||||
GList *l;
|
||||
guint8 mdbuf[239];
|
||||
guint idx = 0;
|
||||
guint number_keys;
|
||||
g_autoptr(GList) keys = NULL;
|
||||
|
||||
/* no metadata */
|
||||
if (g_hash_table_size (priv->metadata) == 0)
|
||||
return g_bytes_new (NULL, 0);
|
||||
|
||||
/* check the number of keys */
|
||||
keys = g_hash_table_get_keys (priv->metadata);
|
||||
number_keys = g_list_length (keys);
|
||||
if (number_keys > 59) {
|
||||
g_set_error (error,
|
||||
DFU_ERROR,
|
||||
DFU_ERROR_NOT_SUPPORTED,
|
||||
"too many metadata keys (%u)",
|
||||
number_keys);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* write the signature */
|
||||
mdbuf[idx++] = 'M';
|
||||
mdbuf[idx++] = 'D';
|
||||
mdbuf[idx++] = (guint8) number_keys;
|
||||
for (l = keys; l != NULL; l = l->next) {
|
||||
const gchar *key;
|
||||
const gchar *value;
|
||||
guint key_len;
|
||||
guint value_len;
|
||||
|
||||
/* check key and value length */
|
||||
key = l->data;
|
||||
key_len = (guint) strlen (key);
|
||||
if (key_len > 233) {
|
||||
g_set_error (error,
|
||||
DFU_ERROR,
|
||||
DFU_ERROR_NOT_SUPPORTED,
|
||||
"metdata key too long: %s",
|
||||
key);
|
||||
return NULL;
|
||||
}
|
||||
value = g_hash_table_lookup (priv->metadata, key);
|
||||
value_len = (guint) strlen (value);
|
||||
if (value_len > 233) {
|
||||
g_set_error (error,
|
||||
DFU_ERROR,
|
||||
DFU_ERROR_NOT_SUPPORTED,
|
||||
"value too long: %s",
|
||||
value);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* do we still have space? */
|
||||
if (idx + key_len + value_len + 2 > sizeof(mdbuf)) {
|
||||
g_set_error (error,
|
||||
DFU_ERROR,
|
||||
DFU_ERROR_NOT_SUPPORTED,
|
||||
"not enough space in metadata table, "
|
||||
"already used %u bytes", idx);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* write the key */
|
||||
mdbuf[idx++] = (guint8) key_len;
|
||||
memcpy(mdbuf + idx, key, key_len);
|
||||
idx += key_len;
|
||||
|
||||
/* write the value */
|
||||
mdbuf[idx++] = (guint8) value_len;
|
||||
memcpy(mdbuf + idx, value, value_len);
|
||||
idx += value_len;
|
||||
}
|
||||
g_debug ("metadata table was %u/%" G_GSIZE_FORMAT " bytes",
|
||||
idx, sizeof(mdbuf));
|
||||
return g_bytes_new (mdbuf, idx);
|
||||
}
|
||||
|
||||
static GBytes *
|
||||
dfu_firmware_add_footer (DfuFirmware *firmware, GBytes *contents, GError **error)
|
||||
{
|
||||
DfuFirmwarePrivate *priv = GET_PRIVATE (firmware);
|
||||
DfuFirmwareFooter *ftr;
|
||||
const guint8 *data_bin;
|
||||
const guint8 *data_md;
|
||||
gsize length_bin = 0;
|
||||
gsize length_md = 0;
|
||||
guint32 crc_new;
|
||||
guint8 *buf;
|
||||
g_autoptr(GBytes) metadata_table = NULL;
|
||||
|
||||
/* get any file metadata */
|
||||
metadata_table = dfu_firmware_build_metadata_table (firmware, error);
|
||||
if (metadata_table == NULL)
|
||||
return NULL;
|
||||
data_md = g_bytes_get_data (metadata_table, &length_md);
|
||||
|
||||
/* add the raw firmware data */
|
||||
data_bin = g_bytes_get_data (contents, &length_bin);
|
||||
buf = g_malloc0 (length_bin + length_md + 0x10);
|
||||
memcpy (buf + 0, data_bin, length_bin);
|
||||
|
||||
/* add the metadata table */
|
||||
memcpy (buf + length_bin, data_md, length_md);
|
||||
|
||||
/* set up LE footer */
|
||||
ftr = (DfuFirmwareFooter *) (buf + length_bin + length_md);
|
||||
ftr->release = GUINT16_TO_LE (priv->release);
|
||||
ftr->pid = GUINT16_TO_LE (priv->pid);
|
||||
ftr->vid = GUINT16_TO_LE (priv->vid);
|
||||
ftr->ver = GUINT16_TO_LE (priv->format);
|
||||
ftr->len = (guint8) (sizeof (DfuFirmwareFooter) + length_md);
|
||||
memcpy(ftr->sig, "UFD", 3);
|
||||
crc_new = dfu_firmware_generate_crc32 (buf, length_bin + length_md + 12);
|
||||
ftr->crc = GUINT32_TO_LE (crc_new);
|
||||
|
||||
/* return all data */
|
||||
return g_bytes_new_take (buf, length_bin + length_md + 0x10);
|
||||
}
|
||||
|
||||
/**
|
||||
* dfu_firmware_write_data:
|
||||
* @firmware: a #DfuFirmware
|
||||
@ -1267,7 +559,6 @@ GBytes *
|
||||
dfu_firmware_write_data (DfuFirmware *firmware, GError **error)
|
||||
{
|
||||
DfuFirmwarePrivate *priv = GET_PRIVATE (firmware);
|
||||
DfuImage *image;
|
||||
|
||||
g_return_val_if_fail (DFU_IS_FIRMWARE (firmware), NULL);
|
||||
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
|
||||
@ -1293,54 +584,17 @@ dfu_firmware_write_data (DfuFirmware *firmware, GError **error)
|
||||
}
|
||||
|
||||
/* raw */
|
||||
if (priv->format == DFU_FIRMWARE_FORMAT_RAW) {
|
||||
GBytes *contents;
|
||||
DfuElement *element;
|
||||
image = dfu_firmware_get_image_default (firmware);
|
||||
g_assert (image != NULL);
|
||||
element = dfu_image_get_element (image, 0);
|
||||
if (element == NULL) {
|
||||
g_set_error (error,
|
||||
DFU_ERROR,
|
||||
DFU_ERROR_NOT_FOUND,
|
||||
"no firmware element data to write");
|
||||
return NULL;
|
||||
}
|
||||
contents = dfu_element_get_contents (element);
|
||||
return g_bytes_ref (contents);
|
||||
}
|
||||
if (priv->format == DFU_FIRMWARE_FORMAT_RAW)
|
||||
return dfu_firmware_to_raw (firmware, error);
|
||||
|
||||
/* plain-old DFU */
|
||||
if (priv->format == DFU_FIRMWARE_FORMAT_DFU_1_0) {
|
||||
GBytes *contents;
|
||||
DfuElement *element;
|
||||
image = dfu_firmware_get_image_default (firmware);
|
||||
g_assert (image != NULL);
|
||||
element = dfu_image_get_element (image, 0);
|
||||
if (element == NULL) {
|
||||
g_set_error (error,
|
||||
DFU_ERROR,
|
||||
DFU_ERROR_NOT_FOUND,
|
||||
"no firmware element data to write");
|
||||
return NULL;
|
||||
}
|
||||
contents = dfu_element_get_contents (element);
|
||||
g_assert (contents != NULL);
|
||||
return dfu_firmware_add_footer (firmware, contents, error);
|
||||
}
|
||||
|
||||
/* DfuSe */
|
||||
if (priv->format == DFU_FIRMWARE_FORMAT_DFUSE) {
|
||||
g_autoptr(GBytes) contents = NULL;
|
||||
contents = dfu_firmware_write_data_dfuse (firmware, error);
|
||||
if (contents == NULL)
|
||||
return NULL;
|
||||
return dfu_firmware_add_footer (firmware, contents, error);
|
||||
}
|
||||
/* DFU or DfuSe*/
|
||||
if (priv->format == DFU_FIRMWARE_FORMAT_DFU_1_0 ||
|
||||
priv->format == DFU_FIRMWARE_FORMAT_DFUSE)
|
||||
return dfu_firmware_to_dfu (firmware, error);
|
||||
|
||||
/* Intel HEX */
|
||||
if (priv->format == DFU_FIRMWARE_FORMAT_INTEL_HEX)
|
||||
return dfu_firmware_write_data_ihex (firmware, error);
|
||||
return dfu_firmware_to_ihex (firmware, error);
|
||||
|
||||
/* invalid */
|
||||
g_set_error (error,
|
||||
@ -1420,7 +674,6 @@ dfu_firmware_to_string (DfuFirmware *firmware)
|
||||
g_string_append_printf (str, "vid: 0x%04x\n", priv->vid);
|
||||
g_string_append_printf (str, "pid: 0x%04x\n", priv->pid);
|
||||
g_string_append_printf (str, "release: 0x%04x\n", priv->release);
|
||||
g_string_append_printf (str, "crc: 0x%08x\n", priv->crc);
|
||||
g_string_append_printf (str, "format: %s [0x%04x]\n",
|
||||
dfu_firmware_format_to_string (priv->format),
|
||||
priv->format);
|
||||
@ -1495,3 +748,19 @@ dfu_firmware_get_cipher_kind (DfuFirmware *firmware)
|
||||
g_return_val_if_fail (DFU_IS_FIRMWARE (firmware), 0);
|
||||
return priv->cipher_kind;
|
||||
}
|
||||
|
||||
/**
|
||||
* dfu_firmware_set_cipher_kind:
|
||||
* @firmware: a #DfuFirmware
|
||||
*
|
||||
* Sets the kind of cipher used by the firmware file.
|
||||
*
|
||||
* Since: 0.6.3
|
||||
**/
|
||||
void
|
||||
dfu_firmware_set_cipher_kind (DfuFirmware *firmware, DfuCipherKind cipher_kind)
|
||||
{
|
||||
DfuFirmwarePrivate *priv = GET_PRIVATE (firmware);
|
||||
g_return_if_fail (DFU_IS_FIRMWARE (firmware));
|
||||
priv->cipher_kind = cipher_kind;
|
||||
}
|
||||
|
@ -114,6 +114,8 @@ void dfu_firmware_set_release (DfuFirmware *firmware,
|
||||
guint16 release);
|
||||
void dfu_firmware_set_format (DfuFirmware *firmware,
|
||||
DfuFirmwareFormat format);
|
||||
void dfu_firmware_set_cipher_kind (DfuFirmware *firmware,
|
||||
DfuCipherKind cipher_kind);
|
||||
|
||||
gboolean dfu_firmware_parse_data (DfuFirmware *firmware,
|
||||
GBytes *bytes,
|
||||
@ -133,6 +135,7 @@ gboolean dfu_firmware_write_file (DfuFirmware *firmware,
|
||||
GError **error);
|
||||
gchar *dfu_firmware_to_string (DfuFirmware *firmware);
|
||||
|
||||
GHashTable *dfu_firmware_get_metadata_table(DfuFirmware *firmware);
|
||||
const gchar *dfu_firmware_get_metadata (DfuFirmware *firmware,
|
||||
const gchar *key);
|
||||
void dfu_firmware_set_metadata (DfuFirmware *firmware,
|
||||
|
323
libdfu/dfu-format-dfu.c
Normal file
323
libdfu/dfu-format-dfu.c
Normal file
@ -0,0 +1,323 @@
|
||||
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
|
||||
*
|
||||
* Copyright (C) 2015-2016 Richard Hughes <richard@hughsie.com>
|
||||
*
|
||||
* Licensed under the GNU Lesser General Public License Version 2.1
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "dfu-element.h"
|
||||
#include "dfu-format-dfu.h"
|
||||
#include "dfu-format-metadata.h"
|
||||
#include "dfu-format-dfuse.h"
|
||||
#include "dfu-format-raw.h"
|
||||
#include "dfu-image.h"
|
||||
#include "dfu-error.h"
|
||||
|
||||
typedef struct __attribute__((packed)) {
|
||||
guint16 release;
|
||||
guint16 pid;
|
||||
guint16 vid;
|
||||
guint16 ver;
|
||||
guint8 sig[3];
|
||||
guint8 len;
|
||||
guint32 crc;
|
||||
} DfuFirmwareFooter;
|
||||
|
||||
/**
|
||||
* dfu_firmware_detect_dfu: (skip)
|
||||
* @bytes: data to parse
|
||||
*
|
||||
* Attempts to sniff the data and work out the firmware format
|
||||
*
|
||||
* Returns: a #DfuFirmwareFormat, e.g. %DFU_FIRMWARE_FORMAT_RAW
|
||||
**/
|
||||
DfuFirmwareFormat
|
||||
dfu_firmware_detect_dfu (GBytes *bytes)
|
||||
{
|
||||
DfuFirmwareFooter *ftr;
|
||||
guint8 *data;
|
||||
gsize len;
|
||||
|
||||
/* check data size */
|
||||
data = (guint8 *) g_bytes_get_data (bytes, &len);
|
||||
if (len < 16)
|
||||
return DFU_FIRMWARE_FORMAT_UNKNOWN;
|
||||
|
||||
/* check for DFU signature */
|
||||
ftr = (DfuFirmwareFooter *) &data[len - sizeof(DfuFirmwareFooter)];
|
||||
if (memcmp (ftr->sig, "UFD", 3) != 0)
|
||||
return DFU_FIRMWARE_FORMAT_RAW;
|
||||
|
||||
return GUINT16_FROM_LE (ftr->ver);
|
||||
}
|
||||
|
||||
static guint32 _crctbl[] = {
|
||||
0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f,
|
||||
0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
|
||||
0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2,
|
||||
0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
|
||||
0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9,
|
||||
0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
|
||||
0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c,
|
||||
0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
|
||||
0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423,
|
||||
0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
|
||||
0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106,
|
||||
0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
|
||||
0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d,
|
||||
0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
|
||||
0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,
|
||||
0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
|
||||
0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7,
|
||||
0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
|
||||
0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa,
|
||||
0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
|
||||
0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81,
|
||||
0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
|
||||
0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84,
|
||||
0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
|
||||
0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,
|
||||
0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
|
||||
0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e,
|
||||
0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
|
||||
0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55,
|
||||
0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
|
||||
0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28,
|
||||
0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
|
||||
0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f,
|
||||
0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
|
||||
0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
|
||||
0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
|
||||
0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69,
|
||||
0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
|
||||
0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc,
|
||||
0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
|
||||
0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693,
|
||||
0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
|
||||
0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d };
|
||||
|
||||
static guint32
|
||||
dfu_firmware_generate_crc32 (const guint8 *data, gsize length)
|
||||
{
|
||||
guint i;
|
||||
guint32 accum = 0xffffffff;
|
||||
for (i = 0; i < length; i++)
|
||||
accum = _crctbl[(accum^data[i]) & 0xff] ^ (accum >> 8);
|
||||
return accum;
|
||||
}
|
||||
|
||||
/**
|
||||
* dfu_firmware_from_dfu: (skip)
|
||||
* @firmware: a #DfuFirmware
|
||||
* @bytes: data to parse
|
||||
* @flags: some #DfuFirmwareParseFlags
|
||||
* @error: a #GError, or %NULL
|
||||
*
|
||||
* Unpacks into a firmware object from dfu data.
|
||||
*
|
||||
* Returns: %TRUE for success
|
||||
**/
|
||||
gboolean
|
||||
dfu_firmware_from_dfu (DfuFirmware *firmware,
|
||||
GBytes *bytes,
|
||||
DfuFirmwareParseFlags flags,
|
||||
GError **error)
|
||||
{
|
||||
DfuFirmwareFooter *ftr;
|
||||
const gchar *cipher_str;
|
||||
gsize len;
|
||||
guint32 crc;
|
||||
guint32 crc_new;
|
||||
guint8 *data;
|
||||
g_autoptr(GBytes) contents = NULL;
|
||||
|
||||
/* check data size */
|
||||
data = (guint8 *) g_bytes_get_data (bytes, &len);
|
||||
if (len < 16) {
|
||||
g_set_error_literal (error,
|
||||
DFU_ERROR,
|
||||
DFU_ERROR_INTERNAL,
|
||||
"size check failed, too small");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* check for DFU signature */
|
||||
ftr = (DfuFirmwareFooter *) &data[len - sizeof(DfuFirmwareFooter)];
|
||||
if (memcmp (ftr->sig, "UFD", 3) != 0) {
|
||||
g_set_error_literal (error,
|
||||
DFU_ERROR,
|
||||
DFU_ERROR_INTERNAL,
|
||||
"no DFU signature");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* check version */
|
||||
if ((flags & DFU_FIRMWARE_PARSE_FLAG_NO_VERSION_TEST) == 0) {
|
||||
if (dfu_firmware_get_format (firmware) != DFU_FIRMWARE_FORMAT_DFU_1_0 &&
|
||||
dfu_firmware_get_format (firmware) != DFU_FIRMWARE_FORMAT_DFUSE) {
|
||||
g_set_error (error,
|
||||
DFU_ERROR,
|
||||
DFU_ERROR_INTERNAL,
|
||||
"version check failed, got %04x",
|
||||
dfu_firmware_get_format (firmware));
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
/* verify the checksum */
|
||||
crc = GUINT32_FROM_LE (ftr->crc);
|
||||
if ((flags & DFU_FIRMWARE_PARSE_FLAG_NO_CRC_TEST) == 0) {
|
||||
crc_new = dfu_firmware_generate_crc32 (data, len - 4);
|
||||
if (crc != crc_new) {
|
||||
g_set_error (error,
|
||||
DFU_ERROR,
|
||||
DFU_ERROR_INTERNAL,
|
||||
"CRC failed, expected %04x, got %04x",
|
||||
crc_new, GUINT32_FROM_LE (ftr->crc));
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
/* set from footer */
|
||||
dfu_firmware_set_vid (firmware, GUINT16_FROM_LE (ftr->vid));
|
||||
dfu_firmware_set_pid (firmware, GUINT16_FROM_LE (ftr->pid));
|
||||
dfu_firmware_set_release (firmware, GUINT16_FROM_LE (ftr->release));
|
||||
|
||||
/* check reported length */
|
||||
if (ftr->len > len) {
|
||||
g_set_error (error,
|
||||
DFU_ERROR,
|
||||
DFU_ERROR_INTERNAL,
|
||||
"reported firmware size %04x larger than file %04x",
|
||||
(guint) ftr->len, (guint) len);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* parse the optional metadata segment */
|
||||
if ((flags & DFU_FIRMWARE_PARSE_FLAG_NO_METADATA) == 0) {
|
||||
gsize offset = len - ftr->len;
|
||||
g_autoptr(GBytes) md = g_bytes_new (&data[offset], ftr->len);
|
||||
if (!dfu_firmware_from_metadata (firmware, md, flags, error))
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* set this automatically */
|
||||
cipher_str = dfu_firmware_get_metadata (firmware, DFU_METADATA_KEY_CIPHER_KIND);
|
||||
if (cipher_str != NULL) {
|
||||
if (g_strcmp0 (cipher_str, "XTEA") == 0)
|
||||
dfu_firmware_set_cipher_kind (firmware, DFU_CIPHER_KIND_XTEA);
|
||||
else
|
||||
g_warning ("Unknown CipherKind: %s", cipher_str);
|
||||
}
|
||||
|
||||
/* parse DfuSe prefix */
|
||||
contents = g_bytes_new_from_bytes (bytes, 0, len - ftr->len);
|
||||
if (dfu_firmware_get_format (firmware) == DFU_FIRMWARE_FORMAT_DFUSE)
|
||||
return dfu_firmware_from_dfuse (firmware, contents, flags, error);
|
||||
|
||||
/* just copy old-plain DFU file */
|
||||
return dfu_firmware_from_raw (firmware, contents, flags, error);
|
||||
}
|
||||
|
||||
static GBytes *
|
||||
dfu_firmware_add_footer (DfuFirmware *firmware, GBytes *contents, GError **error)
|
||||
{
|
||||
DfuFirmwareFooter *ftr;
|
||||
const guint8 *data_bin;
|
||||
const guint8 *data_md;
|
||||
gsize length_bin = 0;
|
||||
gsize length_md = 0;
|
||||
guint32 crc_new;
|
||||
guint8 *buf;
|
||||
g_autoptr(GBytes) metadata_table = NULL;
|
||||
|
||||
/* get any file metadata */
|
||||
metadata_table = dfu_firmware_to_metadata (firmware, error);
|
||||
if (metadata_table == NULL)
|
||||
return NULL;
|
||||
data_md = g_bytes_get_data (metadata_table, &length_md);
|
||||
|
||||
/* add the raw firmware data */
|
||||
data_bin = g_bytes_get_data (contents, &length_bin);
|
||||
buf = g_malloc0 (length_bin + length_md + 0x10);
|
||||
memcpy (buf + 0, data_bin, length_bin);
|
||||
|
||||
/* add the metadata table */
|
||||
memcpy (buf + length_bin, data_md, length_md);
|
||||
|
||||
/* set up LE footer */
|
||||
ftr = (DfuFirmwareFooter *) (buf + length_bin + length_md);
|
||||
ftr->release = GUINT16_TO_LE (dfu_firmware_get_release (firmware));
|
||||
ftr->pid = GUINT16_TO_LE (dfu_firmware_get_pid (firmware));
|
||||
ftr->vid = GUINT16_TO_LE (dfu_firmware_get_vid (firmware));
|
||||
ftr->ver = GUINT16_TO_LE (dfu_firmware_get_format (firmware));
|
||||
ftr->len = (guint8) (sizeof (DfuFirmwareFooter) + length_md);
|
||||
memcpy(ftr->sig, "UFD", 3);
|
||||
crc_new = dfu_firmware_generate_crc32 (buf, length_bin + length_md + 12);
|
||||
ftr->crc = GUINT32_TO_LE (crc_new);
|
||||
|
||||
/* return all data */
|
||||
return g_bytes_new_take (buf, length_bin + length_md + 0x10);
|
||||
}
|
||||
|
||||
/**
|
||||
* dfu_firmware_to_dfu: (skip)
|
||||
* @firmware: a #DfuFirmware
|
||||
* @error: a #GError, or %NULL
|
||||
*
|
||||
* Packs dfu firmware
|
||||
*
|
||||
* Returns: (transfer full): the packed data
|
||||
**/
|
||||
GBytes *
|
||||
dfu_firmware_to_dfu (DfuFirmware *firmware, GError **error)
|
||||
{
|
||||
/* plain DFU */
|
||||
if (dfu_firmware_get_format (firmware) == DFU_FIRMWARE_FORMAT_DFU_1_0) {
|
||||
GBytes *contents;
|
||||
DfuElement *element;
|
||||
DfuImage *image;
|
||||
image = dfu_firmware_get_image_default (firmware);
|
||||
g_assert (image != NULL);
|
||||
element = dfu_image_get_element (image, 0);
|
||||
if (element == NULL) {
|
||||
g_set_error (error,
|
||||
DFU_ERROR,
|
||||
DFU_ERROR_NOT_FOUND,
|
||||
"no firmware element data to write");
|
||||
return NULL;
|
||||
}
|
||||
contents = dfu_element_get_contents (element);
|
||||
return dfu_firmware_add_footer (firmware, contents, error);
|
||||
}
|
||||
|
||||
/* DfuSe */
|
||||
if (dfu_firmware_get_format (firmware) == DFU_FIRMWARE_FORMAT_DFUSE) {
|
||||
g_autoptr(GBytes) contents = NULL;
|
||||
contents = dfu_firmware_to_dfuse (firmware, error);
|
||||
if (contents == NULL)
|
||||
return NULL;
|
||||
return dfu_firmware_add_footer (firmware, contents, error);
|
||||
}
|
||||
|
||||
g_assert_not_reached ();
|
||||
return NULL;
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
|
||||
*
|
||||
* Copyright (C) 2015 Richard Hughes <richard@hughsie.com>
|
||||
* Copyright (C) 2015-2016 Richard Hughes <richard@hughsie.com>
|
||||
*
|
||||
* Licensed under the GNU Lesser General Public License Version 2.1
|
||||
*
|
||||
@ -19,19 +19,24 @@
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#ifndef __DFU_IMAGE_PRIVATE_H
|
||||
#define __DFU_IMAGE_PRIVATE_H
|
||||
#ifndef __DFU_FORMAT_DFU_H
|
||||
#define __DFU_FORMAT_DFU_H
|
||||
|
||||
#include "dfu-image.h"
|
||||
#include <glib-object.h>
|
||||
#include <gio/gio.h>
|
||||
|
||||
#include "dfu-firmware.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
DfuImage *dfu_image_from_dfuse (const guint8 *data,
|
||||
guint32 length,
|
||||
guint32 *consumed,
|
||||
GError **error);
|
||||
GBytes *dfu_image_to_dfuse (DfuImage *image);
|
||||
DfuFirmwareFormat dfu_firmware_detect_dfu (GBytes *bytes);
|
||||
GBytes *dfu_firmware_to_dfu (DfuFirmware *firmware,
|
||||
GError **error);
|
||||
gboolean dfu_firmware_from_dfu (DfuFirmware *firmware,
|
||||
GBytes *bytes,
|
||||
DfuFirmwareParseFlags flags,
|
||||
GError **error);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __DFU_IMAGE_PRIVATE_H */
|
||||
#endif /* __DFU_FORMAT_DFU_H */
|
402
libdfu/dfu-format-dfuse.c
Normal file
402
libdfu/dfu-format-dfuse.c
Normal file
@ -0,0 +1,402 @@
|
||||
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
|
||||
*
|
||||
* Copyright (C) 2015-2016 Richard Hughes <richard@hughsie.com>
|
||||
*
|
||||
* Licensed under the GNU Lesser General Public License Version 2.1
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "dfu-element.h"
|
||||
#include "dfu-format-dfuse.h"
|
||||
#include "dfu-image.h"
|
||||
#include "dfu-error.h"
|
||||
|
||||
/* DfuSe element header */
|
||||
typedef struct __attribute__((packed)) {
|
||||
guint32 address;
|
||||
guint32 size;
|
||||
} DfuSeElementPrefix;
|
||||
|
||||
/**
|
||||
* 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,
|
||||
DFU_ERROR,
|
||||
DFU_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,
|
||||
DFU_ERROR,
|
||||
DFU_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_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));
|
||||
}
|
||||
|
||||
/* DfuSe image header */
|
||||
typedef struct __attribute__((packed)) {
|
||||
guint8 sig[6];
|
||||
guint8 alt_setting;
|
||||
guint32 target_named;
|
||||
gchar target_name[255];
|
||||
guint32 target_size;
|
||||
guint32 elements;
|
||||
} DfuSeImagePrefix;
|
||||
|
||||
/**
|
||||
* dfu_image_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 image from DfuSe data.
|
||||
*
|
||||
* Returns: a #DfuImage, or %NULL for error
|
||||
**/
|
||||
static DfuImage *
|
||||
dfu_image_from_dfuse (const guint8 *data,
|
||||
guint32 length,
|
||||
guint32 *consumed,
|
||||
GError **error)
|
||||
{
|
||||
DfuSeImagePrefix *im;
|
||||
guint32 elements;
|
||||
guint32 offset = sizeof(DfuSeImagePrefix);
|
||||
guint j;
|
||||
g_autoptr(DfuImage) image = NULL;
|
||||
|
||||
g_assert_cmpint(sizeof(DfuSeImagePrefix), ==, 274);
|
||||
|
||||
/* check input buffer size */
|
||||
if (length < sizeof(DfuSeImagePrefix)) {
|
||||
g_set_error (error,
|
||||
DFU_ERROR,
|
||||
DFU_ERROR_INTERNAL,
|
||||
"invalid image data size %u",
|
||||
(guint32) length);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* verify image signature */
|
||||
im = (DfuSeImagePrefix *) data;
|
||||
if (memcmp (im->sig, "Target", 6) != 0) {
|
||||
g_set_error_literal (error,
|
||||
DFU_ERROR,
|
||||
DFU_ERROR_INVALID_FILE,
|
||||
"invalid DfuSe target signature");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* create new image */
|
||||
image = dfu_image_new ();
|
||||
dfu_image_set_alt_setting (image, im->alt_setting);
|
||||
if (GUINT32_FROM_LE (im->target_named) == 0x01)
|
||||
dfu_image_set_name (image, im->target_name);
|
||||
|
||||
/* parse elements */
|
||||
length -= offset;
|
||||
elements = GUINT32_FROM_LE (im->elements);
|
||||
for (j = 0; j < elements; j++) {
|
||||
guint32 consumed_local;
|
||||
g_autoptr(DfuElement) element = NULL;
|
||||
element = dfu_element_from_dfuse (data + offset, length,
|
||||
&consumed_local, error);
|
||||
if (element == NULL)
|
||||
return NULL;
|
||||
dfu_image_add_element (image, element);
|
||||
offset += consumed_local;
|
||||
length -= consumed_local;
|
||||
}
|
||||
|
||||
/* return size */
|
||||
if (consumed != NULL)
|
||||
*consumed = offset;
|
||||
|
||||
return g_object_ref (image);
|
||||
}
|
||||
|
||||
/**
|
||||
* dfu_image_to_dfuse: (skip)
|
||||
* @image: a #DfuImage
|
||||
*
|
||||
* Packs a DfuSe image
|
||||
*
|
||||
* Returns: (transfer full): the packed data
|
||||
**/
|
||||
static GBytes *
|
||||
dfu_image_to_dfuse (DfuImage *image)
|
||||
{
|
||||
DfuElement *element;
|
||||
DfuSeImagePrefix *im;
|
||||
GBytes *bytes;
|
||||
GPtrArray *elements;
|
||||
guint32 length_total = 0;
|
||||
guint32 offset = sizeof (DfuSeImagePrefix);
|
||||
guint8 *buf;
|
||||
guint i;
|
||||
g_autoptr(GPtrArray) element_array = NULL;
|
||||
|
||||
/* get total size */
|
||||
element_array = g_ptr_array_new_with_free_func ((GDestroyNotify) g_bytes_unref);
|
||||
elements = dfu_image_get_elements (image);
|
||||
for (i = 0; i < elements->len; i++) {
|
||||
element = g_ptr_array_index (elements, i);
|
||||
bytes = dfu_element_to_dfuse (element);
|
||||
g_ptr_array_add (element_array, bytes);
|
||||
length_total += (guint32) g_bytes_get_size (bytes);
|
||||
}
|
||||
|
||||
/* add prefix */
|
||||
buf = g_malloc0 (length_total + sizeof (DfuSeImagePrefix));
|
||||
im = (DfuSeImagePrefix *) buf;
|
||||
memcpy (im->sig, "Target", 6);
|
||||
im->alt_setting = dfu_image_get_alt_setting (image);
|
||||
if (dfu_image_get_name (image) != NULL) {
|
||||
im->target_named = GUINT32_TO_LE (0x01);
|
||||
memcpy (im->target_name, dfu_image_get_name (image), 255);
|
||||
}
|
||||
im->target_size = GUINT32_TO_LE (length_total);
|
||||
im->elements = GUINT32_TO_LE (elements->len);
|
||||
|
||||
/* copy data */
|
||||
for (i = 0; i < element_array->len; i++) {
|
||||
const guint8 *data;
|
||||
gsize length;
|
||||
bytes = g_ptr_array_index (element_array, i);
|
||||
data = g_bytes_get_data (bytes, &length);
|
||||
memcpy (buf + offset, data, length);
|
||||
offset += (guint32) length;
|
||||
}
|
||||
return g_bytes_new_take (buf, length_total + sizeof (DfuSeImagePrefix));
|
||||
}
|
||||
|
||||
/* DfuSe header */
|
||||
typedef struct __attribute__((packed)) {
|
||||
guint8 sig[5];
|
||||
guint8 ver;
|
||||
guint32 image_size;
|
||||
guint8 targets;
|
||||
} DfuSePrefix;
|
||||
|
||||
/**
|
||||
* dfu_firmware_to_dfuse: (skip)
|
||||
* @firmware: a #DfuFirmware
|
||||
* @error: a #GError, or %NULL
|
||||
*
|
||||
* Packs a DfuSe firmware
|
||||
*
|
||||
* Returns: (transfer full): the packed data
|
||||
**/
|
||||
GBytes *
|
||||
dfu_firmware_to_dfuse (DfuFirmware *firmware, GError **error)
|
||||
{
|
||||
DfuSePrefix *prefix;
|
||||
GPtrArray *images;
|
||||
guint i;
|
||||
guint32 image_size_total = 0;
|
||||
guint32 offset = sizeof (DfuSePrefix);
|
||||
guint8 *buf;
|
||||
g_autoptr(GPtrArray) dfuse_images = NULL;
|
||||
|
||||
/* get all the image data */
|
||||
dfuse_images = g_ptr_array_new_with_free_func ((GDestroyNotify) g_bytes_unref);
|
||||
images = dfu_firmware_get_images (firmware);
|
||||
for (i = 0; i < images->len; i++) {
|
||||
DfuImage *im = g_ptr_array_index (images, i);
|
||||
GBytes *contents;
|
||||
contents = dfu_image_to_dfuse (im);
|
||||
image_size_total += (guint32) g_bytes_get_size (contents);
|
||||
g_ptr_array_add (dfuse_images, contents);
|
||||
}
|
||||
g_debug ("image_size_total: %" G_GUINT32_FORMAT, image_size_total);
|
||||
|
||||
buf = g_malloc0 (sizeof (DfuSePrefix) + image_size_total);
|
||||
|
||||
/* DfuSe header */
|
||||
prefix = (DfuSePrefix *) buf;
|
||||
memcpy (prefix->sig, "DfuSe", 5);
|
||||
prefix->ver = 0x01;
|
||||
prefix->image_size = GUINT32_TO_LE (offset + image_size_total);
|
||||
if (images->len > G_MAXUINT8) {
|
||||
g_set_error (error,
|
||||
DFU_ERROR,
|
||||
DFU_ERROR_INTERNAL,
|
||||
"too many (%u) images to write DfuSe file",
|
||||
images->len);
|
||||
return FALSE;
|
||||
}
|
||||
prefix->targets = (guint8) images->len;
|
||||
|
||||
/* copy images */
|
||||
for (i = 0; i < dfuse_images->len; i++) {
|
||||
GBytes *contents = g_ptr_array_index (dfuse_images, i);
|
||||
gsize length;
|
||||
const guint8 *data;
|
||||
data = g_bytes_get_data (contents, &length);
|
||||
memcpy (buf + offset, data, length);
|
||||
offset += (guint32) length;
|
||||
}
|
||||
|
||||
/* return blob */
|
||||
return g_bytes_new_take (buf, sizeof (DfuSePrefix) + image_size_total);
|
||||
}
|
||||
|
||||
/**
|
||||
* dfu_firmware_from_dfuse: (skip)
|
||||
* @firmware: a #DfuFirmware
|
||||
* @bytes: data to parse
|
||||
* @flags: some #DfuFirmwareParseFlags
|
||||
* @error: a #GError, or %NULL
|
||||
*
|
||||
* Unpacks into a firmware object from DfuSe data.
|
||||
*
|
||||
* Returns: %TRUE for success
|
||||
**/
|
||||
gboolean
|
||||
dfu_firmware_from_dfuse (DfuFirmware *firmware,
|
||||
GBytes *bytes,
|
||||
DfuFirmwareParseFlags flags,
|
||||
GError **error)
|
||||
{
|
||||
DfuSePrefix *prefix;
|
||||
gsize len;
|
||||
guint32 offset = sizeof(DfuSePrefix);
|
||||
guint8 *data;
|
||||
guint i;
|
||||
|
||||
/* check the prefix (BE) */
|
||||
data = (guint8 *) g_bytes_get_data (bytes, &len);
|
||||
prefix = (DfuSePrefix *) data;
|
||||
if (memcmp (prefix->sig, "DfuSe", 5) != 0) {
|
||||
g_set_error_literal (error,
|
||||
DFU_ERROR,
|
||||
DFU_ERROR_INTERNAL,
|
||||
"invalid DfuSe prefix");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* check the version */
|
||||
if (prefix->ver != 0x01) {
|
||||
g_set_error (error,
|
||||
DFU_ERROR,
|
||||
DFU_ERROR_INTERNAL,
|
||||
"invalid DfuSe version, got %02x",
|
||||
prefix->ver);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* check image size */
|
||||
if (GUINT32_FROM_LE (prefix->image_size) != len) {
|
||||
g_set_error (error,
|
||||
DFU_ERROR,
|
||||
DFU_ERROR_INTERNAL,
|
||||
"invalid DfuSe image size, "
|
||||
"got %" G_GUINT32_FORMAT ", "
|
||||
"expected %" G_GSIZE_FORMAT,
|
||||
GUINT32_FROM_LE (prefix->image_size),
|
||||
len);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* parse the image targets */
|
||||
len -= sizeof(DfuSePrefix);
|
||||
for (i = 0; i < prefix->targets; i++) {
|
||||
guint consumed;
|
||||
g_autoptr(DfuImage) image = NULL;
|
||||
image = dfu_image_from_dfuse (data + offset, (guint32) len,
|
||||
&consumed, error);
|
||||
if (image == NULL)
|
||||
return FALSE;
|
||||
dfu_firmware_add_image (firmware, image);
|
||||
offset += consumed;
|
||||
len -= consumed;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
42
libdfu/dfu-format-dfuse.h
Normal file
42
libdfu/dfu-format-dfuse.h
Normal file
@ -0,0 +1,42 @@
|
||||
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
|
||||
*
|
||||
* Copyright (C) 2015-2016 Richard Hughes <richard@hughsie.com>
|
||||
*
|
||||
* Licensed under the GNU Lesser General Public License Version 2.1
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#ifndef __DFU_FORMAT_DFUSE_H
|
||||
#define __DFU_FORMAT_DFUSE_H
|
||||
|
||||
#include <glib-object.h>
|
||||
#include <gio/gio.h>
|
||||
|
||||
#include "dfu-firmware.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
DfuFirmwareFormat dfu_firmware_detect_dfuse (GBytes *bytes);
|
||||
GBytes *dfu_firmware_to_dfuse (DfuFirmware *firmware,
|
||||
GError **error);
|
||||
gboolean dfu_firmware_from_dfuse (DfuFirmware *firmware,
|
||||
GBytes *bytes,
|
||||
DfuFirmwareParseFlags flags,
|
||||
GError **error);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __DFU_FORMAT_DFUSE_H */
|
311
libdfu/dfu-format-ihex.c
Normal file
311
libdfu/dfu-format-ihex.c
Normal file
@ -0,0 +1,311 @@
|
||||
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
|
||||
*
|
||||
* Copyright (C) 2015-2016 Richard Hughes <richard@hughsie.com>
|
||||
*
|
||||
* Licensed under the GNU Lesser General Public License Version 2.1
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "dfu-element.h"
|
||||
#include "dfu-error.h"
|
||||
#include "dfu-format-ihex.h"
|
||||
#include "dfu-image.h"
|
||||
|
||||
/**
|
||||
* dfu_firmware_detect_ihex: (skip)
|
||||
* @bytes: data to parse
|
||||
*
|
||||
* Attempts to sniff the data and work out the firmware format
|
||||
*
|
||||
* Returns: a #DfuFirmwareFormat, e.g. %DFU_FIRMWARE_FORMAT_RAW
|
||||
**/
|
||||
DfuFirmwareFormat
|
||||
dfu_firmware_detect_ihex (GBytes *bytes)
|
||||
{
|
||||
guint8 *data;
|
||||
gsize len;
|
||||
data = (guint8 *) g_bytes_get_data (bytes, &len);
|
||||
if (len < 12)
|
||||
return DFU_FIRMWARE_FORMAT_UNKNOWN;
|
||||
if (data[0] != ':')
|
||||
return DFU_FIRMWARE_FORMAT_UNKNOWN;
|
||||
return DFU_FIRMWARE_FORMAT_INTEL_HEX;
|
||||
}
|
||||
|
||||
static guint8
|
||||
dfu_firmware_ihex_parse_uint8 (const gchar *data, guint pos)
|
||||
{
|
||||
gchar buffer[3];
|
||||
memcpy (buffer, data + pos, 2);
|
||||
buffer[2] = '\0';
|
||||
return (guint8) g_ascii_strtoull (buffer, NULL, 16);
|
||||
}
|
||||
|
||||
static guint16
|
||||
dfu_firmware_ihex_parse_uint16 (const gchar *data, guint pos)
|
||||
{
|
||||
gchar buffer[5];
|
||||
memcpy (buffer, data + pos, 4);
|
||||
buffer[4] = '\0';
|
||||
return (guint16) g_ascii_strtoull (buffer, NULL, 16);
|
||||
}
|
||||
|
||||
#define DFU_INHX32_RECORD_TYPE_DATA 0
|
||||
#define DFU_INHX32_RECORD_TYPE_EOF 1
|
||||
#define DFU_INHX32_RECORD_TYPE_EXTENDED 4
|
||||
|
||||
/**
|
||||
* dfu_firmware_from_ihex: (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_ihex (DfuFirmware *firmware,
|
||||
GBytes *bytes,
|
||||
DfuFirmwareParseFlags flags,
|
||||
GError **error)
|
||||
{
|
||||
const gchar *in_buffer;
|
||||
gsize len_in;
|
||||
guint16 addr_high = 0;
|
||||
guint16 addr_low = 0;
|
||||
guint32 addr32 = 0;
|
||||
guint32 addr32_last = 0;
|
||||
guint8 checksum;
|
||||
guint8 data_tmp;
|
||||
guint8 len_tmp;
|
||||
guint8 type;
|
||||
guint end;
|
||||
guint i;
|
||||
guint j;
|
||||
guint offset = 0;
|
||||
g_autoptr(DfuElement) element = NULL;
|
||||
g_autoptr(DfuImage) image = NULL;
|
||||
g_autoptr(GBytes) contents = NULL;
|
||||
g_autoptr(GString) string = NULL;
|
||||
|
||||
g_return_val_if_fail (bytes != NULL, FALSE);
|
||||
|
||||
/* create element */
|
||||
image = dfu_image_new ();
|
||||
dfu_image_set_name (image, "ihex");
|
||||
element = dfu_element_new ();
|
||||
|
||||
/* parse records */
|
||||
in_buffer = g_bytes_get_data (bytes, &len_in);
|
||||
string = g_string_new ("");
|
||||
while (offset < len_in) {
|
||||
|
||||
/* check starting token */
|
||||
if (in_buffer[offset] != ':') {
|
||||
g_set_error (error,
|
||||
DFU_ERROR,
|
||||
DFU_ERROR_INVALID_FILE,
|
||||
"invalid starting token, got %c at %x",
|
||||
in_buffer[offset], offset);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* check there's enough data for the smallest possible record */
|
||||
if (offset + 12 > (guint) len_in) {
|
||||
g_set_error (error,
|
||||
DFU_ERROR,
|
||||
DFU_ERROR_INVALID_FILE,
|
||||
"record incomplete at %u, length %u",
|
||||
offset, (guint) len_in);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* length, 16-bit address, type */
|
||||
len_tmp = dfu_firmware_ihex_parse_uint8 (in_buffer, offset+1);
|
||||
addr_low = dfu_firmware_ihex_parse_uint16 (in_buffer, offset+3);
|
||||
type = dfu_firmware_ihex_parse_uint8 (in_buffer, offset+7);
|
||||
|
||||
/* position of checksum */
|
||||
end = offset + 9 + len_tmp * 2;
|
||||
if (end > (guint) len_in) {
|
||||
g_set_error (error,
|
||||
DFU_ERROR,
|
||||
DFU_ERROR_INVALID_FILE,
|
||||
"checksum > file length: %u",
|
||||
end);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* verify checksum */
|
||||
if ((flags & DFU_FIRMWARE_PARSE_FLAG_NO_CRC_TEST) == 0) {
|
||||
checksum = 0;
|
||||
for (i = offset + 1; i < end + 2; i += 2) {
|
||||
data_tmp = dfu_firmware_ihex_parse_uint8 (in_buffer, i);
|
||||
checksum += data_tmp;
|
||||
}
|
||||
if (checksum != 0) {
|
||||
g_set_error_literal (error,
|
||||
DFU_ERROR,
|
||||
DFU_ERROR_INVALID_FILE,
|
||||
"invalid record checksum");
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
/* process different record types */
|
||||
switch (type) {
|
||||
case DFU_INHX32_RECORD_TYPE_DATA:
|
||||
/* if not contiguous with previous record */
|
||||
if ((addr_high + addr_low) != addr32) {
|
||||
if (addr32 == 0x0) {
|
||||
g_debug ("base address %04x", addr_low);
|
||||
dfu_element_set_address (element, addr_low);
|
||||
}
|
||||
// addr32 = addr_high + addr_low;
|
||||
addr32 = ((guint32) addr_high << 16) + addr_low;
|
||||
}
|
||||
|
||||
/* parse bytes from line */
|
||||
for (i = offset + 9; i < end; i += 2) {
|
||||
/* any holes in the hex record */
|
||||
guint32 len_hole = addr32 - addr32_last;
|
||||
if (addr32_last > 0x0 && len_hole > 1) {
|
||||
for (j = 1; j < len_hole; j++) {
|
||||
g_debug ("filling address 0x%04x",
|
||||
addr32_last + j);
|
||||
/* although 0xff might be clearer,
|
||||
* we can't write 0xffff to pic14 */
|
||||
g_string_append_c (string, 0x00);
|
||||
}
|
||||
}
|
||||
/* write into buf */
|
||||
data_tmp = dfu_firmware_ihex_parse_uint8 (in_buffer, i);
|
||||
g_string_append_c (string, (gchar) data_tmp);
|
||||
g_debug ("writing address 0x%04x", addr32);
|
||||
addr32_last = addr32++;
|
||||
}
|
||||
break;
|
||||
case DFU_INHX32_RECORD_TYPE_EOF:
|
||||
break;
|
||||
case DFU_INHX32_RECORD_TYPE_EXTENDED:
|
||||
addr_high = dfu_firmware_ihex_parse_uint16 (in_buffer, offset+9);
|
||||
g_error ("set base address %x", addr_high);
|
||||
addr32 = ((guint32) addr_high << 16) + addr_low;
|
||||
break;
|
||||
default:
|
||||
g_set_error (error,
|
||||
DFU_ERROR,
|
||||
DFU_ERROR_INVALID_FILE,
|
||||
"invalid ihex record type %i",
|
||||
type);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* ignore any line return */
|
||||
offset = end + 2;
|
||||
for (; offset < len_in; offset++) {
|
||||
if (in_buffer[offset] != '\n' &&
|
||||
in_buffer[offset] != '\r')
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* add single image */
|
||||
contents = g_bytes_new (string->str, string->len);
|
||||
dfu_element_set_contents (element, contents);
|
||||
dfu_image_add_element (image, element);
|
||||
dfu_firmware_add_image (firmware, image);
|
||||
dfu_firmware_set_format (firmware, DFU_FIRMWARE_FORMAT_INTEL_HEX);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
dfu_firmware_to_ihex_element (DfuElement *element, GString *str, GError **error)
|
||||
{
|
||||
GBytes *contents;
|
||||
const guint8 *data;
|
||||
const guint chunk_size = 16;
|
||||
gsize len;
|
||||
|
||||
/* get number of chunks */
|
||||
contents = dfu_element_get_contents (element);
|
||||
data = g_bytes_get_data (contents, &len);
|
||||
for (gsize i = 0; i < len; i += chunk_size) {
|
||||
guint8 checksum = 0;
|
||||
|
||||
/* length, 16-bit address, type */
|
||||
gsize chunk_len = MIN (len - i, 16);
|
||||
g_string_append_printf (str, ":%02X%04X%02X",
|
||||
(guint) chunk_len,
|
||||
(guint) (dfu_element_get_address (element) + i),
|
||||
(guint) DFU_INHX32_RECORD_TYPE_DATA);
|
||||
for (gsize j = 0; j < chunk_len; j++)
|
||||
g_string_append_printf (str, "%02X", data[i+j]);
|
||||
|
||||
/* add checksum */
|
||||
for (gsize j = 0; j < (chunk_len * 2) + 8; j++)
|
||||
checksum += (guint8) str->str[str->len - (j + 1)];
|
||||
g_string_append_printf (str, "%02X\n", checksum);
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* dfu_firmware_to_ihex: (skip)
|
||||
* @firmware: a #DfuFirmware
|
||||
* @error: a #GError, or %NULL
|
||||
*
|
||||
* Packs a IHEX firmware
|
||||
*
|
||||
* Returns: (transfer full): the packed data
|
||||
**/
|
||||
GBytes *
|
||||
dfu_firmware_to_ihex (DfuFirmware *firmware, GError **error)
|
||||
{
|
||||
DfuElement *element;
|
||||
DfuImage *image;
|
||||
GPtrArray *images;
|
||||
GPtrArray *elements;
|
||||
guint i;
|
||||
guint j;
|
||||
g_autoptr(GString) str = NULL;
|
||||
|
||||
/* write all the element data */
|
||||
str = g_string_new ("");
|
||||
images = dfu_firmware_get_images (firmware);
|
||||
for (i = 0; i < images->len; i++) {
|
||||
image = g_ptr_array_index (images, i);
|
||||
elements = dfu_image_get_elements (image);
|
||||
for (j = 0; j < elements->len; j++) {
|
||||
element = g_ptr_array_index (elements, j);
|
||||
if (!dfu_firmware_to_ihex_element (element,
|
||||
str,
|
||||
error))
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* add EOF */
|
||||
g_string_append_printf (str, ":000000%02XFF\n",
|
||||
(guint) DFU_INHX32_RECORD_TYPE_EOF);
|
||||
return g_bytes_new (str->str, str->len);
|
||||
}
|
42
libdfu/dfu-format-ihex.h
Normal file
42
libdfu/dfu-format-ihex.h
Normal file
@ -0,0 +1,42 @@
|
||||
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
|
||||
*
|
||||
* Copyright (C) 2015-2016 Richard Hughes <richard@hughsie.com>
|
||||
*
|
||||
* Licensed under the GNU Lesser General Public License Version 2.1
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#ifndef __DFU_FORMAT_IHEX_H
|
||||
#define __DFU_FORMAT_IHEX_H
|
||||
|
||||
#include <glib-object.h>
|
||||
#include <gio/gio.h>
|
||||
|
||||
#include "dfu-firmware.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
DfuFirmwareFormat dfu_firmware_detect_ihex (GBytes *bytes);
|
||||
GBytes *dfu_firmware_to_ihex (DfuFirmware *firmware,
|
||||
GError **error);
|
||||
gboolean dfu_firmware_from_ihex (DfuFirmware *firmware,
|
||||
GBytes *bytes,
|
||||
DfuFirmwareParseFlags flags,
|
||||
GError **error);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __DFU_FORMAT_IHEX_H */
|
216
libdfu/dfu-format-metadata.c
Normal file
216
libdfu/dfu-format-metadata.c
Normal file
@ -0,0 +1,216 @@
|
||||
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
|
||||
*
|
||||
* Copyright (C) 2015-2016 Richard Hughes <richard@hughsie.com>
|
||||
*
|
||||
* Licensed under the GNU Lesser General Public License Version 2.1
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "dfu-element.h"
|
||||
#include "dfu-format-metadata.h"
|
||||
#include "dfu-image.h"
|
||||
#include "dfu-error.h"
|
||||
|
||||
/**
|
||||
* dfu_firmware_from_metadata: (skip)
|
||||
* @firmware: a #DfuFirmware
|
||||
* @bytes: data to parse
|
||||
* @flags: some #DfuFirmwareParseFlags
|
||||
* @error: a #GError, or %NULL
|
||||
*
|
||||
* Unpacks into a firmware object from metadata data.
|
||||
*
|
||||
* The representation in memory is as follows:
|
||||
*
|
||||
* uint16 signature='MD'
|
||||
* uint8 number_of_keys
|
||||
* uint8 number_of_keys
|
||||
* uint8 key(n)_length
|
||||
* ... key(n) (no NUL)
|
||||
* uint8 value(n)_length
|
||||
* ... value(n) (no NUL)
|
||||
* <existing DFU footer>
|
||||
|
||||
* Returns: %TRUE for success
|
||||
**/
|
||||
gboolean
|
||||
dfu_firmware_from_metadata (DfuFirmware *firmware,
|
||||
GBytes *bytes,
|
||||
DfuFirmwareParseFlags flags,
|
||||
GError **error)
|
||||
{
|
||||
gsize data_length;
|
||||
guint8 *data;
|
||||
guint i;
|
||||
guint idx = 2;
|
||||
guint kvlen;
|
||||
guint number_keys;
|
||||
|
||||
/* not big enough */
|
||||
data = g_bytes_get_data (bytes, &data_length);
|
||||
if (data_length <= 0x10)
|
||||
return TRUE;
|
||||
|
||||
/* signature invalid */
|
||||
if (memcmp (data, "MD", 2) != 0)
|
||||
return TRUE;
|
||||
|
||||
/* parse key=value store */
|
||||
number_keys = data[idx++];
|
||||
for (i = 0; i < number_keys; i++) {
|
||||
g_autofree gchar *key = NULL;
|
||||
g_autofree gchar *value = NULL;
|
||||
|
||||
/* parse key */
|
||||
kvlen = data[idx++];
|
||||
if (kvlen > 233) {
|
||||
g_set_error (error,
|
||||
DFU_ERROR,
|
||||
DFU_ERROR_INTERNAL,
|
||||
"metadata table corrupt, key=%u",
|
||||
kvlen);
|
||||
return FALSE;
|
||||
}
|
||||
if (idx + kvlen + 0x10 > data_length) {
|
||||
g_set_error_literal (error,
|
||||
DFU_ERROR,
|
||||
DFU_ERROR_INTERNAL,
|
||||
"metadata table corrupt");
|
||||
return FALSE;
|
||||
}
|
||||
key = g_strndup ((const gchar *) data + idx, kvlen);
|
||||
idx += kvlen;
|
||||
|
||||
/* parse value */
|
||||
kvlen = data[idx++];
|
||||
if (kvlen > 233) {
|
||||
g_set_error (error,
|
||||
DFU_ERROR,
|
||||
DFU_ERROR_INTERNAL,
|
||||
"metadata table corrupt, value=%u",
|
||||
kvlen);
|
||||
return FALSE;
|
||||
}
|
||||
if (idx + kvlen + 0x10 > data_length) {
|
||||
g_set_error_literal (error,
|
||||
DFU_ERROR,
|
||||
DFU_ERROR_INTERNAL,
|
||||
"metadata table corrupt");
|
||||
return FALSE;
|
||||
}
|
||||
value = g_strndup ((const gchar *) data + idx, kvlen);
|
||||
idx += kvlen;
|
||||
dfu_firmware_set_metadata (firmware, key, value);
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* dfu_firmware_to_metadata: (skip)
|
||||
* @firmware: a #DfuFirmware
|
||||
* @error: a #GError, or %NULL
|
||||
*
|
||||
* Packs metadata firmware
|
||||
*
|
||||
* Returns: (transfer full): the packed data
|
||||
**/
|
||||
GBytes *
|
||||
dfu_firmware_to_metadata (DfuFirmware *firmware, GError **error)
|
||||
{
|
||||
GList *l;
|
||||
GHashTable *metadata;
|
||||
guint8 mdbuf[239];
|
||||
guint idx = 0;
|
||||
guint number_keys;
|
||||
g_autoptr(GList) keys = NULL;
|
||||
|
||||
/* no metadata */
|
||||
metadata = dfu_firmware_get_metadata_table (firmware);
|
||||
if (g_hash_table_size (metadata) == 0)
|
||||
return g_bytes_new (NULL, 0);
|
||||
|
||||
/* check the number of keys */
|
||||
keys = g_hash_table_get_keys (metadata);
|
||||
number_keys = g_list_length (keys);
|
||||
if (number_keys > 59) {
|
||||
g_set_error (error,
|
||||
DFU_ERROR,
|
||||
DFU_ERROR_NOT_SUPPORTED,
|
||||
"too many metadata keys (%u)",
|
||||
number_keys);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* write the signature */
|
||||
mdbuf[idx++] = 'M';
|
||||
mdbuf[idx++] = 'D';
|
||||
mdbuf[idx++] = (guint8) number_keys;
|
||||
for (l = keys; l != NULL; l = l->next) {
|
||||
const gchar *key;
|
||||
const gchar *value;
|
||||
guint key_len;
|
||||
guint value_len;
|
||||
|
||||
/* check key and value length */
|
||||
key = l->data;
|
||||
key_len = (guint) strlen (key);
|
||||
if (key_len > 233) {
|
||||
g_set_error (error,
|
||||
DFU_ERROR,
|
||||
DFU_ERROR_NOT_SUPPORTED,
|
||||
"metdata key too long: %s",
|
||||
key);
|
||||
return NULL;
|
||||
}
|
||||
value = g_hash_table_lookup (metadata, key);
|
||||
value_len = (guint) strlen (value);
|
||||
if (value_len > 233) {
|
||||
g_set_error (error,
|
||||
DFU_ERROR,
|
||||
DFU_ERROR_NOT_SUPPORTED,
|
||||
"value too long: %s",
|
||||
value);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* do we still have space? */
|
||||
if (idx + key_len + value_len + 2 > sizeof(mdbuf)) {
|
||||
g_set_error (error,
|
||||
DFU_ERROR,
|
||||
DFU_ERROR_NOT_SUPPORTED,
|
||||
"not enough space in metadata table, "
|
||||
"already used %u bytes", idx);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* write the key */
|
||||
mdbuf[idx++] = (guint8) key_len;
|
||||
memcpy(mdbuf + idx, key, key_len);
|
||||
idx += key_len;
|
||||
|
||||
/* write the value */
|
||||
mdbuf[idx++] = (guint8) value_len;
|
||||
memcpy(mdbuf + idx, value, value_len);
|
||||
idx += value_len;
|
||||
}
|
||||
g_debug ("metadata table was %u/%" G_GSIZE_FORMAT " bytes",
|
||||
idx, sizeof(mdbuf));
|
||||
return g_bytes_new (mdbuf, idx);
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
|
||||
*
|
||||
* Copyright (C) 2015 Richard Hughes <richard@hughsie.com>
|
||||
* Copyright (C) 2015-2016 Richard Hughes <richard@hughsie.com>
|
||||
*
|
||||
* Licensed under the GNU Lesser General Public License Version 2.1
|
||||
*
|
||||
@ -19,19 +19,23 @@
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#ifndef __DFU_ELEMENT_PRIVATE_H
|
||||
#define __DFU_ELEMENT_PRIVATE_H
|
||||
#ifndef __DFU_FORMAT_METADATA_H
|
||||
#define __DFU_FORMAT_METADATA_H
|
||||
|
||||
#include "dfu-element.h"
|
||||
#include <glib-object.h>
|
||||
#include <gio/gio.h>
|
||||
|
||||
#include "dfu-firmware.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
DfuElement *dfu_element_from_dfuse (const guint8 *data,
|
||||
guint32 length,
|
||||
guint32 *consumed,
|
||||
GError **error);
|
||||
GBytes *dfu_element_to_dfuse (DfuElement *element);
|
||||
GBytes *dfu_firmware_to_metadata (DfuFirmware *firmware,
|
||||
GError **error);
|
||||
gboolean dfu_firmware_from_metadata (DfuFirmware *firmware,
|
||||
GBytes *bytes,
|
||||
DfuFirmwareParseFlags flags,
|
||||
GError **error);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __DFU_ELEMENT_PRIVATE_H */
|
||||
#endif /* __DFU_FORMAT_METADATA_H */
|
106
libdfu/dfu-format-raw.c
Normal file
106
libdfu/dfu-format-raw.c
Normal file
@ -0,0 +1,106 @@
|
||||
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
|
||||
*
|
||||
* Copyright (C) 2015-2016 Richard Hughes <richard@hughsie.com>
|
||||
*
|
||||
* Licensed under the GNU Lesser General Public License Version 2.1
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "dfu-element.h"
|
||||
#include "dfu-format-raw.h"
|
||||
#include "dfu-image.h"
|
||||
#include "dfu-error.h"
|
||||
|
||||
/**
|
||||
* dfu_firmware_detect_raw: (skip)
|
||||
* @bytes: data to parse
|
||||
*
|
||||
* Attempts to sniff the data and work out the firmware format
|
||||
*
|
||||
* Returns: a #DfuFirmwareFormat, e.g. %DFU_FIRMWARE_FORMAT_RAW
|
||||
**/
|
||||
DfuFirmwareFormat
|
||||
dfu_firmware_detect_raw (GBytes *bytes)
|
||||
{
|
||||
return DFU_FIRMWARE_FORMAT_RAW;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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_firmware_to_raw: (skip)
|
||||
* @firmware: a #DfuFirmware
|
||||
* @error: a #GError, or %NULL
|
||||
*
|
||||
* Packs raw firmware
|
||||
*
|
||||
* Returns: (transfer full): the packed data
|
||||
**/
|
||||
GBytes *
|
||||
dfu_firmware_to_raw (DfuFirmware *firmware, GError **error)
|
||||
{
|
||||
DfuElement *element;
|
||||
DfuImage *image;
|
||||
GBytes *contents;
|
||||
|
||||
image = dfu_firmware_get_image_default (firmware);
|
||||
if (image == NULL) {
|
||||
g_set_error_literal (error,
|
||||
DFU_ERROR,
|
||||
DFU_ERROR_NOT_FOUND,
|
||||
"no firmware image data to write");
|
||||
return NULL;
|
||||
}
|
||||
element = dfu_image_get_element (image, 0);
|
||||
if (element == NULL) {
|
||||
g_set_error_literal (error,
|
||||
DFU_ERROR,
|
||||
DFU_ERROR_NOT_FOUND,
|
||||
"no firmware element data to write");
|
||||
return NULL;
|
||||
}
|
||||
contents = dfu_element_get_contents (element);
|
||||
return g_bytes_ref (contents);
|
||||
}
|
42
libdfu/dfu-format-raw.h
Normal file
42
libdfu/dfu-format-raw.h
Normal file
@ -0,0 +1,42 @@
|
||||
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
|
||||
*
|
||||
* Copyright (C) 2015-2016 Richard Hughes <richard@hughsie.com>
|
||||
*
|
||||
* Licensed under the GNU Lesser General Public License Version 2.1
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#ifndef __DFU_FORMAT_RAW_H
|
||||
#define __DFU_FORMAT_RAW_H
|
||||
|
||||
#include <glib-object.h>
|
||||
#include <gio/gio.h>
|
||||
|
||||
#include "dfu-firmware.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
DfuFirmwareFormat dfu_firmware_detect_raw (GBytes *bytes);
|
||||
GBytes *dfu_firmware_to_raw (DfuFirmware *firmware,
|
||||
GError **error);
|
||||
gboolean dfu_firmware_from_raw (DfuFirmware *firmware,
|
||||
GBytes *bytes,
|
||||
DfuFirmwareParseFlags flags,
|
||||
GError **error);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __DFU_FORMAT_RAW_H */
|
@ -35,9 +35,9 @@
|
||||
#include <stdio.h>
|
||||
|
||||
#include "dfu-common.h"
|
||||
#include "dfu-element-private.h"
|
||||
#include "dfu-element.h"
|
||||
#include "dfu-error.h"
|
||||
#include "dfu-image-private.h"
|
||||
#include "dfu-image.h"
|
||||
|
||||
static void dfu_image_finalize (GObject *object);
|
||||
|
||||
@ -272,6 +272,10 @@ dfu_image_set_name (DfuImage *image, const gchar *name)
|
||||
sz = MIN ((guint16) strlen (name), 0xff - 1);
|
||||
memcpy (priv->name, name, sz);
|
||||
}
|
||||
|
||||
/* copy junk data in self tests for 1:1 copies */
|
||||
if (name != NULL && G_UNLIKELY (g_getenv ("DFU_SELF_TEST") != NULL))
|
||||
memcpy (priv->name, name, 0xff);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -312,142 +316,3 @@ dfu_image_to_string (DfuImage *image)
|
||||
g_string_truncate (str, str->len - 1);
|
||||
return g_string_free (str, FALSE);
|
||||
}
|
||||
|
||||
/* DfuSe image header */
|
||||
typedef struct __attribute__((packed)) {
|
||||
guint8 sig[6];
|
||||
guint8 alt_setting;
|
||||
guint32 target_named;
|
||||
gchar target_name[255];
|
||||
guint32 target_size;
|
||||
guint32 elements;
|
||||
} DfuSeImagePrefix;
|
||||
|
||||
/**
|
||||
* dfu_image_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 image from DfuSe data.
|
||||
*
|
||||
* Returns: a #DfuImage, or %NULL for error
|
||||
**/
|
||||
DfuImage *
|
||||
dfu_image_from_dfuse (const guint8 *data,
|
||||
guint32 length,
|
||||
guint32 *consumed,
|
||||
GError **error)
|
||||
{
|
||||
DfuImagePrivate *priv;
|
||||
DfuSeImagePrefix *im;
|
||||
guint32 elements;
|
||||
guint32 offset = sizeof(DfuSeImagePrefix);
|
||||
guint j;
|
||||
g_autoptr(DfuImage) image = NULL;
|
||||
|
||||
g_assert_cmpint(sizeof(DfuSeImagePrefix), ==, 274);
|
||||
|
||||
/* check input buffer size */
|
||||
if (length < sizeof(DfuSeImagePrefix)) {
|
||||
g_set_error (error,
|
||||
DFU_ERROR,
|
||||
DFU_ERROR_INTERNAL,
|
||||
"invalid image data size %u",
|
||||
(guint32) length);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* verify image signature */
|
||||
im = (DfuSeImagePrefix *) data;
|
||||
if (memcmp (im->sig, "Target", 6) != 0) {
|
||||
g_set_error_literal (error,
|
||||
DFU_ERROR,
|
||||
DFU_ERROR_INVALID_FILE,
|
||||
"invalid DfuSe target signature");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* create new image */
|
||||
image = dfu_image_new ();
|
||||
priv = GET_PRIVATE (image);
|
||||
priv->alt_setting = im->alt_setting;
|
||||
if (GUINT32_FROM_LE (im->target_named) == 0x01)
|
||||
memcpy (priv->name, im->target_name, 255);
|
||||
|
||||
/* parse elements */
|
||||
length -= offset;
|
||||
elements = GUINT32_FROM_LE (im->elements);
|
||||
for (j = 0; j < elements; j++) {
|
||||
guint32 consumed_local;
|
||||
g_autoptr(DfuElement) element = NULL;
|
||||
element = dfu_element_from_dfuse (data + offset, length,
|
||||
&consumed_local, error);
|
||||
if (element == NULL)
|
||||
return NULL;
|
||||
dfu_image_add_element (image, element);
|
||||
offset += consumed_local;
|
||||
length -= consumed_local;
|
||||
}
|
||||
|
||||
/* return size */
|
||||
if (consumed != NULL)
|
||||
*consumed = offset;
|
||||
|
||||
return g_object_ref (image);
|
||||
}
|
||||
|
||||
/**
|
||||
* dfu_image_to_dfuse: (skip)
|
||||
* @image: a #DfuImage
|
||||
*
|
||||
* Packs a DfuSe image
|
||||
*
|
||||
* Returns: (transfer full): the packed data
|
||||
**/
|
||||
GBytes *
|
||||
dfu_image_to_dfuse (DfuImage *image)
|
||||
{
|
||||
DfuImagePrivate *priv = GET_PRIVATE (image);
|
||||
DfuElement *element;
|
||||
DfuSeImagePrefix *im;
|
||||
GBytes *bytes;
|
||||
guint32 length_total = 0;
|
||||
guint32 offset = sizeof (DfuSeImagePrefix);
|
||||
guint8 *buf;
|
||||
guint i;
|
||||
g_autoptr(GPtrArray) element_array = NULL;
|
||||
|
||||
/* get total size */
|
||||
element_array = g_ptr_array_new_with_free_func ((GDestroyNotify) g_bytes_unref);
|
||||
for (i = 0; i < priv->elements->len; i++) {
|
||||
element = g_ptr_array_index (priv->elements, i);
|
||||
bytes = dfu_element_to_dfuse (element);
|
||||
g_ptr_array_add (element_array, bytes);
|
||||
length_total += (guint32) g_bytes_get_size (bytes);
|
||||
}
|
||||
|
||||
/* add prefix */
|
||||
buf = g_malloc0 (length_total + sizeof (DfuSeImagePrefix));
|
||||
im = (DfuSeImagePrefix *) buf;
|
||||
memcpy (im->sig, "Target", 6);
|
||||
im->alt_setting = priv->alt_setting;
|
||||
if (priv->name != NULL) {
|
||||
im->target_named = GUINT32_TO_LE (0x01);
|
||||
memcpy (im->target_name, priv->name, 255);
|
||||
}
|
||||
im->target_size = GUINT32_TO_LE (length_total);
|
||||
im->elements = GUINT32_TO_LE (priv->elements->len);
|
||||
|
||||
/* copy data */
|
||||
for (i = 0; i < element_array->len; i++) {
|
||||
const guint8 *data;
|
||||
gsize length;
|
||||
bytes = g_ptr_array_index (element_array, i);
|
||||
data = g_bytes_get_data (bytes, &length);
|
||||
memcpy (buf + offset, data, length);
|
||||
offset += (guint32) length;
|
||||
}
|
||||
return g_bytes_new_take (buf, length_total + sizeof (DfuSeImagePrefix));
|
||||
}
|
||||
|
@ -675,6 +675,7 @@ main (int argc, char **argv)
|
||||
|
||||
/* log everything */
|
||||
g_setenv ("G_MESSAGES_DEBUG", "all", FALSE);
|
||||
g_setenv ("DFU_SELF_TEST", "", FALSE);
|
||||
|
||||
/* tests go here */
|
||||
g_test_add_func ("/libdfu/enums", dfu_enums_func);
|
||||
|
Loading…
Reference in New Issue
Block a user