2 * (C) Copyright 2014 Leo C. <erbl259-lmu@yahoo.de>
4 * SPDX-License-Identifier: GPL-2.0
8 #include <avr/eeprom.h>
13 #include "getopt-min.h"
15 #define DEBUG_ENV 0 /* set to 1 to debug */
17 #define debug_env(fmt, args...) \
18 debug_cond(DEBUG_ENV, fmt, ##args)
27 const FLASH
char default_env
[] = {
28 ENV_BAUDRATE
"=" stringify(CONFIG_BAUDRATE
) DELIM
29 ENV_BOOTDELAY
"=" stringify(CONFIG_BOOTDELAY
) DELIM
30 ENV_BOOTCMD
"=" "pin ${pins};loadcpm3;go ${startaddress}" DELIM
31 ENV_CPM3_SYSFILE
"=" CONFIG_CPM3_SYSFILE DELIM
32 ENV_PINALIAS
"=" "0:PG5,1:PG4,2:PB4,3:PB5,4:PB6,5:PB7,"
33 "6:PG3,7:PG2,8:PG1,9:PG0,10:PE7" DELIM
34 "pins" "=" "2,8 low 9 high 3 2" DELIM
39 #define ENV_SIZE (CONFIG_ENV_SIZE - sizeof(uint16_t) - sizeof(uint8_t))
41 #define ENVLIST_DELETE (1<<0)
45 typedef struct environment_s
{
46 uint16_t crc
; /* CRC16 over data bytes */
47 uint8_t flags
; /* active/obsolete flags */
49 #define OBSOLETE_FLAG 0
50 char data
[ENV_SIZE
]; /* Environment data */
54 static uint8_t env_valid
;
55 static char env_list
[ENV_SIZE
];
58 char env_get_char(uint_fast16_t index
)
60 unsigned int off
= CONFIG_ENV_OFFSET
;
65 off
+= CONFIG_ENV_SIZE
;
68 ret
= (char) eeprom_read_byte((const uint8_t *)off
+ index
+
69 offsetof(env_t
, data
));
73 ret
= default_env
[index
];
79 static void envlist_clear(void)
81 memset(env_list
, 0, sizeof env_list
);
85 int env_item_delete(char *ep
)
87 char *next
= ep
+ strlen(ep
) + 1;
92 memmove(ep
, next
, end
- next
);
97 static int envcmp(const char *s1
, const char *s2
)
99 const unsigned char *u1
= (const unsigned char *)s1
;
100 const unsigned char *u2
= (const unsigned char *)s2
;
101 unsigned char c1
, c2
;
104 c1
= *u1
++; c2
= *u2
++;
105 if (c1
== '=') c1
= '\0';
106 if (c2
== '=') c2
= '\0';
107 } while (c1
!= 0 && c1
- c2
== 0);
112 static char *envlist_search(const MEMX
char *name
)
114 char key
[CONFIG_SYS_ENV_NAMELEN
+ 1];
115 const MEMX
char *np
= name
;
119 while (keylen
<= CONFIG_SYS_ENV_NAMELEN
&& (c
= *np
++) != '\0')
123 for (char *lp
= env_list
; *lp
!= 0; ++lp
) {
125 if (envcmp(lp
, key
) == 0)
135 static char *env_item_insert_name(char *pos
, const char *name
, int len
)
137 char *dstp
= pos
+ len
+ 1;
142 if (end
+ len
>= env_list
+ ENV_SIZE
)
145 memmove(dstp
, pos
, end
- pos
);
147 memset(pos
+ strlen(name
), '=', dstp
- pos
- strlen(name
));
154 static char *envlist_alloc(const char *name
, int len
)
160 for (lp
= env_list
; *lp
!= 0; ++lp
) {
162 rc
= envcmp(lp
, name
);
166 /* rc < 0 : check next place */
171 /* rc == 0 : entry found, replace */
175 /* rc > 0 : entry not in list, insert */
176 /* *lp == 0 : entry not in list, insert */
178 lp
= env_item_insert_name(lp
, name
, len
);
184 static char *env_item_insert(char *pos
, const char *envstr
)
186 char *dstp
= pos
+ strlen(envstr
) + 1;
191 if (end
> env_list
+ ENV_SIZE
)
194 memmove(dstp
, pos
, end
- pos
);
200 static char *envlist_enter(const char *ep
)
205 for (lp
= env_list
; *lp
!= 0; ++lp
) {
211 /* rc < 0 : check next place */
216 /* rc == 0 : entry found, replace */
220 /* rc > 0 : entry not in list, insert */
221 /* *lp == 0 : entry not in list, insert */
223 lp
= env_item_insert(lp
, ep
);
230 int envlist_delete(const MEMX
char *name
)
232 char *ep
= envlist_search(name
);
235 return env_item_delete(ep
);
241 int envlist_import(uint8_t flags
)
243 if ((flags
& ENVLIST_DELETE
) != 0)
246 for (uint_fast16_t index
= 0; env_get_char(index
) != '\0'; ) {
247 char name
[CONFIG_SYS_ENV_NAMELEN
+1];
248 uint_fast8_t nlen
= 0;
251 for (len
= 0; (ch
= env_get_char(index
+ len
)) != '\0'; ++len
) {
252 if ((index
+ len
) >= ENV_SIZE
)
259 if (len
> CONFIG_SYS_ENV_NAMELEN
)
266 char *ep
= envlist_alloc(name
, len
);
268 printf_P(PSTR("## Error inserting \"%s\" variable.\n"), name
);
274 while ((*p
++ = env_get_char(index
++)) != '\0')
282 uint16_t env_crc(uint16_t data_offset
)
290 for (i
= 0; !(c
== 0 && c0
== 0) && i
< ENV_SIZE
; i
++)
293 c
= eeprom_read_byte((const uint8_t *) data_offset
+ i
);
304 int env_check_valid(void)
306 const uint16_t offset
[2] = {CONFIG_ENV_OFFSET
,
307 CONFIG_ENV_OFFSET
+ CONFIG_ENV_SIZE
};
308 uint_fast8_t flags
[2], crc_ok
[2];
312 flags
[0] = eeprom_read_byte ((uint8_t *) offset
[0] +
313 offsetof(env_t
, flags
));
314 flags
[1] = eeprom_read_byte ((uint8_t *) offset
[1] +
315 offsetof(env_t
, flags
));
319 eeprom_read_word((uint16_t *) offset
[0] +
320 offsetof(env_t
, crc
))
321 == env_crc(offset
[0] + offsetof(env_t
, data
))
324 eeprom_read_word((uint16_t *) offset
[1] +
325 offsetof(env_t
, crc
))
326 == env_crc(offset
[1] + offsetof(env_t
, data
))
329 if (!crc_ok
[0] && !crc_ok
[1]) {
332 } else if (crc_ok
[0] && !crc_ok
[1]) {
334 } else if (!crc_ok
[0] && crc_ok
[1]) {
337 /* both ok - check serial */
339 if (flags
[1] == ACTIVE_FLAG
&& flags
[0] != ACTIVE_FLAG
)
341 else if (flags
[1] == OBSOLETE_FLAG
&& flags
[0] == 0xFF)
346 if (flags
[0] == ACTIVE_FLAG
&& flags
[1] == OBSOLETE_FLAG
)
348 else if (flags
[0] == OBSOLETE_FLAG
&& flags
[1] == ACTIVE_FLAG
)
350 else if (flags
[0] == 0xFF && flags
[1] == 0)
352 else if (flags
[1] == 0xFF && flags
[0] == 0)
354 else /* flags are equal - almost impossible */
365 env_valid
= env_check_valid();
366 if (env_valid
== 0) {
367 printf_P(PSTR("*** Warning - bad CRC, "
368 "using default environment\n\n"));
371 envlist_import(ENVLIST_DELETE
);
376 char *getenv_str(const MEMX
char *name
)
378 char *ep
= envlist_search(name
);
380 return strchr(ep
, '=') + 1;
386 int env_item_save(char *ep
, uint16_t offset
, int space_left
)
394 if (len
> space_left
)
397 eeprom_update_block(ep
, (uint8_t *) offset
, len
);
406 unsigned int off
= CONFIG_ENV_OFFSET
+ CONFIG_ENV_SIZE
;
407 unsigned int off_red
= CONFIG_ENV_OFFSET
;
413 if (env_valid
== 2) {
414 off
= CONFIG_ENV_OFFSET
;
415 off_red
= CONFIG_ENV_OFFSET
+ CONFIG_ENV_SIZE
;
418 eeprom_update_byte((uint8_t *) off
+ offsetof(env_t
, flags
), 0xff);
420 pos
= off
+ offsetof(env_t
, data
);
425 len
= env_item_save(ep
, pos
, left
);
435 eeprom_update_byte((uint8_t *) pos
, 0);
436 crc
= env_crc(off
+ offsetof(env_t
, data
));
437 eeprom_update_word((uint16_t *) off
+ offsetof(env_t
, crc
), crc
);
438 eeprom_update_byte((uint8_t *) off
+ offsetof(env_t
, flags
),
442 eeprom_update_byte((uint8_t *) off_red
+ offsetof(env_t
, flags
),
444 env_valid
= (env_valid
== 2) ? 1 : 2;
452 int env_item_print(char *ep
, bool mode
)
454 int len
= strlen(ep
) + 1;
457 my_puts_P(PSTR("setenv "));
459 while ((ch
= *ep
++) != '=')
461 printf_P(PSTR(" '%s'\n"), ep
);
471 * Command interface: print one or all environment variables
473 * Returns -1 in case of error, or length of printed string
476 int env_print(const char *name
, bool mode
)
481 if (name
!= NULL
) { /* print a single name */
483 ep
= envlist_search(name
);
485 len
= env_item_print(ep
, mode
);
487 } else { /* print whole list */
491 len
+= env_item_print(ep
, mode
);
500 * Set or delete environment variable
502 * Set a new environment variable,
503 * or replace or delete an existing one.
505 * @param flag (not used)
507 * @param argv[1] Environment variable to set or delete
509 * @param argv[2] ... Value to set it to
511 * @return 0 if ok, 1 on error
514 command_ret_t
_do_env_set(uint_fast8_t flag UNUSED
, int argc
, char * const argv
[])
516 char *name
= argv
[1];
518 if (strchr(name
, '=')) {
519 printf_P(PSTR("## Error: illegal character '='"
520 "in variable name \"%s\"\n"), name
);
521 return CMD_RET_FAILURE
;
523 size_t len
= strlen(name
);
524 if (len
> CONFIG_SYS_ENV_NAMELEN
) {
525 printf_P(PSTR("## Error: Variable name \"%s\" too long. "
526 "(max %d characters)\n"), name
, CONFIG_SYS_ENV_NAMELEN
);
527 return CMD_RET_FAILURE
;
532 int rc
= envlist_delete(name
);
533 return rc
? CMD_RET_FAILURE
: CMD_RET_SUCCESS
;
536 /* Insert / replace new value */
538 for (int_fast8_t i
= 2; i
< argc
; ++i
)
539 len
+= strlen(argv
[i
]) + 1;
541 char *pos
= envlist_alloc(name
, len
);
543 printf_P(PSTR("## Error inserting \"%s\" variable.\n"), name
);
544 return CMD_RET_FAILURE
;
547 char *vp
= pos
+ strlen(name
) + 1;
548 for (int_fast8_t i
= 2; i
< argc
; ++i
) {
550 while ((*vp
++ = *ap
++) != '\0');
556 return CMD_RET_SUCCESS
;
560 * Set an environment variable
562 * @param varname Environment variable to set
563 * @param varvalue Value to set it to
564 * @return 0 if ok, 1 on error
567 int setenv(const MEMX
char *varname
, const char *varvalue
)
573 if (__builtin_avr_flash_segment(varname
) != -1) {
574 tmpname
= malloc(strlen_P(varname
)+1);
575 if (tmpname
== NULL
) {
576 printf_P(PSTR("setenv: Out of Memory!\n"));
579 strcpy_P(tmpname
, varname
);
581 tmpname
= (char *) varname
;
583 const char * const argv
[3] = { NULL
, tmpname
, varvalue
};
585 const char * const argv
[3] = { NULL
, varname
, varvalue
};
590 if (varvalue
== NULL
|| varvalue
[0] == '\0')
593 rc
= (int) _do_env_set(0, argc
, (char * const *)argv
);
596 if (__builtin_avr_flash_segment(varname
) != -1)
603 * Set an environment variable to an integer value
605 * @param name Environment variable to set
606 * @param value Value to set it to
608 * @return 0 if ok, 1 on error
611 int setenv_intval(const MEMX
char *name
, unsigned long value
, int radix
)
615 ultoa(value
, buf
, radix
);
617 return setenv(name
, buf
);
621 * Set an environment variable to a decimal integer value
623 * @param name Environment variable to set
624 * @param value Value to set it to
625 * @return 0 if ok, 1 on error
627 int setenv_ulong(const MEMX
char *name
, unsigned long value
)
629 return setenv_intval(name
, value
, 10);
634 * Set an environment variable to a value in hex
636 * @param name Environment variable to set
637 * @param value Value to set it to
638 * @return 0 if ok, 1 on error
640 int setenv_hex(const MEMX
char *name
, unsigned long value
)
642 return setenv_intval(name
, value
, 16);
647 * Decode the integer value of an environment variable and return it.
649 * @param name Name of environemnt variable
650 * @param base Number base to use (normally 10, or 16 for hex)
651 * @param default_val Default value to return if the variable is not
653 * @return the decoded value, or default_val if not found
655 unsigned long getenv_ulong(const MEMX
char *name
, int base
, unsigned long default_val
)
660 vp
= getenv_str(name
);
663 value
= strtoul(vp
, &endp
, base
);
673 * Read an environment variable as a boolean
675 bool getenv_yesno(const MEMX
char *name
)
677 char *s
= getenv_str(name
);
682 return strchr_P(PSTR("1yYtT"), *s
) != NULL
;
685 return *s == '1' || *s == 'y' || *s == 'Y' || *s == 't' || *s == 'T' ?
690 command_ret_t
do_env_print(cmd_tbl_t
*cmdtp UNUSED
, uint_fast8_t flag UNUSED
,
691 int argc
, char * const argv
[])
694 command_ret_t rc
= CMD_RET_SUCCESS
;
697 while ((opt
= getopt(argc
, argv
, PSTR("s"))) != -1) {
703 return CMD_RET_USAGE
;
707 if (optind
== argc
) {
708 /* print all env vars */
709 int size
= env_print(NULL
, mode
);
711 return CMD_RET_FAILURE
;
713 printf_P(PSTR("\nEnvironment size: %d/%d bytes\n"),
715 return CMD_RET_SUCCESS
;
718 /* print selected env vars */
719 while (optind
< argc
) {
720 int len
= env_print(argv
[optind
], mode
);
722 rc
= CMD_RET_FAILURE
;
731 command_ret_t
do_env_set(cmd_tbl_t
*cmdtp UNUSED
, uint_fast8_t flag
, int argc
, char * const argv
[])
734 return CMD_RET_USAGE
;
736 return _do_env_set(flag
, argc
, argv
);
740 command_ret_t
do_env_default(cmd_tbl_t
*cmdtp UNUSED
, uint_fast8_t flag UNUSED
,
741 int argc UNUSED
, char * const argv
[] UNUSED
)
743 uint8_t tmp
= env_valid
;
746 /* Reset the whole environment */
747 printf_P(PSTR("## Resetting to default environment\n"));
748 envlist_import(ENVLIST_DELETE
);
752 return CMD_RET_SUCCESS
;
756 command_ret_t
do_env_save(cmd_tbl_t
*cmdtp UNUSED
, uint_fast8_t flag UNUSED
, int argc UNUSED
, char * const argv
[] UNUSED
)
758 printf_P(PSTR("Saving Environment ...\n"));
759 return saveenv() ? CMD_RET_FAILURE
: CMD_RET_SUCCESS
;
763 #if defined(CONFIG_AUTO_COMPLETE)
764 int env_complete(char *var
, int maxv
, char *cmdv
[], int bufsz
, char *buf
)
773 while ((idx
= hmatch_r(var
, idx
, &match
, &env_htab
))) {
774 int vallen
= strlen(match
->key
) + 1;
776 if (found
>= maxv
- 2 || bufsz
< vallen
)
780 memcpy(buf
, match
->key
, vallen
);
785 qsort(cmdv
, found
, sizeof(cmdv
[0]), strcmp_compar
);
788 cmdv
[found
++] = "...";
795 cmd_tbl_t cmd_tbl_env
[] = {
796 CMD_TBL_ITEM_COMPLETE(
797 printenv
, CONFIG_SYS_MAXARGS
, 1|CTBL_SUBCMDAUTO
, do_env_print
,
798 "print environment variables",
800 " Print value of environment variable(s) 'name'\n"
801 " If no names are given, print values of all environment variables\n"
802 " -s Print in setenv form",
805 CMD_TBL_ITEM_COMPLETE(
806 setenv
, CONFIG_SYS_MAXARGS
, CTBL_SUBCMDAUTO
, do_env_set
,
807 "set environment variables",
809 " - set environment variable 'name' to 'value ...'\n"
811 " - delete environment variable 'name'",
815 saveenv
, 1, CTBL_SUBCMDAUTO
, do_env_save
,
816 "save environment variables to persistent storage",
820 defaultenv
, 1, CTBL_SUBCMDAUTO
, do_env_default
,
821 "set all environment variables to their default values",
825 CMD_TBL_ITEM_COMPLETE(
826 print
, CONFIG_SYS_MAXARGS
, 1, do_env_print
,
827 "print environment variables",
829 " Print value of environment variable(s) 'name'\n"
830 " If no names are given, print values of all environment variables\n"
831 " -s Print in setenv form",
834 CMD_TBL_ITEM_COMPLETE(
835 set
, CONFIG_SYS_MAXARGS
, 0, do_env_set
,
836 "set environment variables",
838 " - set environment variable 'name' to 'value ...'\n"
840 " - delete environment variable 'name'",
844 save
, 1, 0, do_env_save
,
845 "save environment variables to persistent storage",
849 default, 1, 0, do_env_default
,
850 "set all environment variables to their default values",
855 help
, CONFIG_SYS_MAXARGS
, CTBL_RPT
, do_help
,
856 "Print sub command description/usage",
858 " - print brief description of all sub commands\n"
859 "env help command ...\n"
860 " - print detailed usage of sub cmd 'command'"
863 /* This does not use the CMD_TBL_ITEM macro as ? can't be used in symbol names */
864 {FSTR("?"), CONFIG_SYS_MAXARGS
, 1, do_help
,
866 #ifdef CONFIG_SYS_LONGHELP
868 #endif /* CONFIG_SYS_LONGHELP */
870 #ifdef CONFIG_AUTO_COMPLETE
874 /* Mark end of table */
875 CMD_TBL_END(cmd_tbl_env
)
878 command_ret_t
do_env(cmd_tbl_t
*cmdtp UNUSED
, uint_fast8_t flag UNUSED
, int argc UNUSED
, char * const argv
[] UNUSED
)
880 return CMD_RET_USAGE
;