]> cloudbase.mooo.com Git - z180-stamp.git/blob - avr/env.c
$() expansion deleted (only ${}). setenv_ulong(), setenv_hex()
[z180-stamp.git] / avr / env.c
1 #include "common.h"
2 #include <string.h>
3 #include <stdlib.h>
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
15 #define ENV_SIZE (CONFIG_ENV_SIZE - sizeof(uint16_t) -1)
16 #define ACTIVE_FLAG 1
17 #define OBSOLETE_FLAG 0
18
19 #define ENVLIST_DELETE (1<<0)
20
21
22 /*
23 * Default Environment
24 */
25
26 #define DELIM "\0"
27
28 const FLASH char default_env[] = {
29 "bootdelay=" "3" DELIM
30 "bootcmd=" "reset; loadf; go ${startaddr}" DELIM
31 "baudrate=" "115200" DELIM
32 "startaddr=" "0" DELIM
33 DELIM
34 };
35
36 /* EEPROM storage */
37 typedef struct environment_s {
38 uint16_t crc; /* CRC16 over data bytes */
39 uint8_t flags; /* active/obsolete flags */
40 char data[ENV_SIZE]; /* Environment data */
41 } env_t;
42
43
44 /* */
45 typedef struct env_item_s {
46 char * envvar;
47 } env_item_t;
48
49
50 static uint8_t env_valid;
51 static env_item_t env_list[CONFIG_ENVVAR_MAX];
52 static int entrycount;
53
54
55 static
56 char env_get_char(uint16_t index)
57 {
58 unsigned int off = CONFIG_ENV_OFFSET;
59 char ret;
60
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 +
66 offsetof(env_t, data));
67 break;
68
69 default:
70 ret = default_env[index];
71 }
72
73 return ret;
74 }
75
76
77 static
78 int comp_env_items(const void *m1, const void *m2)
79 {
80 env_item_t *ep1 = (env_item_t *) m1;
81 env_item_t *ep2 = (env_item_t *) m2;
82
83 return strcmp(ep1->envvar, ep2->envvar);
84 }
85
86
87 env_item_t *envlist_search(const MEMX char *name)
88 {
89 env_item_t e;
90
91 e.envvar = (char *) name;
92
93 #ifdef __MEMX
94 char buf[CONFIG_SYS_ENV_NAMELEN+1];
95
96 if (__builtin_avr_flash_segment(name) != -1) {
97 char *p = buf;
98 while ((*p++ = *name++) != '\0')
99 ;
100 e.envvar = buf;
101 }
102 #endif /* __MEMX */
103
104 return bsearch(&e, env_list, entrycount,
105 sizeof(env_item_t), comp_env_items);
106 }
107
108
109 static
110 env_item_t *envlist_enter(env_item_t *e)
111 {
112 const size_t size = sizeof(env_item_t);
113 env_item_t *ep;
114
115 ep = bsearch(e, env_list, entrycount,
116 size, comp_env_items);
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);
123 ep = bsearch(e, env_list, entrycount,
124 size, comp_env_items);
125 }
126 } else {
127 free(ep->envvar);
128 ep->envvar = e->envvar;
129 }
130
131 return ep;
132 }
133
134
135 static
136 int env_item_delete(env_item_t *ep)
137 {
138 if (entrycount == 0)
139 return -1;
140
141 free(ep->envvar);
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
150 static
151 int envlist_delete(const char *name)
152 {
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);
159
160 if (ep != NULL)
161 return env_item_delete(ep);
162
163 return 1;
164 }
165
166
167
168 static
169 int envlist_import(uint8_t flags)
170 {
171 uint16_t index;
172
173 int len;
174 env_item_t e;
175 char *np, c;
176 uint_fast8_t ef;
177
178 if ((flags & ENVLIST_DELETE) != 0)
179 while (entrycount != 0)
180 env_item_delete(&env_list[entrycount-1]);
181
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;
186 }
187
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;
192 }
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);
205 }
206
207
208 return 0;
209 }
210
211
212 static
213 uint16_t env_crc(uint16_t data_offset)
214 {
215 uint16_t crc;
216 uint16_t i;
217 char c, c0;
218
219 crc = 0xffff;
220 c = 0xff;
221 for (i = 0; !(c == 0 && c0 == 0) && i < ENV_SIZE; i++)
222 {
223 c0 = c;
224 c = eeprom_read_byte((const uint8_t *) data_offset + i);
225 crc = crc16(crc, c);
226 }
227 return crc ;
228 }
229
230
231 /**
232 * return valid env
233 */
234 static
235 int 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 );
259
260 if (!crc_ok[0] && !crc_ok[1]) {
261 rc = 0;
262
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
288 }
289
290 return rc;
291 }
292
293
294 int env_init(void)
295 {
296 env_valid = env_check_valid();
297 if (env_valid == 0) {
298 printf_P(PSTR("*** Warning - bad CRC, "
299 "using default environment\n\n"));
300 }
301
302 envlist_import(ENVLIST_DELETE);
303 return 0;
304 }
305
306
307 char *getenv(const MEMX char *name)
308 {
309 env_item_t *ep;
310 char *ret = NULL;
311
312 ep = envlist_search(name);
313 if (ep != NULL)
314 ret = ep->envvar + strlen(ep->envvar) +1;
315 return ret;
316 }
317
318 static
319 int env_item_save(env_item_t *ep, uint16_t offset, int space_left)
320 {
321 int nlen, vlen;
322 char *var = ep->envvar;
323
324 nlen = strlen(var);
325 if (nlen == 0)
326 return 0;
327 vlen = strlen(var + nlen + 1);
328 if (vlen == 0)
329 return 0;
330 if (nlen + vlen + 2 > space_left)
331 return 0;
332
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;
339 }
340
341
342 static
343 int saveenv(void)
344 {
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;
351
352 if (env_valid == 2) {
353 off = CONFIG_ENV_OFFSET;
354 off_red = CONFIG_ENV_OFFSET + CONFIG_ENV_SIZE;
355 }
356
357 eeprom_update_byte((uint8_t *) off + offsetof(env_t, flags), 0xff);
358
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);
373 eeprom_update_byte((uint8_t *) off + offsetof(env_t, flags),
374 ACTIVE_FLAG);
375
376 if (rc == 0) {
377 eeprom_update_byte((uint8_t *) off_red + offsetof(env_t, flags),
378 OBSOLETE_FLAG);
379 env_valid = (env_valid == 2) ? 1 : 2;
380 }
381
382 return rc;
383 }
384
385
386 static
387 int env_item_print(env_item_t *ep)
388 {
389 int len;
390 char *ev = ep->envvar;
391
392 len = printf_P(PSTR("%s="), ev);
393 len += printf_P(PSTR("%s\n"), ev + len);
394
395 return len;
396 }
397
398
399 /*
400 * Command interface: print one or all environment variables
401 *
402 * Returns -1 in case of error, or length of printed string
403 */
404 static
405 int env_print(const MEMX char *name)
406 {
407 int len = -1;
408
409 if (name) { /* print a single name */
410
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
424
425 /**
426 * Set or delete environment variable
427 *
428 * Set a new environment variable,
429 * or replace or delete an existing one.
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
438 */
439 static
440 command_ret_t _do_env_set(int flag, int argc, char * const argv[])
441 {
442 int i, len;
443 char *name, *value, *valp, *p;
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);
455 return CMD_RET_FAILURE;
456 }
457 len = strlen(name);
458 if (len > CONFIG_SYS_ENV_NAMELEN) {
459 printf_P(PSTR("## Error: Variable name \"%s\" too long. "
460 "(max %d characters)\n"), name, CONFIG_SYS_ENV_NAMELEN);
461 return CMD_RET_FAILURE;
462 }
463 /*
464 env_id++;
465 */
466 /* Delete only ? */
467 if (argc < 3 || argv[2] == NULL) {
468 int rc = envlist_delete(name);
469 return rc ? CMD_RET_FAILURE : CMD_RET_SUCCESS;
470 }
471
472 /*
473 * Insert / replace new value
474 */
475 for (i = 2, len += 1; i < argc; ++i)
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);
481 return CMD_RET_FAILURE;
482 }
483 strcpy(value, name);
484 valp = value + strlen(name) + 1;
485 for (i = 2, p = valp; i < argc; ++i) {
486 char *v = argv[i];
487
488 while ((*p++ = *v++) != '\0')
489 ;
490 *(p - 1) = ' ';
491 }
492 if (p != valp)
493 *--p = '\0';
494
495 e.envvar = value;
496 ep = envlist_enter(&e);
497 if (!ep) {
498 printf_P(PSTR("## Error inserting \"%s\" variable.\n"),
499 name);
500 return CMD_RET_FAILURE;
501 }
502
503 return CMD_RET_SUCCESS;
504 }
505
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 */
513 static
514 int 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;
521
522 return (int) _do_env_set(0, argc, (char * const *)argv);
523 }
524
525 /**
526 * Set an environment variable to an integer value
527 *
528 * @param name Environment variable to set
529 * @param value Value to set it to
530 * @param radix Base
531 * @return 0 if ok, 1 on error
532 */
533 static
534 int setenv_intval(const MEMX char *name, unsigned long value, int radix)
535 {
536 char buf[11];
537
538 ultoa(value, buf, radix);
539
540 return setenv(name, buf);
541 }
542
543 /**
544 * Set an environment variable to a decimal integer value
545 *
546 * @param name Environment variable to set
547 * @param value Value to set it to
548 * @return 0 if ok, 1 on error
549 */
550 int setenv_ulong(const MEMX char *name, unsigned long value)
551 {
552 return setenv_intval(name, value, 10);
553 }
554
555
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 */
563 int setenv_hex(const MEMX char *name, unsigned long value)
564 {
565 return setenv_intval(name, value, 16);
566 }
567
568
569 /**
570 * Decode the integer value of an environment variable and return it.
571 *
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
577 */
578 unsigned long getenv_ulong(const MEMX char *name, int base, unsigned long default_val)
579 {
580 unsigned long value;
581 char *vp, *endp;
582
583 env_item_t *ep = envlist_search(name);
584
585 if (ep != NULL ) {
586 vp = ep->envvar + strlen(ep->envvar) +1;
587 value = strtoul(vp, &endp, base);
588 if (endp != vp)
589 return value;
590 }
591
592 return default_val;
593 }
594
595
596 command_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
625 command_ret_t do_env_set(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
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
636 command_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
641 uint8_t tmp = env_valid;
642 env_valid = 0;
643
644 /* Reset the whole environment */
645 printf_P(PSTR("## Resetting to default environment\n"));
646 envlist_import(ENVLIST_DELETE);
647
648 env_valid = tmp;
649
650 return CMD_RET_SUCCESS;
651 }
652
653
654 command_ret_t do_env_save(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
655 {
656 (void) cmdtp; (void) flag; (void) argc; (void) argv;
657
658 printf_P(PSTR("Saving Environment ...\n"));
659 return saveenv() ? CMD_RET_FAILURE : CMD_RET_SUCCESS;
660 }
661
662
663 #if defined(CONFIG_AUTO_COMPLETE)
664 int 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