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
), 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
)