Echo Digital Audio corporation designed data format of firmware. The format seems to be suitable for their SDK for customers and closed. FFADO project implements the format parser in libffado2. Although the implementation is not enough in several points, it's good point to guess the format. I captured asynchronous packet by bus analyzer during updating firmware by Windows driver, aggregated the content of packet by script, then compared it to content of firmware. Some points are cleared (but not all). This commit adds parser for data binary shipped by Echo Digital Audio corporation. I note that driver package shipped by vendors for Fireworks based devices includes no EULA. I think this kind of reimplementation is not limited in the case. The content of file consists of two parts; header after magic bytes, and payload. The header includes metadata about the data of payload. There are four kind of data; for DSP, for IceLynx Micro, for arbitrary data, and for FPGA. The content except for magic bytes is hexadecimal string, therefore it's possible to print out by simple dump tool; cat(1) and less(1). The header includes CRC32 value of the decoded data and checksum of every bytes of the decoded data. When the header includes a flag, the CRC32 value is written to the end of area for firmware. Signed-off-by: Takashi Sakamoto <o-takashi@xxxxxxxxxxxxx> --- efw-downloader/src/file-cntr.c | 183 +++++++++++++++++++++++++++++++++ efw-downloader/src/file-cntr.h | 39 +++++++ efw-downloader/src/meson.build | 5 + 3 files changed, 227 insertions(+) create mode 100644 efw-downloader/src/file-cntr.c create mode 100644 efw-downloader/src/file-cntr.h diff --git a/efw-downloader/src/file-cntr.c b/efw-downloader/src/file-cntr.c new file mode 100644 index 0000000..1e159f7 --- /dev/null +++ b/efw-downloader/src/file-cntr.c @@ -0,0 +1,183 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// Copyright (c) 2020 Takashi Sakamoto +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> + +#include <zlib.h> + +#include "file-cntr.h" + +#define MAGIC_BYTES "1651 1 0 0 0\r\n" +#define PAYLOAD_OFFSET_QUADS 0x3f + +static int parse_entry(FILE *handle, uint32_t *val) +{ + char buf[16]; + char *end; + size_t len; + int err = 0; + + if (fgets(buf, sizeof(buf), handle) != buf) { + if (ferror(handle)) + err = -errno; + else + err = INT_MAX; // Use the value for EOF. + return err; + } + + len = strlen(buf); + if (buf[0] != '0' || buf[1] != 'x' || buf[len - 2] != '\r' || buf[len - 1] != '\n') + return -EPROTO; + buf[len - 2] = '\0'; + + *val = strtoul(buf, &end, 16); + if (*end != '\0') + return -EPROTO; + + return err; +} + +static int parse_header(FILE *handle, struct file_cntr_header *header) +{ + uint32_t *buf = (uint32_t *)header; + int err = 0; + int i; + + for (i = 0; i < 8; ++i) { + uint32_t val; + + err = parse_entry(handle, &val); + if (err != 0) + break; + + buf[i] = val; + } + + if (err == INT_MAX) + err = -ENODATA; + + return err; +} + +static int parse_payload(FILE *handle, unsigned int quads, uint32_t *blob) +{ + int err = 0; + int i; + + for (i = 0; i < quads; ++i) { + uint32_t val; + + err = parse_entry(handle, &val); + if (err != 0) + break; + + blob[i] = val; + } + + if (i != quads || err == INT_MAX) + err = -ENODATA; + + return err; +} + +static int check_crc32(struct file_cntr *cntr) +{ + uint32_t blob_crc32; + + blob_crc32 = crc32(0ul, (const uint8_t *)cntr->payload.blob, + cntr->payload.count * sizeof(*cntr->payload.blob)); + if (blob_crc32 != cntr->header.blob_crc32) + return -EINVAL; + + return 0; +} + +static int check_checksum(struct file_cntr *cntr) +{ + uint32_t checksum = 0; + int i, j; + + for (i = 0; i < cntr->payload.count; ++i) { + for (j = 3; j >= 0; --j) + checksum += (cntr->payload.blob[i] >> (j * 8)) & 0xff; + } + + if (checksum != cntr->header.blob_checksum) + return -EINVAL; + + return 0; +} + +int file_cntr_parse(struct file_cntr *cntr, const char *filepath) +{ + FILE *handle; + char buf[16]; + size_t till_data; + int err = 0; + + handle = fopen(filepath, "r"); + if (handle == NULL) + return -errno; + + // Check magic bytes. + if (fgets(buf, sizeof(buf), handle) != buf) { + if (ferror(handle)) + err = -errno; + else + err = -ENODATA; + goto end; + } + + if (memcmp(buf, MAGIC_BYTES, sizeof(MAGIC_BYTES))) { + err = -EPROTO; + goto end; + } + + // Parse header. + err = parse_header(handle, &cntr->header); + if (err < 0) + goto end; + + // Skip to area for data. + till_data = (PAYLOAD_OFFSET_QUADS - 7) * 12; + if (fseek(handle, till_data, SEEK_CUR) < 0) { + err = -errno; + goto end; + } + + cntr->payload.blob = calloc(cntr->header.blob_quads, sizeof(*cntr->payload.blob)); + if (cntr->payload.blob == NULL) { + err = -ENOMEM; + goto end; + } + cntr->payload.count = cntr->header.blob_quads; + + err = parse_payload(handle, cntr->header.blob_quads, cntr->payload.blob); + if (err < 0) { + free(cntr->payload.blob); + goto end; + } + + err = check_crc32(cntr); + if (err < 0) { + free(cntr->payload.blob); + goto end; + } + + err = check_checksum(cntr); + if (err < 0) + free(cntr->payload.blob); +end: + fclose(handle); + + return err; +} + +void file_cntr_release(struct file_cntr *cntr) +{ + if (cntr->payload.blob != NULL) + free(cntr->payload.blob); + cntr->payload.blob = NULL; +} diff --git a/efw-downloader/src/file-cntr.h b/efw-downloader/src/file-cntr.h new file mode 100644 index 0000000..c4657f1 --- /dev/null +++ b/efw-downloader/src/file-cntr.h @@ -0,0 +1,39 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// Copyright (c) 2020 Takashi Sakamoto +#ifndef __FILE_CNTR__ +#define __FILE_CNTR__ + +#include <stdint.h> + +enum blob_type { + BLOB_TYPE_DSP = 0, + BLOB_TYPE_ICELYNX = 1, + BLOB_TYPE_DATA = 2, + BLOB_TYPE_FPGA = 3, +}; + +struct file_cntr_header { + enum blob_type type; + uint32_t offset_addr; + uint32_t blob_quads; + uint32_t blob_crc32; + uint32_t blob_checksum; + uint32_t version; + uint32_t crc_in_region_end; + uint32_t cntr_quads; +}; + +struct file_cntr_payload { + uint32_t *blob; + size_t count; +}; + +struct file_cntr { + struct file_cntr_header header; + struct file_cntr_payload payload; +}; + +int file_cntr_parse(struct file_cntr *cntr, const char *filepath); +void file_cntr_release(struct file_cntr *cntr); + +#endif diff --git a/efw-downloader/src/meson.build b/efw-downloader/src/meson.build index 8738d76..05d491d 100644 --- a/efw-downloader/src/meson.build +++ b/efw-downloader/src/meson.build @@ -11,12 +11,15 @@ hinawa = dependency('hinawa', version: '>=2.1', ) +zlib = dependency('zlib') + sources = [ 'main.c', 'efw-proto.c', 'config-rom.c', 'node-dispatcher.c', 'efw-commands.c', + 'file-cntr.c', 'subcmd-device.c', 'op-device-read.c', ] @@ -26,6 +29,7 @@ headers = [ 'config-rom.h', 'node-dispatcher.h', 'efw-commands.h', + 'file-cntr.h', 'subcmds.h', ] @@ -42,6 +46,7 @@ executable('efw-downloader', dependencies: [ gobject, hinawa, + zlib, ], install: true, ) -- 2.25.1