]> cloudbase.mooo.com Git - z180-stamp.git/blob - avr/env.c
Merge tag 'fatfs-0.10c'
[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 const FLASH char *comp_key;
78
79 static
80 int comp_env_items(const void *m1, const void *m2)
81 {
82 env_item_t *ep1 = (env_item_t *) m1;
83 env_item_t *ep2 = (env_item_t *) m2;
84
85 if (ep1 == NULL)
86 return - strcmp_P(ep2->envvar, comp_key);
87 else
88 return strcmp(ep1->envvar, ep2->envvar);
89 }
90
91
92 env_item_t *envlist_search(const MEMX char *name)
93 {
94 #ifdef __MEMX
95 if (__builtin_avr_flash_segment(name) != -1) {
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);
106 }
107 #else
108 env_item_t e;
109 e.envvar = (char *) name;
110
111 return bsearch(&e, env_list, entrycount,
112 sizeof(env_item_t), comp_env_items);
113 #endif /* __MEMX */
114 }
115
116
117 static
118 env_item_t *envlist_enter(env_item_t *e)
119 {
120 const size_t size = sizeof(env_item_t);
121 env_item_t *ep;
122
123 ep = bsearch(e, env_list, entrycount,
124 size, comp_env_items);
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);
131 ep = bsearch(e, env_list, entrycount,
132 size, comp_env_items);
133 }
134 } else {
135 free(ep->envvar);
136 ep->envvar = e->envvar;
137 }
138
139 return ep;
140 }
141
142
143 static
144 int env_item_delete(env_item_t *ep)
145 {
146 if (entrycount == 0)
147 return -1;
148
149 free(ep->envvar);
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
158 static
159 int envlist_delete(const MEMX char *name)
160 {
161 env_item_t *ep = envlist_search(name);
162
163 if (ep != NULL)
164 return env_item_delete(ep);
165
166 return 1;
167 }
168
169
170
171 static
172 int envlist_import(uint8_t flags)
173 {
174 uint16_t index;
175
176 int len;
177 env_item_t e;
178 char *np, c;
179 uint_fast8_t ef;
180
181 if ((flags & ENVLIST_DELETE) != 0)
182 while (entrycount != 0)
183 env_item_delete(&env_list[entrycount-1]);
184
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;
189 }
190
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;
195 }
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);
208 }
209
210
211 return 0;
212 }
213
214
215 static
216 uint16_t env_crc(uint16_t data_offset)
217 {
218 uint16_t crc;
219 uint16_t i;
220 char c, c0;
221
222 crc = 0xffff;
223 c = 0xff;
224 for (i = 0; !(c == 0 && c0 == 0) && i < ENV_SIZE; i++)
225 {
226 c0 = c;
227 c = eeprom_read_byte((const uint8_t *) data_offset + i);
228 crc = crc16(crc, c);
229 }
230 return crc ;
231 }
232
233
234 /**
235 * return valid env
236 */
237 static
238 int 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 );
262
263 if (!crc_ok[0] && !crc_ok[1]) {
264 rc = 0;
265
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
291 }
292
293 return rc;
294 }
295
296
297 int env_init(void)
298 {
299 env_valid = env_check_valid();
300 if (env_valid == 0) {
301 printf_P(PSTR("*** Warning - bad CRC, "
302 "using default environment\n\n"));
303 }
304
305 envlist_import(ENVLIST_DELETE);
306 return 0;
307 }
308
309
310 char *getenv(const MEMX char *name)
311 {
312 env_item_t *ep;
313 char *ret = NULL;
314
315 ep = envlist_search(name);
316 if (ep != NULL)
317 ret = ep->envvar + strlen(ep->envvar) +1;
318 return ret;
319 }
320
321 static
322 int env_item_save(env_item_t *ep, uint16_t offset, int space_left)
323 {
324 int nlen, vlen;
325 char *var = ep->envvar;
326
327 nlen = strlen(var);
328 if (nlen == 0)
329 return 0;
330 vlen = strlen(var + nlen + 1);
331 if (vlen == 0)
332 return 0;
333 if (nlen + vlen + 2 > space_left)
334 return 0;
335
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;
342 }
343
344
345 static
346 int saveenv(void)
347 {
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;
354
355 if (env_valid == 2) {
356 off = CONFIG_ENV_OFFSET;
357 off_red = CONFIG_ENV_OFFSET + CONFIG_ENV_SIZE;
358 }
359
360 eeprom_update_byte((uint8_t *) off + offsetof(env_t, flags), 0xff);
361
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);
376 eeprom_update_byte((uint8_t *) off + offsetof(env_t, flags),
377 ACTIVE_FLAG);
378
379 if (rc == 0) {
380 eeprom_update_byte((uint8_t *) off_red + offsetof(env_t, flags),
381 OBSOLETE_FLAG);
382 env_valid = (env_valid == 2) ? 1 : 2;
383 }
384
385 return rc;
386 }
387
388
389 static
390 int env_item_print(env_item_t *ep)
391 {
392 int len;
393 char *ev = ep->envvar;
394
395 len = printf_P(PSTR("%s="), ev);
396 len += printf_P(PSTR("%s\n"), ev + len);
397
398 return len;
399 }
400
401
402 /*
403 * Command interface: print one or all environment variables
404 *
405 * Returns -1 in case of error, or length of printed string
406 */
407 static
408 int env_print(const MEMX char *name)
409 {
410 int len = -1;
411
412 if (name) { /* print a single name */
413
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
427
428 /**
429 * Set or delete environment variable
430 *
431 * Set a new environment variable,
432 * or replace or delete an existing one.
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
441 */
442 static
443 command_ret_t _do_env_set(int flag, int argc, char * const argv[])
444 {
445 int i, len;
446 char *name, *value, *valp, *p;
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);
458 return CMD_RET_FAILURE;
459 }
460 len = strlen(name);
461 if (len > CONFIG_SYS_ENV_NAMELEN) {
462 printf_P(PSTR("## Error: Variable name \"%s\" too long. "
463 "(max %d characters)\n"), name, CONFIG_SYS_ENV_NAMELEN);
464 return CMD_RET_FAILURE;
465 }
466 /*
467 env_id++;
468 */
469 /* Delete only ? */
470 if (argc < 3 || argv[2] == NULL) {
471 int rc = envlist_delete(name);
472 return rc ? CMD_RET_FAILURE : CMD_RET_SUCCESS;
473 }
474
475 /*
476 * Insert / replace new value
477 */
478 for (i = 2, len += 1; i < argc; ++i)
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);
484 return CMD_RET_FAILURE;
485 }
486 strcpy(value, name);
487 valp = value + strlen(name) + 1;
488 for (i = 2, p = valp; i < argc; ++i) {
489 char *v = argv[i];
490
491 while ((*p++ = *v++) != '\0')
492 ;
493 *(p - 1) = ' ';
494 }
495 if (p != valp)
496 *--p = '\0';
497
498 e.envvar = value;
499 ep = envlist_enter(&e);
500 if (!ep) {
501 printf_P(PSTR("## Error inserting \"%s\" variable.\n"),
502 name);
503 return CMD_RET_FAILURE;
504 }
505
506 return CMD_RET_SUCCESS;
507 }
508
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 */
516 static
517 int 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;
524
525 return (int) _do_env_set(0, argc, (char * const *)argv);
526 }
527
528 /**
529 * Set an environment variable to an integer value
530 *
531 * @param name Environment variable to set
532 * @param value Value to set it to
533 * @param radix Base
534 * @return 0 if ok, 1 on error
535 */
536 static
537 int setenv_intval(const MEMX char *name, unsigned long value, int radix)
538 {
539 char buf[11];
540
541 ultoa(value, buf, radix);
542
543 return setenv(name, buf);
544 }
545
546 /**
547 * Set an environment variable to a decimal integer value
548 *
549 * @param name Environment variable to set
550 * @param value Value to set it to
551 * @return 0 if ok, 1 on error
552 */
553 int setenv_ulong(const MEMX char *name, unsigned long value)
554 {
555 return setenv_intval(name, value, 10);
556 }
557
558
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 */
566 int setenv_hex(const MEMX char *name, unsigned long value)
567 {
568 return setenv_intval(name, value, 16);
569 }
570
571
572 /**
573 * Decode the integer value of an environment variable and return it.
574 *
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
580 */
581 unsigned long getenv_ulong(const MEMX char *name, int base, unsigned long default_val)
582 {
583 unsigned long value;
584 char *vp, *endp;
585
586 env_item_t *ep = envlist_search(name);
587
588 if (ep != NULL ) {
589 vp = ep->envvar + strlen(ep->envvar) +1;
590 value = strtoul(vp, &endp, base);
591 if (endp != vp)
592 return value;
593 }
594
595 return default_val;
596 }
597
598
599 command_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
628 command_ret_t do_env_set(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
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
639 command_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
644 uint8_t tmp = env_valid;
645 env_valid = 0;
646
647 /* Reset the whole environment */
648 printf_P(PSTR("## Resetting to default environment\n"));
649 envlist_import(ENVLIST_DELETE);
650
651 env_valid = tmp;
652
653 return CMD_RET_SUCCESS;
654 }
655
656
657 command_ret_t do_env_save(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
658 {
659 (void) cmdtp; (void) flag; (void) argc; (void) argv;
660
661 printf_P(PSTR("Saving Environment ...\n"));
662 return saveenv() ? CMD_RET_FAILURE : CMD_RET_SUCCESS;
663 }
664
665
666 #if defined(CONFIG_AUTO_COMPLETE)
667 int 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