2 * (C) Copyright 2014 Leo C. <erbl259-lmu@yahoo.de>
4 * SPDX-License-Identifier: GPL-2.0
11 #include <avr/eeprom.h>
17 #include "getopt-min.h"
22 #define ENV_SIZE (CONFIG_ENV_SIZE - sizeof(uint16_t) -1)
24 #define OBSOLETE_FLAG 0
26 #define ENVLIST_DELETE (1<<0)
35 const FLASH
char default_env
[] = {
36 ENV_BAUDRATE
"=" "115200" DELIM
37 ENV_BOOTDELAY
"=" "3" DELIM
38 ENV_BOOTCMD
"=" "pin ${pins}; loadcpm3; go ${startaddress}" DELIM
39 ENV_CPM3_SYSFILE
"=" CONFIG_CPM3_SYSFILE DELIM
40 ENV_PINALIAS
"=" "0:PG5,1:PG4,2:PB4,3:PB5,4:PB6,5:PB7,"
41 "6:PG3,7:PG2,8:PG1,9:PG0,10:PE7" DELIM
42 ENV_STARTADDRESS
"=" "0" DELIM
43 "pins" "=" "2,8 low 9 high 3 2" DELIM
49 typedef struct environment_s
{
50 uint16_t crc
; /* CRC16 over data bytes */
51 uint8_t flags
; /* active/obsolete flags */
52 char data
[ENV_SIZE
]; /* Environment data */
57 typedef struct env_item_s
{
62 static uint8_t env_valid
;
63 static env_item_t env_list
[CONFIG_ENVVAR_MAX
];
64 static int entrycount
;
68 char env_get_char(uint16_t index
)
70 unsigned int off
= CONFIG_ENV_OFFSET
;
75 off
+= CONFIG_ENV_SIZE
;
77 ret
= (char) eeprom_read_byte((const uint8_t *)off
+ index
+
78 offsetof(env_t
, data
));
82 ret
= default_env
[index
];
89 static const FLASH
char *comp_key
;
92 int comp_env_items(const void *m1
, const void *m2
)
94 env_item_t
*ep1
= (env_item_t
*) m1
;
95 env_item_t
*ep2
= (env_item_t
*) m2
;
98 return - strcmp_P(ep2
->envvar
, comp_key
);
100 return strcmp(ep1
->envvar
, ep2
->envvar
);
104 env_item_t
*envlist_search(const MEMX
char *name
)
107 if (__builtin_avr_flash_segment(name
) != -1) {
109 return bsearch(0, env_list
, entrycount
,
110 sizeof(env_item_t
), comp_env_items
);
114 e
.envvar
= (char *) name
;
116 return bsearch(&e
, env_list
, entrycount
,
117 sizeof(env_item_t
), comp_env_items
);
121 e
.envvar
= (char *) name
;
123 return bsearch(&e
, env_list
, entrycount
,
124 sizeof(env_item_t
), comp_env_items
);
130 env_item_t
*envlist_enter(env_item_t
*e
)
132 const size_t size
= sizeof(env_item_t
);
135 ep
= bsearch(e
, env_list
, entrycount
,
136 size
, comp_env_items
);
139 if (entrycount
< CONFIG_ENVVAR_MAX
) {
141 env_list
[entrycount
++] = *e
;
142 qsort(env_list
, entrycount
, size
, comp_env_items
);
143 ep
= bsearch(e
, env_list
, entrycount
,
144 size
, comp_env_items
);
148 ep
->envvar
= e
->envvar
;
156 int env_item_delete(env_item_t
*ep
)
164 size_t size
= sizeof(env_item_t
);
165 memmove(ep
, ep
+ 1, (env_list
- ep
)*size
+ entrycount
*size
);
171 int envlist_delete(const MEMX
char *name
)
173 env_item_t
*ep
= envlist_search(name
);
176 return env_item_delete(ep
);
184 int envlist_import(uint8_t flags
)
193 if ((flags
& ENVLIST_DELETE
) != 0)
194 while (entrycount
!= 0)
195 env_item_delete(&env_list
[entrycount
-1]);
197 for (index
= 0; env_get_char(index
) != '\0'; ) {
198 for (len
= 0; env_get_char(index
+ len
) != '\0'; ++len
) {
199 if ((index
+ len
) >= ENV_SIZE
)
203 np
= (char *) xmalloc(len
+1);
205 printf_P(PSTR("## Can't malloc %d bytes\n"), len
+1);
210 while ((c
= env_get_char(index
++)) != '\0') {
211 if (!ef
&& (c
== '=')) {
228 uint16_t env_crc(uint16_t data_offset
)
236 for (i
= 0; !(c
== 0 && c0
== 0) && i
< ENV_SIZE
; i
++)
239 c
= eeprom_read_byte((const uint8_t *) data_offset
+ i
);
250 int env_check_valid(void)
252 const uint16_t offset
[2] = {CONFIG_ENV_OFFSET
,
253 CONFIG_ENV_OFFSET
+ CONFIG_ENV_SIZE
};
254 uint_fast8_t flags
[2], crc_ok
[2];
258 flags
[0] = eeprom_read_byte ((uint8_t *) offset
[0] +
259 offsetof(env_t
, flags
));
260 flags
[1] = eeprom_read_byte ((uint8_t *) offset
[1] +
261 offsetof(env_t
, flags
));
265 eeprom_read_word((uint16_t *) offset
[0] +
266 offsetof(env_t
, crc
))
267 == env_crc(offset
[0] + offsetof(env_t
, data
))
270 eeprom_read_word((uint16_t *) offset
[1] +
271 offsetof(env_t
, crc
))
272 == env_crc(offset
[1] + offsetof(env_t
, data
))
275 if (!crc_ok
[0] && !crc_ok
[1]) {
278 } else if (crc_ok
[0] && !crc_ok
[1]) {
280 } else if (!crc_ok
[0] && crc_ok
[1]) {
283 /* both ok - check serial */
285 if (flags
[1] == ACTIVE_FLAG
&& flags
[0] != ACTIVE_FLAG
)
287 else if (flags
[1] == OBSOLETE_FLAG
&& flags
[0] == 0xFF)
292 if (flags
[0] == ACTIVE_FLAG
&& flags
[1] == OBSOLETE_FLAG
)
294 else if (flags
[0] == OBSOLETE_FLAG
&& flags
[1] == ACTIVE_FLAG
)
296 else if (flags
[0] == 0xFF && flags
[1] == 0)
298 else if (flags
[1] == 0xFF && flags
[0] == 0)
300 else /* flags are equal - almost impossible */
311 env_valid
= env_check_valid();
312 if (env_valid
== 0) {
313 printf_P(PSTR("*** Warning - bad CRC, "
314 "using default environment\n\n"));
317 envlist_import(ENVLIST_DELETE
);
322 char *getenv_str(const MEMX
char *name
)
327 ep
= envlist_search(name
);
329 ret
= ep
->envvar
+ strlen(ep
->envvar
) +1;
334 int env_item_save(env_item_t
*ep
, uint16_t offset
, int space_left
)
337 char *var
= ep
->envvar
;
342 vlen
= strlen(var
+ nlen
+ 1);
345 if (nlen
+ vlen
+ 2 > space_left
)
348 eeprom_update_block(var
, (uint8_t *) offset
, nlen
);
350 eeprom_update_byte((uint8_t *) offset
++, '=');
351 eeprom_update_block(var
+ nlen
+1, (uint8_t *) offset
, vlen
+1);
353 return nlen
+ vlen
+ 2;
360 unsigned int off
= CONFIG_ENV_OFFSET
+ CONFIG_ENV_SIZE
;
361 unsigned int off_red
= CONFIG_ENV_OFFSET
;
367 if (env_valid
== 2) {
368 off
= CONFIG_ENV_OFFSET
;
369 off_red
= CONFIG_ENV_OFFSET
+ CONFIG_ENV_SIZE
;
372 eeprom_update_byte((uint8_t *) off
+ offsetof(env_t
, flags
), 0xff);
374 pos
= off
+ offsetof(env_t
, data
);
376 for (int i
= 0 ; i
< entrycount
; i
++) {
377 len
= env_item_save(&env_list
[i
], pos
, left
);
385 eeprom_update_byte((uint8_t *) pos
, 0);
386 crc
= env_crc(off
+ offsetof(env_t
, data
));
387 eeprom_update_word((uint16_t *) off
+ offsetof(env_t
, crc
), crc
);
388 eeprom_update_byte((uint8_t *) off
+ offsetof(env_t
, flags
),
392 eeprom_update_byte((uint8_t *) off_red
+ offsetof(env_t
, flags
),
394 env_valid
= (env_valid
== 2) ? 1 : 2;
402 int env_item_print(env_item_t
*ep
, bool mode
)
405 char *ev
= ep
->envvar
;
408 len
= printf_P(PSTR("setenv %s "), ev
) - 7;
409 len
+= printf_P(PSTR("'%s'\n"), ev
+ len
) - 2;
411 len
= printf_P(PSTR("%s="), ev
);
412 len
+= printf_P(PSTR("%s\n"), ev
+ len
);
419 * Command interface: print one or all environment variables
421 * Returns -1 in case of error, or length of printed string
424 int env_print(const MEMX
char *name
, bool mode
)
428 if (name
) { /* print a single name */
430 env_item_t
*ep
= envlist_search(name
);
432 len
= env_item_print(ep
, mode
);
434 } else { /* print whole list */
436 for (int i
= 0 ; i
< entrycount
; i
++) {
437 len
+= env_item_print(&env_list
[i
], mode
);
445 * Set or delete environment variable
447 * Set a new environment variable,
448 * or replace or delete an existing one.
450 * @param flag (not used)
452 * @param argv[1] Environment variable to set or delete
454 * @param argv[2] ... Value to set it to
456 * @return 0 if ok, 1 on error
459 command_ret_t
_do_env_set(int flag
, int argc
, char * const argv
[])
462 char *name
, *value
, *valp
, *p
;
470 if (strchr(name
, '=')) {
471 printf_P(PSTR("## Error: illegal character '='"
472 "in variable name \"%s\"\n"), name
);
473 return CMD_RET_FAILURE
;
476 if (len
> CONFIG_SYS_ENV_NAMELEN
) {
477 printf_P(PSTR("## Error: Variable name \"%s\" too long. "
478 "(max %d characters)\n"), name
, CONFIG_SYS_ENV_NAMELEN
);
479 return CMD_RET_FAILURE
;
485 if (argc
< 3 || argv
[2] == NULL
) {
486 int rc
= envlist_delete(name
);
487 return rc
? CMD_RET_FAILURE
: CMD_RET_SUCCESS
;
491 * Insert / replace new value
493 for (i
= 2, len
+= 1; i
< argc
; ++i
)
494 len
+= strlen(argv
[i
]) + 1;
496 value
= xmalloc(len
);
498 printf_P(PSTR("## Can't malloc %d bytes\n"), len
);
499 return CMD_RET_FAILURE
;
502 valp
= value
+ strlen(name
) + 1;
503 for (i
= 2, p
= valp
; i
< argc
; ++i
) {
506 while ((*p
++ = *v
++) != '\0')
514 ep
= envlist_enter(&e
);
516 printf_P(PSTR("## Error inserting \"%s\" variable.\n"),
518 return CMD_RET_FAILURE
;
521 return CMD_RET_SUCCESS
;
525 * Set an environment variable
527 * @param varname Environment variable to set
528 * @param varvalue Value to set it to
529 * @return 0 if ok, 1 on error
532 int setenv(const MEMX
char *varname
, const char *varvalue
)
537 char *tmpname
= NULL
;
538 if (__builtin_avr_flash_segment(varname
) != -1) {
539 tmpname
= malloc(strlen_P(varname
)+1);
540 if (tmpname
== NULL
) {
541 printf_P(PSTR("setenv: Out of Memory!\n"));
544 strcpy_P(tmpname
, varname
);
546 tmpname
= (char *) varname
;
549 const char * const argv
[3] = { NULL
, tmpname
, varvalue
};
552 if (varvalue
== NULL
|| varvalue
[0] == '\0')
555 rc
= (int) _do_env_set(0, argc
, (char * const *)argv
);
564 * Set an environment variable to an integer value
566 * @param name Environment variable to set
567 * @param value Value to set it to
569 * @return 0 if ok, 1 on error
572 int setenv_intval(const MEMX
char *name
, unsigned long value
, int radix
)
576 ultoa(value
, buf
, radix
);
578 return setenv(name
, buf
);
582 * Set an environment variable to a decimal integer value
584 * @param name Environment variable to set
585 * @param value Value to set it to
586 * @return 0 if ok, 1 on error
588 int setenv_ulong(const MEMX
char *name
, unsigned long value
)
590 return setenv_intval(name
, value
, 10);
595 * Set an environment variable to a value in hex
597 * @param name Environment variable to set
598 * @param value Value to set it to
599 * @return 0 if ok, 1 on error
601 int setenv_hex(const MEMX
char *name
, unsigned long value
)
603 return setenv_intval(name
, value
, 16);
608 * Decode the integer value of an environment variable and return it.
610 * @param name Name of environemnt variable
611 * @param base Number base to use (normally 10, or 16 for hex)
612 * @param default_val Default value to return if the variable is not
614 * @return the decoded value, or default_val if not found
616 unsigned long getenv_ulong(const MEMX
char *name
, int base
, unsigned long default_val
)
621 env_item_t
*ep
= envlist_search(name
);
624 vp
= ep
->envvar
+ strlen(ep
->envvar
) +1;
625 value
= strtoul(vp
, &endp
, base
);
635 * Read an environment variable as a boolean
637 bool getenv_yesno(const MEMX
char *name
)
639 char *s
= getenv_str(name
);
644 return strchr_P(PSTR("1yYtT"), *s
) != NULL
;
647 return *s == '1' || *s == 'y' || *s == 'Y' || *s == 't' || *s == 'T' ?
652 command_ret_t
do_env_print(cmd_tbl_t
*cmdtp
, int flag
, int argc
, char * const argv
[])
654 (void) cmdtp
; (void) flag
;
657 command_ret_t rc
= CMD_RET_SUCCESS
;
663 while ((opt
= getopt(argc
, argv
, PSTR("s"))) != -1) {
669 return CMD_RET_USAGE
;
673 if (optind
== argc
) {
674 /* print all env vars */
675 int size
= env_print(NULL
, mode
);
677 return CMD_RET_FAILURE
;
679 printf_P(PSTR("\nEnvironment size: %d/%d bytes\n"),
681 return CMD_RET_SUCCESS
;
684 /* print selected env vars */
685 while (optind
< argc
) {
686 int rc
= env_print(argv
[optind
], mode
);
688 printf_P(PSTR("## Error: \"%s\" not defined\n"), argv
[optind
]);
689 rc
= CMD_RET_FAILURE
;
698 command_ret_t
do_env_set(cmd_tbl_t
*cmdtp
, int flag
, int argc
, char * const argv
[])
703 return CMD_RET_USAGE
;
705 return _do_env_set(flag
, argc
, argv
);
709 command_ret_t
do_env_default(cmd_tbl_t
*cmdtp
, int flag
,
710 int argc
, char * const argv
[])
712 (void) cmdtp
; (void) flag
; (void) argc
; (void) argv
;
714 uint8_t tmp
= env_valid
;
717 /* Reset the whole environment */
718 printf_P(PSTR("## Resetting to default environment\n"));
719 envlist_import(ENVLIST_DELETE
);
723 return CMD_RET_SUCCESS
;
727 command_ret_t
do_env_save(cmd_tbl_t
*cmdtp
, int flag
, int argc
, char * const argv
[])
729 (void) cmdtp
; (void) flag
; (void) argc
; (void) argv
;
731 printf_P(PSTR("Saving Environment ...\n"));
732 return saveenv() ? CMD_RET_FAILURE
: CMD_RET_SUCCESS
;
736 #if defined(CONFIG_AUTO_COMPLETE)
737 int env_complete(char *var
, int maxv
, char *cmdv
[], int bufsz
, char *buf
)
746 while ((idx
= hmatch_r(var
, idx
, &match
, &env_htab
))) {
747 int vallen
= strlen(match
->key
) + 1;
749 if (found
>= maxv
- 2 || bufsz
< vallen
)
753 memcpy(buf
, match
->key
, vallen
);
758 qsort(cmdv
, found
, sizeof(cmdv
[0]), strcmp_compar
);
761 cmdv
[found
++] = "...";