]> cloudbase.mooo.com Git - z180-stamp.git/blame - avr/env.c
process_macros: reduce heap usage and fragmentation
[z180-stamp.git] / avr / env.c
CommitLineData
d684c216
L
1#include "common.h"
2#include <string.h>
3#include <stdlib.h>
d684c216
L
4#include <avr/eeprom.h>
5
6#include "config.h"
7#include "debug.h"
8#include "xmalloc.h"
9#include "crc.h"
10#include "command.h"
11
12#include "env.h"
13
14
323398b1
L
15#define ENV_SIZE (CONFIG_ENV_SIZE - sizeof(uint16_t) -1)
16#define ACTIVE_FLAG 1
17#define OBSOLETE_FLAG 0
d684c216 18
7e24905c
L
19#define ENVLIST_DELETE (1<<0)
20
d684c216 21
534e1dfc
L
22/*
23 * Default Environment
24 */
1da3acc4
L
25
26#define DELIM "\0"
27
534e1dfc
L
28const FLASH char default_env[] = {
29 "bootdelay=" "3" DELIM
f76ca346 30 "bootcmd=" "reset; loadf; go ${startaddr}" DELIM
534e1dfc
L
31 "baudrate=" "115200" DELIM
32 "startaddr=" "0" DELIM
33 DELIM
34};
35
1da3acc4 36/* EEPROM storage */
d684c216
L
37typedef struct environment_s {
38 uint16_t crc; /* CRC16 over data bytes */
323398b1 39 uint8_t flags; /* active/obsolete flags */
d684c216
L
40 char data[ENV_SIZE]; /* Environment data */
41} env_t;
42
43
1da3acc4 44/* */
d684c216 45typedef struct env_item_s {
55027f3b 46 char * envvar;
d684c216
L
47} env_item_t;
48
1da3acc4 49
323398b1 50static uint8_t env_valid;
d684c216
L
51static env_item_t env_list[CONFIG_ENVVAR_MAX];
52static int entrycount;
53
54
1da3acc4
L
55static
56char env_get_char(uint16_t index)
d684c216
L
57{
58 unsigned int off = CONFIG_ENV_OFFSET;
55027f3b 59 char ret;
d684c216 60
55027f3b
L
61 switch (env_valid) {
62 case 2:
63 off += CONFIG_ENV_SIZE;
64 case 1:
65 ret = (char) eeprom_read_byte((const uint8_t *)off + index +
d684c216 66 offsetof(env_t, data));
55027f3b 67 break;
d684c216 68
55027f3b
L
69 default:
70 ret = default_env[index];
d684c216 71 }
d684c216 72
55027f3b 73 return ret;
d684c216
L
74}
75
d684c216
L
76
77static
78int comp_env_items(const void *m1, const void *m2)
79{
d684c216
L
80 env_item_t *ep1 = (env_item_t *) m1;
81 env_item_t *ep2 = (env_item_t *) m2;
7e24905c 82
55027f3b 83 return strcmp(ep1->envvar, ep2->envvar);
d684c216
L
84}
85
1da3acc4 86
7e24905c
L
87env_item_t *envlist_search(const MEMX char *name)
88{
55027f3b
L
89 env_item_t e;
90
91 e.envvar = (char *) name;
7e24905c
L
92
93#ifdef __MEMX
94 char buf[CONFIG_SYS_ENV_NAMELEN+1];
95
96 if (__builtin_avr_flash_segment(name) != -1) {
55027f3b 97 char *p = buf;
7e24905c
L
98 while ((*p++ = *name++) != '\0')
99 ;
55027f3b 100 e.envvar = buf;
7e24905c
L
101 }
102#endif /* __MEMX */
103
55027f3b
L
104 return bsearch(&e, env_list, entrycount,
105 sizeof(env_item_t), comp_env_items);
7e24905c
L
106}
107
108
109static
110env_item_t *envlist_enter(env_item_t *e)
111{
7e24905c
L
112 const size_t size = sizeof(env_item_t);
113 env_item_t *ep;
114
55027f3b
L
115 ep = bsearch(e, env_list, entrycount,
116 size, comp_env_items);
7e24905c
L
117
118 if (ep == NULL) {
119 if (entrycount < CONFIG_ENVVAR_MAX) {
120
121 env_list[entrycount++] = *e;
122 qsort(env_list, entrycount, size, comp_env_items);
55027f3b
L
123 ep = bsearch(e, env_list, entrycount,
124 size, comp_env_items);
7e24905c
L
125 }
126 } else {
55027f3b
L
127 free(ep->envvar);
128 ep->envvar = e->envvar;
7e24905c
L
129 }
130
131 return ep;
132}
133
134
135static
136int env_item_delete(env_item_t *ep)
137{
138 if (entrycount == 0)
139 return -1;
55027f3b
L
140
141 free(ep->envvar);
7e24905c
L
142
143 entrycount--;
144 size_t size = sizeof(env_item_t);
145 memmove(ep, ep + 1, (env_list - ep)*size + entrycount*size);
146
147 return 0;
148}
149
150static
151int envlist_delete(const char *name)
152{
55027f3b
L
153 env_item_t e;
154
155 e.envvar = (char *) name;
156
157 env_item_t *ep = bsearch(&e, env_list, entrycount,
158 sizeof(env_item_t), comp_env_items);
7e24905c
L
159
160 if (ep != NULL)
161 return env_item_delete(ep);
162
163 return 1;
164}
165
166
7e24905c
L
167
168static
169int envlist_import(uint8_t flags)
d684c216 170{
55027f3b
L
171 uint16_t index;
172
173 int len;
1da3acc4 174 env_item_t e;
55027f3b
L
175 char *np, c;
176 uint_fast8_t ef;
7e24905c
L
177
178 if ((flags & ENVLIST_DELETE) != 0)
179 while (entrycount != 0)
180 env_item_delete(&env_list[entrycount-1]);
181
55027f3b
L
182 for (index = 0; env_get_char(index) != '\0'; ) {
183 for (len = 0; env_get_char(index + len) != '\0'; ++len) {
184 if ((index + len) >= ENV_SIZE)
185 return -1;
1da3acc4 186 }
d684c216 187
55027f3b
L
188 np = (char *) xmalloc(len+1);
189 if (np == NULL) {
190 printf_P(PSTR("## Can't malloc %d bytes\n"), len+1);
191 return 1;
d684c216 192 }
55027f3b
L
193 e.envvar = np;
194 ef = 0;
195 while ((c = env_get_char(index++)) != '\0') {
196 if (!ef && (c == '=')) {
197 *np = '\0';
198 ef = 1;
199 } else
200 *np = c;
201 np++;
202 }
203 *np = '\0';
204 envlist_enter(&e);
d684c216 205 }
d684c216 206
55027f3b 207
1da3acc4
L
208 return 0;
209}
d684c216
L
210
211
1da3acc4
L
212static
213uint16_t env_crc(uint16_t data_offset)
323398b1 214{
7e24905c 215 uint16_t crc;
1da3acc4
L
216 uint16_t i;
217 char c, c0;
323398b1 218
1da3acc4
L
219 crc = 0xffff;
220 c = 0xff;
221 for (i = 0; !(c == 0 && c0 == 0) && i < ENV_SIZE; i++)
222 {
7e24905c 223 c0 = c;
1da3acc4
L
224 c = eeprom_read_byte((const uint8_t *) data_offset + i);
225 crc = crc16(crc, c);
323398b1 226 }
1da3acc4
L
227 return crc ;
228}
323398b1
L
229
230
1da3acc4
L
231/**
232 * return valid env
323398b1 233 */
1da3acc4
L
234static
235int env_check_valid(void)
236{
237 const uint16_t offset[2] = {CONFIG_ENV_OFFSET,
238 CONFIG_ENV_OFFSET + CONFIG_ENV_SIZE};
239 uint_fast8_t flags[2], crc_ok[2];
240 int rc;
241
242 /* read FLAGS */
243 flags[0] = eeprom_read_byte ((uint8_t *) offset[0] +
244 offsetof(env_t, flags));
245 flags[1] = eeprom_read_byte ((uint8_t *) offset[1] +
246 offsetof(env_t, flags));
247
248 /* check CRC */
249 crc_ok[0] = (
250 eeprom_read_word((uint16_t *) offset[0] +
251 offsetof(env_t, crc))
252 == env_crc(offset[0] + offsetof(env_t, data))
253 );
254 crc_ok[1] = (
255 eeprom_read_word((uint16_t *) offset[1] +
256 offsetof(env_t, crc))
257 == env_crc(offset[1] + offsetof(env_t, data))
258 );
323398b1 259
1da3acc4
L
260 if (!crc_ok[0] && !crc_ok[1]) {
261 rc = 0;
323398b1 262
1da3acc4
L
263 } else if (crc_ok[0] && !crc_ok[1]) {
264 rc = 1;
265 } else if (!crc_ok[0] && crc_ok[1]) {
266 rc = 2;
267 } else {
268 /* both ok - check serial */
269#if 1
270 if (flags[1] == ACTIVE_FLAG && flags[0] != ACTIVE_FLAG)
271 rc = 2;
272 else if (flags[1] == OBSOLETE_FLAG && flags[0] == 0xFF)
273 rc = 2;
274 else
275 rc = 1;
276#else
277 if (flags[0] == ACTIVE_FLAG && flags[1] == OBSOLETE_FLAG)
278 rc = 1;
279 else if (flags[0] == OBSOLETE_FLAG && flags[1] == ACTIVE_FLAG)
280 rc = 2;
281 else if (flags[0] == 0xFF && flags[1] == 0)
282 rc = 2;
283 else if (flags[1] == 0xFF && flags[0] == 0)
284 rc = 1;
285 else /* flags are equal - almost impossible */
286 rc = 1;
287#endif
323398b1 288 }
7e24905c 289
1da3acc4
L
290 return rc;
291}
323398b1
L
292
293
1da3acc4 294int env_init(void)
323398b1 295{
1da3acc4
L
296 env_valid = env_check_valid();
297 if (env_valid == 0) {
55027f3b 298 printf_P(PSTR("*** Warning - bad CRC, "
1da3acc4 299 "using default environment\n\n"));
d684c216 300 }
55027f3b 301
7e24905c
L
302 envlist_import(ENVLIST_DELETE);
303 return 0;
323398b1 304}
d684c216 305
1da3acc4 306
fc30b18e 307char *getenv(const MEMX char *name)
d684c216
L
308{
309 env_item_t *ep;
310 char *ret = NULL;
311
55027f3b 312 ep = envlist_search(name);
d684c216 313 if (ep != NULL)
55027f3b 314 ret = ep->envvar + strlen(ep->envvar) +1;
d684c216
L
315 return ret;
316}
317
1da3acc4
L
318static
319int env_item_save(env_item_t *ep, uint16_t offset, int space_left)
323398b1 320{
55027f3b
L
321 int nlen, vlen;
322 char *var = ep->envvar;
323398b1 323
55027f3b
L
324 nlen = strlen(var);
325 if (nlen == 0)
1da3acc4 326 return 0;
55027f3b
L
327 vlen = strlen(var + nlen + 1);
328 if (vlen == 0)
329 return 0;
330 if (nlen + vlen + 2 > space_left)
1da3acc4 331 return 0;
7e24905c 332
55027f3b
L
333 eeprom_update_block(var, (uint8_t *) offset, nlen);
334 offset += nlen;
335 eeprom_update_byte((uint8_t *) offset++, '=');
336 eeprom_update_block(var + nlen +1, (uint8_t *) offset, vlen+1);
337
338 return nlen + vlen + 2;
7e24905c 339}
1da3acc4 340
323398b1 341
1da3acc4 342static
d684c216
L
343int saveenv(void)
344{
323398b1
L
345 unsigned int off = CONFIG_ENV_OFFSET + CONFIG_ENV_SIZE;
346 unsigned int off_red = CONFIG_ENV_OFFSET;
347 unsigned int pos;
348 int len, left;
349 uint16_t crc;
350 int rc = 0;
d684c216 351
323398b1
L
352 if (env_valid == 2) {
353 off = CONFIG_ENV_OFFSET;
354 off_red = CONFIG_ENV_OFFSET + CONFIG_ENV_SIZE;
355 }
356
7e24905c 357 eeprom_update_byte((uint8_t *) off + offsetof(env_t, flags), 0xff);
d684c216 358
323398b1
L
359 pos = off + offsetof(env_t, data);
360 left = ENV_SIZE - 1;
361 for (int i = 0 ; i < entrycount; i++) {
362 len = env_item_save(&env_list[i], pos, left);
363 if (len == 0) {
364 return 1;
365 }
366 pos += len;
367 left -= len;
368 }
369 /* terminate list */
370 eeprom_update_byte((uint8_t *) pos, 0);
371 crc = env_crc(off + offsetof(env_t, data));
372 eeprom_update_word((uint16_t *) off + offsetof(env_t, crc), crc);
7e24905c 373 eeprom_update_byte((uint8_t *) off + offsetof(env_t, flags),
323398b1
L
374 ACTIVE_FLAG);
375
376 if (rc == 0) {
7e24905c 377 eeprom_update_byte((uint8_t *) off_red + offsetof(env_t, flags),
323398b1
L
378 OBSOLETE_FLAG);
379 env_valid = (env_valid == 2) ? 1 : 2;
323398b1 380 }
7e24905c 381
d684c216
L
382 return rc;
383}
384
d684c216 385
1da3acc4
L
386static
387int env_item_print(env_item_t *ep)
d684c216 388{
1da3acc4 389 int len;
55027f3b
L
390 char *ev = ep->envvar;
391
392 len = printf_P(PSTR("%s="), ev);
393 len += printf_P(PSTR("%s\n"), ev + len);
1da3acc4
L
394
395 return len;
7e24905c 396}
323398b1
L
397
398
d684c216
L
399/*
400 * Command interface: print one or all environment variables
401 *
402 * Returns -1 in case of error, or length of printed string
403 */
7e24905c 404static
fc30b18e 405int env_print(const MEMX char *name)
d684c216
L
406{
407 int len = -1;
408
409 if (name) { /* print a single name */
7e24905c 410
d684c216
L
411 env_item_t *ep = envlist_search(name);
412 if (ep != NULL)
413 len = env_item_print(ep);
414
415 } else { /* print whole list */
416 len = 0;
417 for (int i = 0 ; i < entrycount; i++) {
418 len += env_item_print(&env_list[i]);
419 }
420 }
421 return len;
422}
423
1da3acc4 424
72f58822
L
425/**
426 * Set or delete environment variable
427 *
d684c216
L
428 * Set a new environment variable,
429 * or replace or delete an existing one.
72f58822
L
430 *
431 * @param flag (not used)
432 * @param argc
433 * @param argv[1] Environment variable to set or delete
434 * if no more args
435 * @param argv[2] ... Value to set it to
436 *
437 * @return 0 if ok, 1 on error
d684c216 438 */
1da3acc4 439static
d0581f88 440command_ret_t _do_env_set(int flag, int argc, char * const argv[])
d684c216 441{
72f58822 442 int i, len;
55027f3b 443 char *name, *value, *valp, *p;
d684c216
L
444 env_item_t e, *ep;
445
446 (void) flag;
447 debug("Initial value for argc=%d\n", argc);
448
449 name = argv[1];
450 value = argv[2];
451
452 if (strchr(name, '=')) {
453 printf_P(PSTR("## Error: illegal character '='"
454 "in variable name \"%s\"\n"), name);
d0581f88 455 return CMD_RET_FAILURE;
d684c216 456 }
55027f3b
L
457 len = strlen(name);
458 if (len > CONFIG_SYS_ENV_NAMELEN) {
d684c216
L
459 printf_P(PSTR("## Error: Variable name \"%s\" too long. "
460 "(max %d characters)\n"), name, CONFIG_SYS_ENV_NAMELEN);
d0581f88 461 return CMD_RET_FAILURE;
d684c216
L
462 }
463/*
464 env_id++;
465*/
466 /* Delete only ? */
467 if (argc < 3 || argv[2] == NULL) {
468 int rc = envlist_delete(name);
d0581f88 469 return rc ? CMD_RET_FAILURE : CMD_RET_SUCCESS;
d684c216
L
470 }
471
472 /*
473 * Insert / replace new value
474 */
55027f3b 475 for (i = 2, len += 1; i < argc; ++i)
d684c216
L
476 len += strlen(argv[i]) + 1;
477
478 value = xmalloc(len);
479 if (value == NULL) {
480 printf_P(PSTR("## Can't malloc %d bytes\n"), len);
d0581f88 481 return CMD_RET_FAILURE;
d684c216 482 }
55027f3b
L
483 strcpy(value, name);
484 valp = value + strlen(name) + 1;
485 for (i = 2, p = valp; i < argc; ++i) {
d684c216
L
486 char *v = argv[i];
487
55027f3b 488 while ((*p++ = *v++) != '\0')
d684c216 489 ;
55027f3b 490 *(p - 1) = ' ';
d684c216 491 }
55027f3b
L
492 if (p != valp)
493 *--p = '\0';
d684c216 494
55027f3b 495 e.envvar = value;
d684c216
L
496 ep = envlist_enter(&e);
497 if (!ep) {
498 printf_P(PSTR("## Error inserting \"%s\" variable.\n"),
499 name);
d0581f88 500 return CMD_RET_FAILURE;
d684c216
L
501 }
502
d0581f88 503 return CMD_RET_SUCCESS;
d684c216
L
504}
505
72f58822
L
506/**
507 * Set an environment variable
508 *
509 * @param varname Environment variable to set
510 * @param varvalue Value to set it to
511 * @return 0 if ok, 1 on error
512 */
1da3acc4 513static
d684c216
L
514int setenv(const char *varname, const char *varvalue)
515{
516 const char * const argv[3] = { NULL, varname, varvalue };
517 int argc = 3;
518
519 if (varvalue == NULL || varvalue[0] == '\0')
520 --argc;
7e24905c 521
d0581f88 522 return (int) _do_env_set(0, argc, (char * const *)argv);
d684c216
L
523}
524
d684c216
L
525/**
526 * Set an environment variable to an integer value
527 *
f76ca346
L
528 * @param name Environment variable to set
529 * @param value Value to set it to
530 * @param radix Base
d684c216
L
531 * @return 0 if ok, 1 on error
532 */
f76ca346
L
533static
534int setenv_intval(const MEMX char *name, unsigned long value, int radix)
d684c216 535{
f76ca346 536 char buf[11];
d684c216 537
f76ca346 538 ultoa(value, buf, radix);
d684c216 539
f76ca346
L
540 return setenv(name, buf);
541}
d684c216
L
542
543/**
f76ca346 544 * Set an environment variable to a decimal integer value
d684c216 545 *
f76ca346
L
546 * @param name Environment variable to set
547 * @param value Value to set it to
d684c216
L
548 * @return 0 if ok, 1 on error
549 */
f76ca346 550int setenv_ulong(const MEMX char *name, unsigned long value)
d684c216 551{
f76ca346
L
552 return setenv_intval(name, value, 10);
553}
554
d684c216 555
f76ca346
L
556/**
557 * Set an environment variable to a value in hex
558 *
559 * @param name Environment variable to set
560 * @param value Value to set it to
561 * @return 0 if ok, 1 on error
562 */
563int setenv_hex(const MEMX char *name, unsigned long value)
564{
565 return setenv_intval(name, value, 16);
d684c216
L
566}
567
35e9ec0c 568
72f58822 569/**
35e9ec0c 570 * Decode the integer value of an environment variable and return it.
72f58822 571 *
35e9ec0c
L
572 * @param name Name of environemnt variable
573 * @param base Number base to use (normally 10, or 16 for hex)
574 * @param default_val Default value to return if the variable is not
575 * found
576 * @return the decoded value, or default_val if not found
72f58822 577 */
fc30b18e 578unsigned long getenv_ulong(const MEMX char *name, int base, unsigned long default_val)
d684c216 579{
d684c216 580 unsigned long value;
35e9ec0c
L
581 char *vp, *endp;
582
583 env_item_t *ep = envlist_search(name);
d684c216 584
35e9ec0c 585 if (ep != NULL ) {
55027f3b
L
586 vp = ep->envvar + strlen(ep->envvar) +1;
587 value = strtoul(vp, &endp, base);
588 if (endp != vp)
589 return value;
35e9ec0c 590 }
d684c216 591
35e9ec0c 592 return default_val;
d684c216
L
593}
594
35e9ec0c 595
f76ca346
L
596command_ret_t do_env_print(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
597{
598 command_ret_t rc = CMD_RET_SUCCESS;
599
600 (void) cmdtp; (void) flag;
601
602 if (argc == 1) {
603 /* print all env vars */
604 int size = env_print(NULL);
605 if (size < 0)
606 return CMD_RET_FAILURE;
607 printf_P(PSTR("\nEnvironment size: %d/%d bytes\n"),
608 size, ENV_SIZE);
609 return CMD_RET_SUCCESS;
610 }
611
612 /* print selected env vars */
613 for (int i = 1; i < argc; ++i) {
614 int rc = env_print(argv[i]);
615 if (rc < 0) {
616 printf_P(PSTR("## Error: \"%s\" not defined\n"), argv[i]);
617 rc = CMD_RET_FAILURE;
618 }
619 }
620
621 return rc;
622}
623
624
d0581f88 625command_ret_t do_env_set(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
d684c216
L
626{
627 (void) cmdtp;
628
629 if (argc < 2)
630 return CMD_RET_USAGE;
631
632 return _do_env_set(flag, argc, argv);
633}
634
635
7e24905c
L
636command_ret_t do_env_default(cmd_tbl_t *cmdtp, int flag,
637 int argc, char * const argv[])
638{
639 (void) cmdtp; (void) flag; (void) argc; (void) argv;
640
55027f3b
L
641 uint8_t tmp = env_valid;
642 env_valid = 0;
643
7e24905c 644 /* Reset the whole environment */
55027f3b 645 printf_P(PSTR("## Resetting to default environment\n"));
7e24905c
L
646 envlist_import(ENVLIST_DELETE);
647
55027f3b
L
648 env_valid = tmp;
649
7e24905c
L
650 return CMD_RET_SUCCESS;
651}
652
55027f3b 653
d0581f88 654command_ret_t do_env_save(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
323398b1
L
655{
656 (void) cmdtp; (void) flag; (void) argc; (void) argv;
657
658 printf_P(PSTR("Saving Environment ...\n"));
d0581f88 659 return saveenv() ? CMD_RET_FAILURE : CMD_RET_SUCCESS;
323398b1
L
660}
661
55027f3b 662
7e24905c 663#if defined(CONFIG_AUTO_COMPLETE)
d684c216
L
664int env_complete(char *var, int maxv, char *cmdv[], int bufsz, char *buf)
665{
666 ENTRY *match;
667 int found, idx;
668
669 idx = 0;
670 found = 0;
671 cmdv[0] = NULL;
672
673 while ((idx = hmatch_r(var, idx, &match, &env_htab))) {
674 int vallen = strlen(match->key) + 1;
675
676 if (found >= maxv - 2 || bufsz < vallen)
677 break;
678
679 cmdv[found++] = buf;
680 memcpy(buf, match->key, vallen);
681 buf += vallen;
682 bufsz -= vallen;
683 }
684
685 qsort(cmdv, found, sizeof(cmdv[0]), strcmp_compar);
686
687 if (idx)
688 cmdv[found++] = "...";
689
690 cmdv[found] = NULL;
691 return found;
692}
693#endif