aboutsummaryrefslogtreecommitdiffstats
path: root/tools/firmware-utils/src/buffalo-tag.c
diff options
context:
space:
mode:
Diffstat (limited to 'tools/firmware-utils/src/buffalo-tag.c')
-rw-r--r--tools/firmware-utils/src/buffalo-tag.c374
1 files changed, 374 insertions, 0 deletions
diff --git a/tools/firmware-utils/src/buffalo-tag.c b/tools/firmware-utils/src/buffalo-tag.c
new file mode 100644
index 000000000..b5db72ef9
--- /dev/null
+++ b/tools/firmware-utils/src/buffalo-tag.c
@@ -0,0 +1,374 @@
+/*
+ * Copyright (C) 2009-2011 Gabor Juhos <juhosg@openwrt.org>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <libgen.h>
+#include <getopt.h> /* for getopt() */
+#include <netinet/in.h>
+
+#include "buffalo-lib.h"
+
+#define ERR(fmt, ...) do { \
+ fflush(0); \
+ fprintf(stderr, "[%s] *** error: " fmt "\n", \
+ progname, ## __VA_ARGS__ ); \
+} while (0)
+
+static char *region_table[] = {
+ "JP", "US", "EU", "AP", "TW", "KR"
+};
+
+#define MAX_INPUT_FILES 2
+
+static char *progname;
+static char *ifname[MAX_INPUT_FILES];
+static ssize_t fsize[MAX_INPUT_FILES];
+static int num_files;
+static char *ofname;
+static char *product;
+static char *brand;
+static char *language;
+static char *hwver;
+static char *platform;
+static int flag;
+static char *major;
+static char *minor = "1.01";
+static int skipcrc;
+static uint32_t base1;
+static uint32_t base2;
+static char *region_code;
+static uint32_t region_mask;
+static int num_regions;
+
+void usage(int status)
+{
+ FILE *stream = (status != EXIT_SUCCESS) ? stderr : stdout;
+
+ fprintf(stream, "Usage: %s [OPTIONS...]\n", progname);
+ fprintf(stream,
+"\n"
+"Options:\n"
+" -a <platform> set platform to <platform>\n"
+" -b <brand> set brand to <brand>\n"
+" -c <base1>\n"
+" -d <base2>\n"
+" -f <flag> set flag to <flag>\n"
+" -i <file> read input from the file <file>\n"
+" -l <language> set language to <language>\n"
+" -m <version> set minor version to <version>\n"
+" -o <file> write output to the file <file>\n"
+" -p <product> set product to <product>\n"
+" -r <region> set image region to <region>\n"
+" valid regions: JP, US, EU, AP, TW, KR, M_\n"
+" -s skip CRC calculation\n"
+" -v <version> set major version to <version>\n"
+" -w <version> set harwdware version to <version>\n"
+" -h show this screen\n"
+ );
+
+ exit(status);
+}
+
+static int check_params(void)
+{
+
+#define CHECKSTR(_var, _name, _len) do { \
+ if ((_var) == NULL) { \
+ ERR("no %s specified", (_name)); \
+ return -1; \
+ } \
+ if ((_len) > 0 && \
+ strlen((_var)) > ((_len) - 1)) { \
+ ERR("%s is too long", (_name)); \
+ return -1; \
+ } \
+} while (0)
+
+ if (num_files == 0)
+ ERR("no input files specified");
+
+ CHECKSTR(ofname, "output file", 0);
+ CHECKSTR(brand, "brand", TAG_BRAND_LEN);
+ CHECKSTR(product, "product", TAG_PRODUCT_LEN);
+ CHECKSTR(platform, "platform", TAG_PLATFORM_LEN);
+ CHECKSTR(major, "major version", TAG_VERSION_LEN);
+ CHECKSTR(minor, "minor version", TAG_VERSION_LEN);
+ CHECKSTR(language, "language", TAG_LANGUAGE_LEN);
+
+ if (hwver)
+ CHECKSTR(hwver, "hardware version", 2);
+
+ if (num_regions == 0) {
+ ERR("no region code specified");
+ return -1;
+ }
+
+ return 0;
+
+#undef CHECKSTR
+}
+
+static int process_region(char *reg)
+{
+ int i;
+
+ if (strlen(reg) != 2) {
+ ERR("invalid region code '%s'", reg);
+ return -1;
+ }
+
+ if (strcmp(reg, "M_") == 0) {
+ region_code = reg;
+ region_mask |= ~0;
+ num_regions = 32;
+ return 0;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(region_table); i++)
+ if (strcmp(reg, region_table[i]) == 0) {
+ region_code = reg;
+ region_mask |= 1 << i;
+ num_regions++;
+ return 0;
+ }
+
+ ERR("unknown region code '%s'", reg);
+ return -1;
+}
+
+static int process_ifname(char *name)
+{
+ if (num_files >= ARRAY_SIZE(ifname)) {
+ ERR("too many input files specified");
+ return -1;
+ }
+
+ ifname[num_files++] = name;
+ return 0;
+}
+
+static void fixup_tag(unsigned char *buf, ssize_t buflen)
+{
+ struct buffalo_tag *tag = (struct buffalo_tag *) buf;
+
+ memset(tag, '\0', sizeof(*tag));
+
+ memcpy(tag->brand, brand, strlen(brand));
+ memcpy(tag->product, product, strlen(product));
+ memcpy(tag->platform, platform, strlen(platform));
+ memcpy(tag->ver_major, major, strlen(major));
+ memcpy(tag->ver_minor, minor, strlen(minor));
+ memcpy(tag->language, language, strlen(language));
+
+ if (num_regions > 1) {
+ tag->region_code[0] = 'M';
+ tag->region_code[1] = '_';
+ tag->region_mask = htonl(region_mask);
+ } else {
+ memcpy(tag->region_code, region_code, 2);
+ }
+
+ tag->len = htonl(buflen);
+ tag->data_len = htonl(fsize[0]);
+ tag->base1 = htonl(base1);
+ tag->base2 = htonl(base2);
+ tag->flag = flag;
+
+ if (hwver) {
+ memcpy(tag->hwv, "hwv", 3);
+ memcpy(tag->hwv_val, hwver, strlen(hwver));
+ }
+
+ if (!skipcrc)
+ tag->crc = htonl(buffalo_crc(buf, buflen));
+}
+
+static void fixup_tag2(unsigned char *buf, ssize_t buflen)
+{
+ struct buffalo_tag2 *tag = (struct buffalo_tag2 *) buf;
+
+ memset(tag, '\0', sizeof(*tag));
+
+ memcpy(tag->brand, brand, strlen(brand));
+ memcpy(tag->product, product, strlen(product));
+ memcpy(tag->platform, platform, strlen(platform));
+ memcpy(tag->ver_major, major, strlen(major));
+ memcpy(tag->ver_minor, minor, strlen(minor));
+ memcpy(tag->language, language, strlen(language));
+
+ if (num_regions > 1) {
+ tag->region_code[0] = 'M';
+ tag->region_code[1] = '_';
+ tag->region_mask = htonl(region_mask);
+ } else {
+ memcpy(tag->region_code, region_code, 2);
+ }
+
+ tag->total_len = htonl(buflen);
+ tag->len1 = htonl(fsize[0]);
+ tag->len2 = htonl(fsize[1]);
+ tag->flag = flag;
+
+ if (hwver) {
+ memcpy(tag->hwv, "hwv", 3);
+ memcpy(tag->hwv_val, hwver, strlen(hwver));
+ }
+
+ if (!skipcrc)
+ tag->crc = htonl(buffalo_crc(buf, buflen));
+}
+
+static int tag_file(void)
+{
+ unsigned char *buf;
+ ssize_t offset;
+ ssize_t hdrlen;
+ ssize_t buflen;
+ int err;
+ int ret = -1;
+ int i;
+
+ if (num_files == 1)
+ hdrlen = sizeof(struct buffalo_tag);
+ else
+ hdrlen = sizeof(struct buffalo_tag2);
+
+ buflen = hdrlen;
+
+ for (i = 0; i < num_files; i++) {
+ fsize[i] = get_file_size(ifname[i]);
+ if (fsize[i] < 0) {
+ ERR("unable to get size of '%s'", ifname[i]);
+ goto out;
+ }
+ buflen += fsize[i];
+ }
+
+ buf = malloc(buflen);
+ if (!buf) {
+ ERR("no memory for buffer\n");
+ goto out;
+ }
+
+ offset = hdrlen;
+ for (i = 0; i < num_files; i++) {
+ err = read_file_to_buf(ifname[i], buf + offset, fsize[i]);
+ if (err) {
+ ERR("unable to read from file '%s'", ifname[i]);
+ goto free_buf;
+ }
+
+ offset += fsize[i];
+ }
+
+ if (num_files == 1)
+ fixup_tag(buf, buflen);
+ else
+ fixup_tag2(buf, buflen);
+
+ err = write_buf_to_file(ofname, buf, buflen);
+ if (err) {
+ ERR("unable to write to file '%s'", ofname);
+ goto free_buf;
+ }
+
+ ret = 0;
+
+free_buf:
+ free(buf);
+out:
+ return ret;
+}
+
+int main(int argc, char *argv[])
+{
+ int res = EXIT_FAILURE;
+ int err;
+
+ progname = basename(argv[0]);
+
+ while ( 1 ) {
+ int c;
+
+ c = getopt(argc, argv, "a:b:c:d:f:hi:l:m:o:p:r:sv:w:");
+ if (c == -1)
+ break;
+
+ switch (c) {
+ case 'a':
+ platform = optarg;
+ break;
+ case 'b':
+ brand = optarg;
+ break;
+ case 'c':
+ base1 = strtoul(optarg, NULL, 16);
+ break;
+ case 'd':
+ base2 = strtoul(optarg, NULL, 16);
+ break;
+ case 'f':
+ flag = strtoul(optarg, NULL, 2);
+ break;
+ case 'i':
+ err = process_ifname(optarg);
+ if (err)
+ goto out;
+ break;
+ case 'l':
+ language = optarg;
+ break;
+ case 'm':
+ minor = optarg;
+ break;
+ case 'o':
+ ofname = optarg;
+ break;
+ case 'p':
+ product = optarg;
+ break;
+ case 'r':
+ err = process_region(optarg);
+ if (err)
+ goto out;
+ break;
+ case 's':
+ skipcrc = 1;
+ break;
+ case 'v':
+ major = optarg;
+ break;
+ case 'w':
+ hwver = optarg;
+ break;
+ case 'h':
+ usage(EXIT_SUCCESS);
+ break;
+ default:
+ usage(EXIT_FAILURE);
+ break;
+ }
+ }
+
+ err = check_params();
+ if (err)
+ goto out;
+
+ err = tag_file();
+ if (err)
+ goto out;
+
+ res = EXIT_SUCCESS;
+
+out:
+ return res;
+}