2 * RouterBOOT configuration utility
4 * Copyright (C) 2010 Gabor Juhos <juhosg@openwrt.org>
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License version 2 as published
8 * by the Free Software Foundation.
20 #include <linux/limits.h>
25 #define RBCFG_TMP_FILE "/tmp/.rbcfg"
26 #define RBCFG_MTD_NAME "soft_config"
28 #define RB_ERR_NOTFOUND 1
29 #define RB_ERR_INVALID 2
30 #define RB_ERR_NOMEM 3
33 #define ARRAY_SIZE(_a) (sizeof((_a)) / sizeof((_a)[0]))
51 #define RBCFG_ENV_TYPE_U32 0
57 const struct rbcfg_value *values;
61 #define CMD_FLAG_USES_CFG 0x01
63 struct rbcfg_command {
67 int (*exec)(int argc, const char *argv[]);
70 static void usage(void);
74 static struct rbcfg_ctx *rbcfg_ctx;
75 static char *rbcfg_name;
77 #define CFG_U32(_name, _desc, _val) { \
83 static const struct rbcfg_value rbcfg_boot_delay[] = {
84 CFG_U32("1", "1 second", RB_BOOT_DELAY_1SEC),
85 CFG_U32("2", "2 seconds", RB_BOOT_DELAY_2SEC),
86 CFG_U32("3", "3 seconds", RB_BOOT_DELAY_3SEC),
87 CFG_U32("4", "4 seconds", RB_BOOT_DELAY_4SEC),
88 CFG_U32("5", "5 seconds", RB_BOOT_DELAY_5SEC),
89 CFG_U32("6", "6 seconds", RB_BOOT_DELAY_6SEC),
90 CFG_U32("7", "7 seconds", RB_BOOT_DELAY_7SEC),
91 CFG_U32("8", "8 seconds", RB_BOOT_DELAY_8SEC),
92 CFG_U32("9", "9 seconds", RB_BOOT_DELAY_9SEC),
95 static const struct rbcfg_value rbcfg_boot_device[] = {
96 CFG_U32("eth", "boot over Ethernet",
97 RB_BOOT_DEVICE_ETHER),
98 CFG_U32("nandeth", "boot from NAND, if fail then Ethernet",
99 RB_BOOT_DEVICE_NANDETH),
100 CFG_U32("ethnand", "boot Ethernet once, then NAND",
101 RB_BOOT_DEVICE_ETHONCE),
102 CFG_U32("nand", "boot from NAND only",
103 RB_BOOT_DEVICE_NANDONLY),
106 static const struct rbcfg_value rbcfg_boot_key[] = {
107 CFG_U32("any", "any key", RB_BOOT_KEY_ANY),
108 CFG_U32("del", "<Delete> key only", RB_BOOT_KEY_DEL),
111 static const struct rbcfg_value rbcfg_boot_protocol[] = {
112 CFG_U32("bootp", "BOOTP protocol", RB_BOOT_PROTOCOL_BOOTP),
113 CFG_U32("dhcp", "DHCP protocol", RB_BOOT_PROTOCOL_DHCP),
116 static const struct rbcfg_value rbcfg_uart_speed[] = {
117 CFG_U32("115200", "", RB_UART_SPEED_115200),
118 CFG_U32("57600", "", RB_UART_SPEED_57600),
119 CFG_U32("38400", "", RB_UART_SPEED_38400),
120 CFG_U32("19200", "", RB_UART_SPEED_19200),
121 CFG_U32("9600", "", RB_UART_SPEED_9600),
122 CFG_U32("4800", "", RB_UART_SPEED_4800),
123 CFG_U32("2400", "", RB_UART_SPEED_2400),
124 CFG_U32("1200", "", RB_UART_SPEED_1200),
125 CFG_U32("off", "disable console output", RB_UART_SPEED_OFF),
128 static const struct rbcfg_value rbcfg_cpu_mode[] = {
129 CFG_U32("powersave", "power save", RB_CPU_MODE_POWERSAVE),
130 CFG_U32("regular", "regular (better for -0c environment)",
131 RB_CPU_MODE_REGULAR),
134 static const struct rbcfg_value rbcfg_booter[] = {
135 CFG_U32("regular", "load regular booter", RB_BOOTER_REGULAR),
136 CFG_U32("backup", "force backup-booter loading", RB_BOOTER_BACKUP),
139 static const struct rbcfg_env rbcfg_envs[] = {
141 .name = "boot_delay",
142 .id = RB_ID_BOOT_DELAY,
143 .type = RBCFG_ENV_TYPE_U32,
144 .values = rbcfg_boot_delay,
145 .num_values = ARRAY_SIZE(rbcfg_boot_delay),
147 .name = "boot_device",
148 .id = RB_ID_BOOT_DEVICE,
149 .type = RBCFG_ENV_TYPE_U32,
150 .values = rbcfg_boot_device,
151 .num_values = ARRAY_SIZE(rbcfg_boot_device),
154 .id = RB_ID_BOOT_KEY,
155 .type = RBCFG_ENV_TYPE_U32,
156 .values = rbcfg_boot_key,
157 .num_values = ARRAY_SIZE(rbcfg_boot_key),
159 .name = "boot_protocol",
160 .id = RB_ID_BOOT_PROTOCOL,
161 .type = RBCFG_ENV_TYPE_U32,
162 .values = rbcfg_boot_protocol,
163 .num_values = ARRAY_SIZE(rbcfg_boot_protocol),
167 .type = RBCFG_ENV_TYPE_U32,
168 .values = rbcfg_booter,
169 .num_values = ARRAY_SIZE(rbcfg_booter),
172 .id = RB_ID_CPU_MODE,
173 .type = RBCFG_ENV_TYPE_U32,
174 .values = rbcfg_cpu_mode,
175 .num_values = ARRAY_SIZE(rbcfg_cpu_mode),
177 .name = "uart_speed",
178 .id = RB_ID_UART_SPEED,
179 .type = RBCFG_ENV_TYPE_U32,
180 .values = rbcfg_uart_speed,
181 .num_values = ARRAY_SIZE(rbcfg_uart_speed),
185 static inline uint16_t
186 get_u16(const void *buf)
188 const uint8_t *p = buf;
190 return ((uint16_t) p[1] + ((uint16_t) p[0] << 8));
193 static inline uint32_t
194 get_u32(const void *buf)
196 const uint8_t *p = buf;
198 return ((uint32_t) p[3] + ((uint32_t) p[2] << 8) +
199 ((uint32_t) p[1] << 16) + ((uint32_t) p[0] << 24));
203 put_u32(void *buf, uint32_t val)
208 p[2] = (val >> 8) & 0xff;
209 p[1] = (val >> 16) & 0xff;
210 p[0] = (val >> 24) & 0xff;
214 rbcfg_find_tag(struct rbcfg_ctx *ctx, uint16_t tag_id, uint16_t *tag_len,
219 char *buf = ctx->buf;
220 unsigned int buflen = ctx->buflen;
221 int ret = RB_ERR_NOTFOUND;
223 /* skip magic and CRC value */
239 if (id == RB_ID_TERMINATOR)
257 fprintf(stderr, "no tag found with id=%u\n", tag_id);
263 rbcfg_get_u32(struct rbcfg_ctx *ctx, uint16_t id, uint32_t *val)
269 err = rbcfg_find_tag(ctx, id, &tag_len, &tag_data);
273 *val = get_u32(tag_data);
278 rbcfg_set_u32(struct rbcfg_ctx *ctx, uint16_t id, uint32_t val)
284 err = rbcfg_find_tag(ctx, id, &tag_len, &tag_data);
288 put_u32(tag_data, val);
292 char *rbcfg_find_mtd(const char *name, int *erase_size)
301 f = fopen("/proc/mtd", "r");
307 p = fgets(dev, sizeof(dev), f);
311 if (!strstr(dev, name))
314 err = sscanf(dev, "mtd%d: %08x", &mtd_num, erase_size);
318 sprintf(dev, "/dev/mtdblock%d", mtd_num);
323 if ((s.st_mode & S_IFBLK) == 0)
326 ret = malloc(strlen(dev) + 1);
330 strncpy(ret, dev, strlen(dev) + 1);
339 rbcfg_check_tmp(struct rbcfg_ctx *ctx)
344 err = stat(ctx->tmp_file, &s);
348 if ((s.st_mode & S_IFREG) == 0)
351 if (s.st_size != ctx->buflen)
358 rbcfg_load(struct rbcfg_ctx *ctx)
361 uint32_t crc_orig, crc;
367 tmp = rbcfg_check_tmp(ctx);
368 name = (tmp) ? ctx->tmp_file : ctx->mtd_device;
370 fd = open(name, O_RDONLY);
372 fprintf(stderr, "unable to open %s\n", name);
377 err = read(fd, ctx->buf, ctx->buflen);
378 if (err != ctx->buflen) {
379 fprintf(stderr, "unable to read from %s\n", name);
384 magic = get_u32(ctx->buf);
385 if (magic != RB_MAGIC_SOFT) {
386 fprintf(stderr, "invalid configuration\n");
387 err = RB_ERR_INVALID;
391 crc_orig = get_u32(ctx->buf + 4);
392 put_u32(ctx->buf + 4, 0);
393 crc = cyg_ether_crc32((unsigned char *) ctx->buf, ctx->buflen);
394 if (crc != crc_orig) {
395 fprintf(stderr, "configuration has CRC error\n");
396 err = RB_ERR_INVALID;
412 struct rbcfg_ctx *ctx;
416 mtd_device = rbcfg_find_mtd(RBCFG_MTD_NAME, &buflen);
418 fprintf(stderr, "unable to find configuration\n");
419 return RB_ERR_NOTFOUND;
422 ctx = malloc(sizeof(struct rbcfg_ctx) + buflen);
428 ctx->mtd_device = mtd_device;
429 ctx->tmp_file = RBCFG_TMP_FILE;
430 ctx->buflen = buflen;
431 ctx->buf = (char *) &ctx[1];
433 err = rbcfg_load(ctx);
448 rbcfg_update(int tmp)
450 struct rbcfg_ctx *ctx = rbcfg_ctx;
456 put_u32(ctx->buf, RB_MAGIC_SOFT);
457 put_u32(ctx->buf + 4, 0);
458 crc = cyg_ether_crc32((unsigned char *) ctx->buf, ctx->buflen);
459 put_u32(ctx->buf + 4, crc);
461 name = (tmp) ? ctx->tmp_file : ctx->mtd_device;
462 fd = open(name, O_WRONLY | O_CREAT);
464 fprintf(stderr, "unable to open %s for writing\n", name);
469 err = write(fd, ctx->buf, ctx->buflen);
470 if (err != ctx->buflen) {
487 struct rbcfg_ctx *ctx;
490 free(ctx->mtd_device);
494 static const struct rbcfg_value *
495 rbcfg_env_find(const struct rbcfg_env *env, const char *name)
499 for (i = 0; i < env->num_values; i++) {
500 const struct rbcfg_value *v = &env->values[i];
502 if (strcmp(v->name, name) == 0)
509 static const struct rbcfg_value *
510 rbcfg_env_find_u32(const struct rbcfg_env *env, uint32_t val)
514 for (i = 0; i < env->num_values; i++) {
515 const struct rbcfg_value *v = &env->values[i];
517 if (v->val.u32 == val)
525 rbcfg_env_get_u32(const struct rbcfg_env *env)
527 const struct rbcfg_value *v;
531 err = rbcfg_get_u32(rbcfg_ctx, env->id, &val);
535 v = rbcfg_env_find_u32(env, val);
537 fprintf(stderr, "unknown value %08x found for %s\n",
546 rbcfg_env_set_u32(const struct rbcfg_env *env, const char *data)
548 const struct rbcfg_value *v;
551 v = rbcfg_env_find(env, data);
553 fprintf(stderr, "invalid value '%s'\n", data);
554 return RB_ERR_INVALID;
557 err = rbcfg_set_u32(rbcfg_ctx, env->id, v->val.u32);
562 rbcfg_env_get(const struct rbcfg_env *env)
564 const char *ret = NULL;
567 case RBCFG_ENV_TYPE_U32:
568 ret = rbcfg_env_get_u32(env);
576 rbcfg_env_set(const struct rbcfg_env *env, const char *data)
581 case RBCFG_ENV_TYPE_U32:
582 ret = rbcfg_env_set_u32(env, data);
590 rbcfg_cmd_apply(int argc, const char *argv[])
592 return rbcfg_update(0);
596 rbcfg_cmd_help(int argc, const char *argv[])
603 rbcfg_cmd_get(int argc, const char *argv[])
605 int err = RB_ERR_NOTFOUND;
610 return RB_ERR_INVALID;
613 for (i = 0; i < ARRAY_SIZE(rbcfg_envs); i++) {
614 const struct rbcfg_env *env = &rbcfg_envs[i];
617 if (strcmp(env->name, argv[0]))
620 value = rbcfg_env_get(env);
622 fprintf(stdout, "%s\n", value);
632 rbcfg_cmd_set(int argc, const char *argv[])
634 int err = RB_ERR_INVALID;
638 /* not enough parameters */
640 return RB_ERR_INVALID;
643 for (i = 0; i < ARRAY_SIZE(rbcfg_envs); i++) {
644 const struct rbcfg_env *env = &rbcfg_envs[i];
646 if (strcmp(env->name, argv[0]))
649 err = rbcfg_env_set(env, argv[1]);
651 err = rbcfg_update(1);
659 rbcfg_cmd_show(int argc, const char *argv[])
665 return RB_ERR_INVALID;
668 for (i = 0; i < ARRAY_SIZE(rbcfg_envs); i++) {
669 const struct rbcfg_env *env = &rbcfg_envs[i];
672 value = rbcfg_env_get(env);
674 fprintf(stdout, "%s=%s\n", env->name, value);
680 static const struct rbcfg_command rbcfg_commands[] = {
684 "\t- write configuration to the mtd device",
685 .flags = CMD_FLAG_USES_CFG,
686 .exec = rbcfg_cmd_apply,
690 "\t- show this screen",
691 .exec = rbcfg_cmd_help,
694 .usage = "get <name>\n"
695 "\t- get value of the configuration option <name>",
696 .flags = CMD_FLAG_USES_CFG,
697 .exec = rbcfg_cmd_get,
700 .usage = "set <name> <value>\n"
701 "\t- set value of the configuration option <name> to <value>",
702 .flags = CMD_FLAG_USES_CFG,
703 .exec = rbcfg_cmd_set,
707 "\t- show value of all configuration options",
708 .flags = CMD_FLAG_USES_CFG,
709 .exec = rbcfg_cmd_show,
720 fprintf(stderr, "Usage: %s <command>\n", rbcfg_name);
722 fprintf(stderr, "\nCommands:\n");
723 for (i = 0; i < ARRAY_SIZE(rbcfg_commands); i++) {
724 const struct rbcfg_command *cmd;
725 cmd = &rbcfg_commands[i];
727 len = snprintf(buf, sizeof(buf), "%s", cmd->usage);
729 fprintf(stderr, "%s\n", buf);
732 fprintf(stderr, "\nConfiguration options:\n");
733 for (i = 0; i < ARRAY_SIZE(rbcfg_envs); i++) {
734 const struct rbcfg_env *env;
737 env = &rbcfg_envs[i];
738 fprintf(stderr, "\n%s:\n", env->name);
739 for (j = 0; j < env->num_values; j++) {
740 const struct rbcfg_value *v = &env->values[j];
741 fprintf(stderr, "\t%-12s %s\n", v->name, v->desc);
744 fprintf(stderr, "\n");
747 int main(int argc, const char *argv[])
749 const struct rbcfg_command *cmd = NULL;
753 rbcfg_name = (char *) argv[0];
760 for (i = 0; i < ARRAY_SIZE(rbcfg_commands); i++) {
761 if (strcmp(rbcfg_commands[i].command, argv[1]) == 0) {
762 cmd = &rbcfg_commands[i];
768 fprintf(stderr, "unknown command '%s'\n", argv[1]);
776 if (cmd->flags & CMD_FLAG_USES_CFG) {
782 ret = cmd->exec(argc, argv);
784 if (cmd->flags & CMD_FLAG_USES_CFG)