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