diff options
-rw-r--r-- | tools/firmware-utils/Makefile | 1 | ||||
-rw-r--r-- | tools/firmware-utils/src/lxlfw.c | 282 |
2 files changed, 283 insertions, 0 deletions
diff --git a/tools/firmware-utils/Makefile b/tools/firmware-utils/Makefile index 2d2c96ce0a..97c89eec27 100644 --- a/tools/firmware-utils/Makefile +++ b/tools/firmware-utils/Makefile @@ -25,6 +25,7 @@ define Host/Compile mkdir -p $(HOST_BUILD_DIR)/bin $(call cc,addpattern) $(call cc,asustrx) + $(call cc,lxlfw) $(call cc,trx) $(call cc,otrx) $(call cc,motorola-bin) diff --git a/tools/firmware-utils/src/lxlfw.c b/tools/firmware-utils/src/lxlfw.c new file mode 100644 index 0000000000..15678b8736 --- /dev/null +++ b/tools/firmware-utils/src/lxlfw.c @@ -0,0 +1,282 @@ +// SPDX-License-Identifier: GPL-2.0-or-later OR MIT +/* + * Luxul's firmware container format + * + * Copyright 2020 Legrand AV Inc. + */ + +#define _GNU_SOURCE + +#include <byteswap.h> +#include <endian.h> +#include <errno.h> +#include <libgen.h> +#include <stddef.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#if __BYTE_ORDER == __BIG_ENDIAN +#define cpu_to_le32(x) bswap_32(x) +#define cpu_to_le16(x) bswap_16(x) +#define le32_to_cpu(x) bswap_32(x) +#define le16_to_cpu(x) bswap_16(x) +#elif __BYTE_ORDER == __LITTLE_ENDIAN +#define cpu_to_le32(x) (x) +#define cpu_to_le16(x) (x) +#define le32_to_cpu(x) (x) +#define le16_to_cpu(x) (x) +#endif + +#define min(a, b) \ + ({ \ + __typeof__ (a) _a = (a); \ + __typeof__ (b) _b = (b); \ + _a < _b ? _a : _b; \ + }) + +#define max(a, b) \ + ({ \ + __typeof__ (a) _a = (a); \ + __typeof__ (b) _b = (b); \ + _a > _b ? _a : _b; \ + }) + +#define LXL_FLAGS_VENDOR_LUXUL 0x00000001 + +struct lxl_hdr { + char magic[4]; /* "LXL#" */ + uint32_t version; + uint32_t hdr_len; + uint8_t v0_end[0]; + /* Version: 1+ */ + uint32_t flags; + char board[16]; + uint8_t v1_end[0]; + /* Version: 2+ */ + uint8_t release[4]; + uint8_t v2_end[0]; +} __packed; + +static uint32_t lxlfw_hdr_len(uint32_t version) +{ + switch (version) { + case 0: + return offsetof(struct lxl_hdr, v0_end); + case 1: + return offsetof(struct lxl_hdr, v1_end); + case 2: + return offsetof(struct lxl_hdr, v2_end); + default: + fprintf(stderr, "Unsupported version %d\n", version); + return 0; + } +} + +/************************************************** + * Info + **************************************************/ + +static int lxlfw_info(int argc, char **argv) { + struct lxl_hdr hdr; + uint32_t version; + uint32_t hdr_len; + char board[17]; + size_t bytes; + int err = 0; + FILE *lxl; + int flags; + + if (argc < 3) { + fprintf(stderr, "Missing <file> argument\n"); + err = -EINVAL; + goto out; + } + + lxl = fopen(argv[2], "r"); + if (!lxl) { + fprintf(stderr, "Could not open \"%s\" file\n", argv[2]); + err = -ENOENT; + goto out; + } + + bytes = fread(&hdr, 1, sizeof(hdr), lxl); + if (bytes < offsetof(struct lxl_hdr, v0_end)) { + fprintf(stderr, "Input file too small to use Luxul format\n"); + err = -ENXIO; + goto err_close; + } + + if (memcmp(hdr.magic, "LXL#", 4)) { + fprintf(stderr, "File <file> does not use Luxul's format\n"); + err = -EINVAL; + goto err_close; + } + + version = le32_to_cpu(hdr.version); + hdr_len = lxlfw_hdr_len(version); + if (bytes < hdr_len) { + fprintf(stderr, "Input file too small for header version %d\n", version); + err = -ENXIO; + goto err_close; + } + + printf("Format version:\t%d\n", version); + printf("Header length:\t%d\n", le32_to_cpu(hdr.hdr_len)); + if (version >= 1) { + printf("Flags:\t\t"); + flags = le32_to_cpu(hdr.flags); + if (flags & LXL_FLAGS_VENDOR_LUXUL) + printf("VENDOR_LUXUL "); + printf("\n"); + memcpy(board, hdr.board, sizeof(hdr.board)); + board[16] = '\0'; + printf("Board:\t\t%s\n", board); + } + if (version >= 2) { + printf("Release:\t"); + if (hdr.release[0] || hdr.release[1] || hdr.release[2] || hdr.release[3]) { + printf("%hu.%hu.%hu", hdr.release[0], hdr.release[1], hdr.release[2]); + if (hdr.release[3]) + printf(".%hu", hdr.release[3]); + } + printf("\n"); + } + +err_close: + fclose(lxl); +out: + return err; +} + +/************************************************** + * Create + **************************************************/ + +static int lxlfw_create(int argc, char **argv) { + struct lxl_hdr hdr = { + .magic = { 'L', 'X', 'L', '#' }, + }; + char *in_path = NULL; + uint32_t version = 0; + uint32_t hdr_len; + ssize_t bytes; + char buf[512]; + int err = 0; + FILE *lxl; + FILE *in; + int c; + + if (argc < 3) { + fprintf(stderr, "Missing <file> argument\n"); + err = -EINVAL; + goto out; + } + + optind = 3; + while ((c = getopt(argc, argv, "i:lb:r:")) != -1) { + switch (c) { + case 'i': + in_path = optarg; + break; + case 'l': + hdr.flags |= cpu_to_le32(LXL_FLAGS_VENDOR_LUXUL); + version = max(version, 1); + break; + case 'b': + memcpy(hdr.board, optarg, strlen(optarg) > 16 ? 16 : strlen(optarg)); + version = max(version, 1); + break; + case 'r': + if (sscanf(optarg, "%hhu.%hhu.%hhu.%hhu", &hdr.release[0], &hdr.release[1], &hdr.release[2], &hdr.release[3]) < 1) { + fprintf(stderr, "Failed to parse release number \"%s\"\n", optarg); + err = -EINVAL; + goto out; + } + version = max(version, 2); + break; + } + } + + hdr.version = cpu_to_le32(version); + hdr_len = lxlfw_hdr_len(version); + if (!hdr_len) { + err = -EINVAL; + goto out; + } + hdr.hdr_len = cpu_to_le32(hdr_len); + + if (!in_path) { + fprintf(stderr, "Missing input file argument\n"); + err = -EINVAL; + goto out; + } + + in = fopen(in_path, "r"); + if (!in) { + fprintf(stderr, "Could not open input file %s\n", in_path); + err = -EIO; + goto out; + } + + lxl = fopen(argv[2], "w+"); + if (!lxl) { + fprintf(stderr, "Could not open \"%s\" file\n", argv[2]); + err = -EIO; + goto err_close_in; + } + + bytes = fwrite(&hdr, 1, hdr_len, lxl); + if (bytes != hdr_len) { + fprintf(stderr, "Could not write Luxul's header\n"); + err = -EIO; + goto err_close_lxl; + } + + while ((bytes = fread(buf, 1, sizeof(buf), in)) > 0) { + if (fwrite(buf, 1, bytes, lxl) != bytes) { + fprintf(stderr, "Could not copy %zu bytes from input file\n", bytes); + err = -EIO; + goto err_close_lxl; + } + } + +err_close_lxl: + fclose(lxl); +err_close_in: + fclose(in); +out: + return err; +} + +/************************************************** + * Start + **************************************************/ + +static void usage() { + printf("Usage:\n"); + printf("\n"); + printf("Get info about Luxul firmware:\n"); + printf("\tlxlfw info <file>\n"); + printf("\n"); + printf("Create new Luxul firmware:\n"); + printf("\tlxlfw create <file> [options]\n"); + printf("\t-i file\t\t\t\tinput file for Luxul's firmware container\n"); + printf("\t-l\t\t\t\tmark firmware as created by Luxul company (DON'T USE)\n"); + printf("\t-b board\t\t\tboard (device) name\n"); + printf("\t-r release\t\t\trelease number (e.g. 5.1.0, 7.1.0.2)\n"); +} + +int main(int argc, char **argv) { + if (argc > 1) { + if (!strcmp(argv[1], "info")) + return lxlfw_info(argc, argv); + else if (!strcmp(argv[1], "create")) + return lxlfw_create(argc, argv); + } + + usage(); + return 0; +} |