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
;
67 ret
= (char) eeprom_read_byte((const uint8_t *)off
+ index
+
68 offsetof(env_t
, data
));
72 ret
= default_env
[index
];
78 static void envlist_clear(void)
80 memset(env_list
, 0, sizeof env_list
);
84 int env_item_delete(char *ep
)
86 char *next
= ep
+ strlen(ep
) + 1;
91 memmove(ep
, next
, end
- next
);
96 static int envcmp(const char *s1
, const char *s2
)
98 const unsigned char *u1
= (const unsigned char *)s1
;
99 const unsigned char *u2
= (const unsigned char *)s2
;
100 unsigned char c1
, c2
;
103 c1
= *u1
++; c2
= *u2
++;
104 if (c1
== '=') c1
= '\0';
105 if (c2
== '=') c2
= '\0';
106 } while (c1
!= 0 && c1
- c2
== 0);
111 static char *envlist_search(const MEMX
char *name
)
113 char key
[CONFIG_SYS_ENV_NAMELEN
+ 1];
114 const MEMX
char *np
= name
;
118 while (keylen
<= CONFIG_SYS_ENV_NAMELEN
&& (c
= *np
++) != '\0')
122 for (char *lp
= env_list
; *lp
!= 0; ++lp
) {
124 if (envcmp(lp
, key
) == 0)
134 static char *env_item_insert_name(char *pos
, const char *name
, int len
)
136 char *dstp
= pos
+ len
+ 1;
141 if (end
+ len
>= env_list
+ ENV_SIZE
)
144 memmove(dstp
, pos
, end
- pos
);
146 memset(pos
+ strlen(name
), '=', dstp
- pos
- strlen(name
));
153 static char *envlist_alloc(const char *name
, int len
)
159 for (lp
= env_list
; *lp
!= 0; ++lp
) {
161 rc
= envcmp(lp
, name
);
165 /* rc < 0 : check next place */
170 /* rc == 0 : entry found, replace */
174 /* rc > 0 : entry not in list, insert */
175 /* *lp == 0 : entry not in list, insert */
177 lp
= env_item_insert_name(lp
, name
, len
);
183 static char *env_item_insert(char *pos
, const char *envstr
)
185 char *dstp
= pos
+ strlen(envstr
) + 1;
190 if (end
> env_list
+ ENV_SIZE
)
193 memmove(dstp
, pos
, end
- pos
);
199 static char *envlist_enter(const char *ep
)
204 for (lp
= env_list
; *lp
!= 0; ++lp
) {
210 /* rc < 0 : check next place */
215 /* rc == 0 : entry found, replace */
219 /* rc > 0 : entry not in list, insert */
220 /* *lp == 0 : entry not in list, insert */
222 lp
= env_item_insert(lp
, ep
);
229 int envlist_delete(const MEMX
char *name
)
231 char *ep
= envlist_search(name
);
234 return env_item_delete(ep
);
240 int envlist_import(uint8_t flags
)
242 if ((flags
& ENVLIST_DELETE
) != 0)
245 for (uint_fast16_t index
= 0; env_get_char(index
) != '\0'; ) {
246 char name
[CONFIG_SYS_ENV_NAMELEN
+1];
247 uint_fast8_t nlen
= 0;
250 for (len
= 0; (ch
= env_get_char(index
+ len
)) != '\0'; ++len
) {
251 if ((index
+ len
) >= ENV_SIZE
)
258 if (len
> CONFIG_SYS_ENV_NAMELEN
)
265 char *ep
= envlist_alloc(name
, len
);
267 printf_P(PSTR("## Error inserting \"%s\" variable.\n"), name
);
273 while ((*p
++ = env_get_char(index
++)) != '\0')
281 uint16_t env_crc(uint16_t data_offset
)
289 for (i
= 0; !(c
== 0 && c0
== 0) && i
< ENV_SIZE
; i
++)
292 c
= eeprom_read_byte((const uint8_t *) data_offset
+ i
);
303 int env_check_valid(void)
305 const uint16_t offset
[2] = {CONFIG_ENV_OFFSET
,
306 CONFIG_ENV_OFFSET
+ CONFIG_ENV_SIZE
};
307 uint_fast8_t flags
[2], crc_ok
[2];
311 flags
[0] = eeprom_read_byte ((uint8_t *) offset
[0] +
312 offsetof(env_t
, flags
));
313 flags
[1] = eeprom_read_byte ((uint8_t *) offset
[1] +
314 offsetof(env_t
, flags
));
318 eeprom_read_word((uint16_t *) offset
[0] +
319 offsetof(env_t
, crc
))
320 == env_crc(offset
[0] + offsetof(env_t
, data
))
323 eeprom_read_word((uint16_t *) offset
[1] +
324 offsetof(env_t
, crc
))
325 == env_crc(offset
[1] + offsetof(env_t
, data
))
328 if (!crc_ok
[0] && !crc_ok
[1]) {
331 } else if (crc_ok
[0] && !crc_ok
[1]) {
333 } else if (!crc_ok
[0] && crc_ok
[1]) {
336 /* both ok - check serial */
338 if (flags
[1] == ACTIVE_FLAG
&& flags
[0] != ACTIVE_FLAG
)
340 else if (flags
[1] == OBSOLETE_FLAG
&& flags
[0] == 0xFF)
345 if (flags
[0] == ACTIVE_FLAG
&& flags
[1] == OBSOLETE_FLAG
)
347 else if (flags
[0] == OBSOLETE_FLAG
&& flags
[1] == ACTIVE_FLAG
)
349 else if (flags
[0] == 0xFF && flags
[1] == 0)
351 else if (flags
[1] == 0xFF && flags
[0] == 0)
353 else /* flags are equal - almost impossible */
364 env_valid
= env_check_valid();
365 if (env_valid
== 0) {
366 printf_P(PSTR("*** Warning - bad CRC, "
367 "using default environment\n\n"));
370 envlist_import(ENVLIST_DELETE
);
375 char *getenv_str(const MEMX
char *name
)
377 char *ep
= envlist_search(name
);
379 return strchr(ep
, '=') + 1;
385 int env_item_save(char *ep
, uint16_t offset
, int space_left
)
393 if (len
> space_left
)
396 eeprom_update_block(ep
, (uint8_t *) offset
, len
);
405 unsigned int off
= CONFIG_ENV_OFFSET
+ CONFIG_ENV_SIZE
;
406 unsigned int off_red
= CONFIG_ENV_OFFSET
;
412 if (env_valid
== 2) {
413 off
= CONFIG_ENV_OFFSET
;
414 off_red
= CONFIG_ENV_OFFSET
+ CONFIG_ENV_SIZE
;
417 eeprom_update_byte((uint8_t *) off
+ offsetof(env_t
, flags
), 0xff);
419 pos
= off
+ offsetof(env_t
, data
);
424 len
= env_item_save(ep
, pos
, left
);
434 eeprom_update_byte((uint8_t *) pos
, 0);
435 crc
= env_crc(off
+ offsetof(env_t
, data
));
436 eeprom_update_word((uint16_t *) off
+ offsetof(env_t
, crc
), crc
);
437 eeprom_update_byte((uint8_t *) off
+ offsetof(env_t
, flags
),
441 eeprom_update_byte((uint8_t *) off_red
+ offsetof(env_t
, flags
),
443 env_valid
= (env_valid
== 2) ? 1 : 2;
451 int env_item_print(char *ep
, bool mode
)
453 int len
= strlen(ep
) + 1;
456 my_puts_P(PSTR("setenv "));
458 while ((ch
= *ep
++) != '=')
460 printf_P(PSTR(" '%s'\n"), ep
);
470 * Command interface: print one or all environment variables
472 * Returns -1 in case of error, or length of printed string
475 int env_print(const char *name
, bool mode
)
480 if (name
!= NULL
) { /* print a single name */
482 ep
= envlist_search(name
);
484 len
= env_item_print(ep
, mode
);
486 } else { /* print whole list */
490 len
+= env_item_print(ep
, mode
);
499 * Set or delete environment variable
501 * Set a new environment variable,
502 * or replace or delete an existing one.
504 * @param flag (not used)
506 * @param argv[1] Environment variable to set or delete
508 * @param argv[2] ... Value to set it to
510 * @return 0 if ok, 1 on error
513 command_ret_t
_do_env_set(uint_fast8_t flag UNUSED
, int argc
, char * const argv
[])
515 char *name
= argv
[1];
517 if (strchr(name
, '=')) {
518 printf_P(PSTR("## Error: illegal character '='"
519 "in variable name \"%s\"\n"), name
);
520 return CMD_RET_FAILURE
;
522 size_t len
= strlen(name
);
523 if (len
> CONFIG_SYS_ENV_NAMELEN
) {
524 printf_P(PSTR("## Error: Variable name \"%s\" too long. "
525 "(max %d characters)\n"), name
, CONFIG_SYS_ENV_NAMELEN
);
526 return CMD_RET_FAILURE
;
531 int rc
= envlist_delete(name
);
532 return rc
? CMD_RET_FAILURE
: CMD_RET_SUCCESS
;
535 /* Insert / replace new value */
537 for (int_fast8_t i
= 2; i
< argc
; ++i
)
538 len
+= strlen(argv
[i
]) + 1;
540 char *pos
= envlist_alloc(name
, len
);
542 printf_P(PSTR("## Error inserting \"%s\" variable.\n"), name
);
543 return CMD_RET_FAILURE
;
546 char *vp
= pos
+ strlen(name
) + 1;
547 for (int_fast8_t i
= 2; i
< argc
; ++i
) {
549 while ((*vp
++ = *ap
++) != '\0');
555 return CMD_RET_SUCCESS
;
559 * Set an environment variable
561 * @param varname Environment variable to set
562 * @param varvalue Value to set it to
563 * @return 0 if ok, 1 on error
566 int setenv(const MEMX
char *varname
, const char *varvalue
)
572 if (__builtin_avr_flash_segment(varname
) != -1) {
573 tmpname
= malloc(strlen_P(varname
)+1);
574 if (tmpname
== NULL
) {
575 printf_P(PSTR("setenv: Out of Memory!\n"));
578 strcpy_P(tmpname
, varname
);
580 tmpname
= (char *) varname
;
582 const char * const argv
[3] = { NULL
, tmpname
, varvalue
};
584 const char * const argv
[3] = { NULL
, varname
, varvalue
};
589 if (varvalue
== NULL
|| varvalue
[0] == '\0')
592 rc
= (int) _do_env_set(0, argc
, (char * const *)argv
);
595 if (__builtin_avr_flash_segment(varname
) != -1)
602 * Set an environment variable to an integer value
604 * @param name Environment variable to set
605 * @param value Value to set it to
607 * @return 0 if ok, 1 on error
610 int setenv_intval(const MEMX
char *name
, unsigned long value
, int radix
)
614 ultoa(value
, buf
, radix
);
616 return setenv(name
, buf
);
620 * Set an environment variable to a decimal integer value
622 * @param name Environment variable to set
623 * @param value Value to set it to
624 * @return 0 if ok, 1 on error
626 int setenv_ulong(const MEMX
char *name
, unsigned long value
)
628 return setenv_intval(name
, value
, 10);
633 * Set an environment variable to a value in hex
635 * @param name Environment variable to set
636 * @param value Value to set it to
637 * @return 0 if ok, 1 on error
639 int setenv_hex(const MEMX
char *name
, unsigned long value
)
641 return setenv_intval(name
, value
, 16);
646 * Decode the integer value of an environment variable and return it.
648 * @param name Name of environemnt variable
649 * @param base Number base to use (normally 10, or 16 for hex)
650 * @param default_val Default value to return if the variable is not
652 * @return the decoded value, or default_val if not found
654 unsigned long getenv_ulong(const MEMX
char *name
, int base
, unsigned long default_val
)
659 vp
= getenv_str(name
);
662 value
= strtoul(vp
, &endp
, base
);
672 * Read an environment variable as a boolean
674 bool getenv_yesno(const MEMX
char *name
)
676 char *s
= getenv_str(name
);
681 return strchr_P(PSTR("1yYtT"), *s
) != NULL
;
684 return *s == '1' || *s == 'y' || *s == 'Y' || *s == 't' || *s == 'T' ?
689 command_ret_t
do_env_print(cmd_tbl_t
*cmdtp UNUSED
, uint_fast8_t flag UNUSED
,
690 int argc
, char * const argv
[])
693 command_ret_t rc
= CMD_RET_SUCCESS
;
696 while ((opt
= getopt(argc
, argv
, PSTR("s"))) != -1) {
702 return CMD_RET_USAGE
;
706 if (optind
== argc
) {
707 /* print all env vars */
708 int size
= env_print(NULL
, mode
);
710 return CMD_RET_FAILURE
;
712 printf_P(PSTR("\nEnvironment size: %d/%d bytes\n"),
714 return CMD_RET_SUCCESS
;
717 /* print selected env vars */
718 while (optind
< argc
) {
719 int rc
= env_print(argv
[optind
], mode
);
721 printf_P(PSTR("## Error: \"%s\" not defined\n"), argv
[optind
]);
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
;