diff --git a/src/fu-ihex-firmware.c b/src/fu-ihex-firmware.c index b60c9ec10..a8c15df8a 100644 --- a/src/fu-ihex-firmware.c +++ b/src/fu-ihex-firmware.c @@ -16,6 +16,7 @@ struct _FuIhexFirmware { FuFirmware parent_instance; + GPtrArray *records; }; G_DEFINE_TYPE (FuIhexFirmware, fu_ihex_firmware, FU_TYPE_FIRMWARE) @@ -28,6 +29,44 @@ G_DEFINE_TYPE (FuIhexFirmware, fu_ihex_firmware, FU_TYPE_FIRMWARE) #define DFU_INHX32_RECORD_TYPE_START_LINEAR 0x05 #define DFU_INHX32_RECORD_TYPE_SIGNATURE 0xfd +/** + * fu_ihex_firmware_get_records: + * @self: A #FuIhexFirmware + * + * Returns the raw lines from tokenization. + * + * This might be useful if the plugin is expecting the hex file to be a list + * of operations, rather than a simple linear image with filled holes. + * + * Returns: (transfer none) (element-type FuIhexFirmwareRecord): records + * + * Since: 1.3.4 + **/ +GPtrArray * +fu_ihex_firmware_get_records (FuIhexFirmware *self) +{ + g_return_val_if_fail (FU_IS_IHEX_FIRMWARE (self), NULL); + return self->records; +} + +static void +fu_ihex_firmware_record_free (FuIhexFirmwareRecord *rcd) +{ + g_string_free (rcd->buf, TRUE); + g_free (rcd); +} + +G_DEFINE_AUTOPTR_CLEANUP_FUNC(FuIhexFirmwareRecord, fu_ihex_firmware_record_free) + +static FuIhexFirmwareRecord * +fu_ihex_firmware_record_new (guint ln, const gchar *buf) +{ + FuIhexFirmwareRecord *rcd = g_new0 (FuIhexFirmwareRecord, 1); + rcd->ln = ln; + rcd->buf = g_string_new (buf); + return rcd; +} + static const gchar * fu_ihex_firmware_record_type_to_string (guint8 record_type) { @@ -48,6 +87,24 @@ fu_ihex_firmware_record_type_to_string (guint8 record_type) return NULL; } +static gboolean +fu_ihex_firmware_tokenize (FuFirmware *firmware, GBytes *fw, + FwupdInstallFlags flags, GError **error) +{ + FuIhexFirmware *self = FU_IHEX_FIRMWARE (firmware); + gsize sz = 0; + const gchar *data = g_bytes_get_data (fw, &sz); + g_auto(GStrv) lines = fu_common_strnsplit (data, sz, "\n", -1); + + for (guint ln = 0; lines[ln] != NULL; ln++) { + g_autoptr(FuIhexFirmwareRecord) rcd = NULL; + g_strdelimit (lines[ln], "\r\x1a", '\0'); + rcd = fu_ihex_firmware_record_new (ln + 1, lines[ln]); + g_ptr_array_add (self->records, g_steal_pointer (&rcd)); + } + return TRUE; +} + static gboolean fu_ihex_firmware_parse (FuFirmware *firmware, GBytes *fw, @@ -56,27 +113,21 @@ fu_ihex_firmware_parse (FuFirmware *firmware, FwupdInstallFlags flags, GError **error) { - const gchar *data; + FuIhexFirmware *self = FU_IHEX_FIRMWARE (firmware); gboolean got_eof = FALSE; - gsize sz = 0; guint32 abs_addr = 0x0; guint32 addr_last = 0x0; guint32 img_addr = G_MAXUINT32; guint32 seg_addr = 0x0; - g_auto(GStrv) lines = NULL; g_autoptr(FuFirmwareImage) img = fu_firmware_image_new (NULL); g_autoptr(GBytes) img_bytes = NULL; g_autoptr(GByteArray) buf = g_byte_array_new (); g_autoptr(GByteArray) buf_signature = g_byte_array_new (); - g_return_val_if_fail (fw != NULL, FALSE); - /* parse records */ - data = g_bytes_get_data (fw, &sz); - lines = fu_common_strnsplit (data, sz, "\n", -1); - for (guint ln = 0; lines[ln] != NULL; ln++) { - const gchar *line = lines[ln]; - gsize linesz; + for (guint k = 0; k < self->records->len; k++) { + FuIhexFirmwareRecord *rcd = g_ptr_array_index (self->records, k); + const gchar *line = rcd->buf->str; guint32 addr; guint8 byte_cnt; guint8 record_type; @@ -87,9 +138,7 @@ fu_ihex_firmware_parse (FuFirmware *firmware, continue; /* ignore blank lines */ - g_strdelimit (lines[ln], "\r\x1a", '\0'); - linesz = strlen (line); - if (linesz == 0) + if (rcd->buf->len == 0) continue; /* check starting token */ @@ -98,17 +147,17 @@ fu_ihex_firmware_parse (FuFirmware *firmware, FWUPD_ERROR, FWUPD_ERROR_INVALID_FILE, "invalid starting token on line %u: %s", - ln + 1, line); + rcd->ln, line); return FALSE; } /* check there's enough data for the smallest possible record */ - if (linesz < 11) { + if (rcd->buf->len < 11) { g_set_error (error, FWUPD_ERROR, FWUPD_ERROR_INVALID_FILE, "line %u is incomplete, length %u", - ln + 1, (guint) linesz); + rcd->ln, (guint) rcd->buf->len); return FALSE; } @@ -125,12 +174,12 @@ fu_ihex_firmware_parse (FuFirmware *firmware, /* position of checksum */ line_end = 9 + byte_cnt * 2; - if (line_end > (guint) linesz) { + if (line_end > (guint) rcd->buf->len) { g_set_error (error, FWUPD_ERROR, FWUPD_ERROR_INVALID_FILE, "line %u malformed, length: %u", - ln + 1, line_end); + rcd->ln, line_end); return FALSE; } @@ -146,7 +195,7 @@ fu_ihex_firmware_parse (FuFirmware *firmware, FWUPD_ERROR, FWUPD_ERROR_INVALID_FILE, "line %u has invalid checksum (0x%02x)", - ln + 1, checksum); + rcd->ln, checksum); return FALSE; } } @@ -166,7 +215,7 @@ fu_ihex_firmware_parse (FuFirmware *firmware, "invalid address 0x%x, last was 0x%x on line %u", (guint) addr, (guint) addr_last, - ln + 1); + rcd->ln); return FALSE; } @@ -182,12 +231,12 @@ fu_ihex_firmware_parse (FuFirmware *firmware, FWUPD_ERROR_INVALID_FILE, "hole of 0x%x bytes too large to fill on line %u", (guint) len_hole, - ln + 1); + rcd->ln); return FALSE; } if (addr_last > 0x0 && len_hole > 1) { g_debug ("filling address 0x%08x to 0x%08x on line %u", - addr_last + 1, addr_last + len_hole - 1, ln + 1); + addr_last + 1, addr_last + len_hole - 1, rcd->ln); for (guint j = 1; j < len_hole; j++) { /* although 0xff might be clearer, * we can't write 0xffff to pic14 */ @@ -213,21 +262,21 @@ fu_ihex_firmware_parse (FuFirmware *firmware, break; case DFU_INHX32_RECORD_TYPE_EXTENDED_LINEAR: abs_addr = fu_firmware_strparse_uint16 (line + 9) << 16; - g_debug (" abs_addr:\t0x%02x on line %u", abs_addr, ln + 1); + g_debug (" abs_addr:\t0x%02x on line %u", abs_addr, rcd->ln); break; case DFU_INHX32_RECORD_TYPE_START_LINEAR: abs_addr = fu_firmware_strparse_uint32 (line + 9); - g_debug (" abs_addr:\t0x%08x on line %u", abs_addr, ln + 1); + g_debug (" abs_addr:\t0x%08x on line %u", abs_addr, rcd->ln); break; case DFU_INHX32_RECORD_TYPE_EXTENDED_SEGMENT: /* segment base address, so ~1Mb addressable */ seg_addr = fu_firmware_strparse_uint16 (line + 9) * 16; - g_debug (" seg_addr:\t0x%08x on line %u", seg_addr, ln + 1); + g_debug (" seg_addr:\t0x%08x on line %u", seg_addr, rcd->ln); break; case DFU_INHX32_RECORD_TYPE_START_SEGMENT: /* initial content of the CS:IP registers */ seg_addr = fu_firmware_strparse_uint32 (line + 9); - g_debug (" seg_addr:\t0x%02x on line %u", seg_addr, ln + 1); + g_debug (" seg_addr:\t0x%02x on line %u", seg_addr, rcd->ln); break; case DFU_INHX32_RECORD_TYPE_SIGNATURE: for (guint i = 9; i < line_end; i += 2) { @@ -243,7 +292,7 @@ fu_ihex_firmware_parse (FuFirmware *firmware, FWUPD_ERROR, FWUPD_ERROR_INVALID_FILE, "invalid ihex record type %i on line %u", - record_type, ln + 1); + record_type, rcd->ln); return FALSE; } } @@ -360,16 +409,28 @@ fu_ihex_firmware_write (FuFirmware *firmware, GError **error) return g_bytes_new (str->str, str->len); } +static void +fu_ihex_firmware_finalize (GObject *object) +{ + FuIhexFirmware *self = FU_IHEX_FIRMWARE (object); + g_ptr_array_unref (self->records); + G_OBJECT_CLASS (fu_ihex_firmware_parent_class)->finalize (object); +} + static void fu_ihex_firmware_init (FuIhexFirmware *self) { + self->records = g_ptr_array_new_with_free_func ((GFreeFunc) fu_ihex_firmware_record_free); } static void fu_ihex_firmware_class_init (FuIhexFirmwareClass *klass) { + GObjectClass *object_class = G_OBJECT_CLASS (klass); FuFirmwareClass *klass_firmware = FU_FIRMWARE_CLASS (klass); + object_class->finalize = fu_ihex_firmware_finalize; klass_firmware->parse = fu_ihex_firmware_parse; + klass_firmware->tokenize = fu_ihex_firmware_tokenize; klass_firmware->write = fu_ihex_firmware_write; } diff --git a/src/fu-ihex-firmware.h b/src/fu-ihex-firmware.h index 4632d7346..a6754dd9b 100644 --- a/src/fu-ihex-firmware.h +++ b/src/fu-ihex-firmware.h @@ -11,4 +11,10 @@ #define FU_TYPE_IHEX_FIRMWARE (fu_ihex_firmware_get_type ()) G_DECLARE_FINAL_TYPE (FuIhexFirmware, fu_ihex_firmware, FU, IHEX_FIRMWARE, FuFirmware) +typedef struct { + guint ln; + GString *buf; +} FuIhexFirmwareRecord; + FuFirmware *fu_ihex_firmware_new (void); +GPtrArray *fu_ihex_firmware_get_records (FuIhexFirmware *self);