[OpenWrt-Devel,1/2] firmware-utils: add lxlfw tool for generating Luxul firmwares
diff mbox series

Message ID 1580924264-939-1-git-send-email-dan.haab@luxul.com
State Accepted
Headers show
Series
  • [OpenWrt-Devel,1/2] firmware-utils: add lxlfw tool for generating Luxul firmwares
Related show

Commit Message

Dan Haab Feb. 5, 2020, 5:37 p.m. UTC
From: Dan Haab <dan.haab@legrand.com>

It's a simple tool prepending image with a Luxul header.

Signed-off-by: Dan Haab <dan.haab@legrand.com>
---
 tools/firmware-utils/Makefile    |   1 +
 tools/firmware-utils/src/lxlfw.c | 282 +++++++++++++++++++++++++++++++++++++++
 2 files changed, 283 insertions(+)
 create mode 100644 tools/firmware-utils/src/lxlfw.c

Patch
diff mbox series

diff --git a/tools/firmware-utils/Makefile b/tools/firmware-utils/Makefile
index 2d2c96c..97c89ee 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 0000000..15678b8
--- /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;
+}