From d684c21619905153eff68c43927207248925f6c2 Mon Sep 17 00:00:00 2001 From: Leo C Date: Tue, 12 Aug 2014 12:35:28 +0200 Subject: [PATCH] New U-Boot like AVR main program. Uses U-Boot source code taken from: git://git.denx.de/u-boot.git --- .gitignore | 15 +- avr/Tupfile | 17 +- avr/bootc.c | 175 ++++++++++ avr/cli.c | 369 ++++++++++++++++++++ avr/cli.h | 64 ++++ avr/cli_readline.c | 595 +++++++++++++++++++++++++++++++ avr/cli_readline.h | 50 +++ avr/cmd_echo.c | 58 ++++ avr/cmd_help.c | 32 ++ avr/command.c | 525 ++++++++++++++++++++++++++++ avr/command.h | 157 +++++++++ avr/command_tbl.c | 76 ++++ avr/common.h | 57 +++ avr/con-utils.c | 91 +++++ avr/con-utils.h | 22 ++ avr/config.h | 24 ++ avr/crc.h | 12 + avr/debug.h | 20 ++ avr/env.c | 808 +++++++++++++++++++++++++++++++++++++++++++ avr/env.h | 12 + avr/serial.c | 8 +- avr/serial.h | 3 +- avr/timer.c | 87 +++++ avr/timer.h | 7 + avr/xmalloc.c | 27 ++ avr/xmalloc.h | 8 + avr/z180-stamp-avr.c | 6 +- configs/debug.config | 2 +- configs/gcc.tup | 40 +++ 29 files changed, 3350 insertions(+), 17 deletions(-) create mode 100644 avr/bootc.c create mode 100644 avr/cli.c create mode 100644 avr/cli.h create mode 100644 avr/cli_readline.c create mode 100644 avr/cli_readline.h create mode 100644 avr/cmd_echo.c create mode 100644 avr/cmd_help.c create mode 100644 avr/command.c create mode 100644 avr/command.h create mode 100644 avr/command_tbl.c create mode 100644 avr/common.h create mode 100644 avr/con-utils.c create mode 100644 avr/con-utils.h create mode 100644 avr/config.h create mode 100644 avr/crc.h create mode 100644 avr/env.c create mode 100644 avr/env.h create mode 100644 avr/timer.c create mode 100644 avr/timer.h create mode 100644 avr/xmalloc.c create mode 100644 avr/xmalloc.h create mode 100644 configs/gcc.tup diff --git a/.gitignore b/.gitignore index 1cb02ea..725065f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,11 @@ -/.tup -Makefiledev -/.settings +#Makefiledev /.cproject /.project -/doc/ -/joe/ -/scratch/ +/.settings +/.tup +/build-debug +/doc +/fatfs +/joe +/scratch + diff --git a/avr/Tupfile b/avr/Tupfile index a90b7b5..d90f225 100644 --- a/avr/Tupfile +++ b/avr/Tupfile @@ -1,15 +1,22 @@ include_rules -PROG = z180-stamp-avr -SRC = z180-stamp-avr.c serial.c z80-if.c -SRC_Z = ../z180/hdrom.c +PROG = stamp-bootc +SRC = bootc.c +SRC += cli.c cli_readline.c command.c command_tbl.c +SRC += cmd_help.c cmd_echo.c +SRC += env.c xmalloc.c +SRC += timer.c con-utils.c serial.c +SRC += z80-if.c + +#SRC_Z = ../z180/hdrom.c + #TARGETS = $(PROG).elf MCU_TARGET = atmega1281 F_CPU = 18432000UL DEFS = -DF_CPU=$(F_CPU) -DBAUD=115200UL -INCLUDES += ../z180 +#INCLUDES += ../z180 ############################################################################### @@ -34,7 +41,7 @@ endif CFLAGS = -g -Os CFLAGS += -mmcu=$(MCU_TARGET) CFLAGS += -std=gnu99 -CFLAGS += -Wall -Wextra -Wimplicit-function-declaration +CFLAGS += -Wall -Wextra CFLAGS += -Wredundant-decls #CFLAGS += -fno-common -ffunction-sections -fdata-sections #CFLAGS += -I $(INCLUDES) diff --git a/avr/bootc.c b/avr/bootc.c new file mode 100644 index 0000000..6dbcdef --- /dev/null +++ b/avr/bootc.c @@ -0,0 +1,175 @@ +/* + */ + + +#include "common.h" + +#include +//#include +//#include +#include +//#include +//#include +//#include + +#include + +#include +#include + + +#include "config.h" +#include "debug.h" +#include "z80-if.h" +#include "con-utils.h" +#include "serial.h" +#include "timer.h" +#include "cli.h" +#include "env.h" + +/*--------------------------------------------------------------------------*/ + +void setup_avr(void) +{ + /* WD */ + + /* CPU */ + + /* Disable JTAG Interface regardless of the JTAGEN fuse setting. */ + MCUCR = _BV(JTD); + MCUCR = _BV(JTD); + + /* disable unused periphels */ + PRR0 = _BV(PRTIM2) | _BV(PRTIM0) | _BV(PRADC); + PRR1 = _BV(PRTIM5) | _BV(PRTIM4) | _BV(PRTIM3) | + _BV(PRUSART3) | _BV(PRUSART2) | _BV(PRUSART1); + + /* disable analog comparator */ + ACSR = _BV(ACD); + /* Ports */ + + /* Clock */ + CLKPR = _BV(CLKPCE); + CLKPR = 0; + + /* Timer */ + + OCR1A = F_CPU / 8 / 1000 - 1; // Timer1: 1000Hz interval (OC1A) + TCCR1B = 0b00001010; + TIMSK1 = _BV(OCIE1A); // Enable TC1.oca interrupt +} + + + +/*******************************************************************************/ +/*******************************************************************************/ + +#define udelay(n) _delay_us(n) + + +/* Stored value of bootdelay, used by autoboot_command() */ +static int stored_bootdelay; + + +/*************************************************************************** + * Watch for 'delay' seconds for autoboot stop. + * returns: 0 - no key, allow autoboot + * 1 - got key, abort + */ + +static int abortboot(int bootdelay) +{ + int abort = 0; + uint32_t ts; + + if (bootdelay >= 0) + printf_P(PSTR("Hit any key to stop autoboot: %2d "), bootdelay); + +#if defined CONFIG_ZERO_BOOTDELAY_CHECK + /* + * Check if key already pressed + * Don't check if bootdelay < 0 + */ + if (bootdelay >= 0) { + if (tstc()) { /* we got a key press */ + (void) my_getchar(); /* consume input */ + my_puts_P(PSTR("\b\b\b 0")); + abort = 1; /* don't auto boot */ + } + } +#endif + + while ((bootdelay > 0) && (!abort)) { + --bootdelay; + /* delay 1000 ms */ + ts = get_timer(0); + do { + if (tstc()) { /* we got a key press */ + abort = 1; /* don't auto boot */ + bootdelay = 0; /* no more delay */ + break; + } + udelay(10000); + } while (!abort && get_timer(ts) < 1000); + + printf_P(PSTR("\b\b\b%2d "), bootdelay); + } + + putchar('\n'); + + return abort; +} + +const char *bootdelay_process(void) +{ + char *s; + int bootdelay; + + s = getenv("bootdelay"); + bootdelay = s ? atoi(s) : CONFIG_BOOTDELAY; + + + debug("### main_loop entered: bootdelay=%d\n\n", bootdelay); + _delay_ms(20); + + s = getenv("bootcmd"); + stored_bootdelay = bootdelay; + return s; +} + +void autoboot_command(const char *s) +{ + debug("### main_loop: bootcmd=\"%s\"\n", s ? s : PSTR("")); + _delay_ms(20); + + if (stored_bootdelay != -1 && s && !abortboot(stored_bootdelay)) { + run_command_list(s, -1); + } +} + + +void main_loop(void) +{ + const char *s; + + s = bootdelay_process(); + autoboot_command(s); + cli_loop(); +} + +int main(void) +{ + setup_avr(); + z80_setup_bus(); + + serial_setup(); + sei(); + + if (env_check() == 0) + set_default_env(); + env_init(); + + printf_P(PSTR("\n(ATMEGA1281+HD64180)_stamp Tester\n")); + + main_loop(); +} diff --git a/avr/cli.c b/avr/cli.c new file mode 100644 index 0000000..44cf5b8 --- /dev/null +++ b/avr/cli.c @@ -0,0 +1,369 @@ +#include "common.h" + +#include +#include +#include +#include + +#include "config.h" +#include "command.h" +#include "xmalloc.h" +#include "debug.h" +#include "env.h" +#include "cli_readline.h" +#include "con-utils.h" +#include "cli.h" + +#define DEBUG_PARSER 0 /* set to 1 to debug */ + +#define debug_parser(fmt, args...) \ + debug_cond(DEBUG_PARSER, fmt, ##args) + +static int cli_parse_line(char *line, char *argv[]) +{ + static const FLASH char delim[] = {" \t"}; + + char *ptr; + uint_fast8_t nargs = 0; + + debug_parser("parse_line: \"%s\"\n", line); + + ptr = strtok_P(line, delim); + while(nargs < CONFIG_SYS_MAXARGS && ptr != NULL) { + argv[nargs++] = ptr; + ptr = strtok_P(NULL, delim); + } + + if (ptr != NULL) + printf_P(PSTR("** Too many args (max. %d) **\n"), CONFIG_SYS_MAXARGS); + + argv[nargs] = NULL; + debug_parser("parse_line: nargs=%d\n", nargs); + + return nargs; +} + +static void process_macros(const char *input, char *output) +{ + char c, prev; + const char *varname_start = NULL; +#if 0 && (CONFIG_SYS_CBSIZE < __UINT_FAST8_MAX__) + uint_fast8_t inputcnt = strlen(input); + uint_fast8_t outputcnt = CONFIG_SYS_CBSIZE; +#else + int inputcnt = strlen(input); + int outputcnt = CONFIG_SYS_CBSIZE; +#endif + uint_fast8_t state = 0; /* 0 = waiting for '$' */ + + /* 1 = waiting for '(' or '{' */ + /* 2 = waiting for ')' or '}' */ + /* 3 = waiting for ''' */ + char *output_start = output; + + debug_parser("[PROCESS_MACROS] INPUT len %d: \"%s\"\n", strlen(input), + input); + + prev = '\0'; /* previous character */ + + while (inputcnt && outputcnt) { + c = *input++; + inputcnt--; + + if (state != 3) { + /* remove one level of escape characters */ + if ((c == '\\') && (prev != '\\')) { + if (inputcnt-- == 0) + break; + prev = c; + c = *input++; + } + } + + switch (state) { + case 0: /* Waiting for (unescaped) $ */ + if ((c == '\'') && (prev != '\\')) { + state = 3; + break; + } + if ((c == '$') && (prev != '\\')) { + state++; + } else { + *(output++) = c; + outputcnt--; + } + break; + case 1: /* Waiting for ( */ + if (c == '(' || c == '{') { + state++; + varname_start = input; + } else { + state = 0; + *(output++) = '$'; + outputcnt--; + + if (outputcnt) { + *(output++) = c; + outputcnt--; + } + } + break; + case 2: /* Waiting for ) */ + if (c == ')' || c == '}') { + char envname[CONFIG_SYS_ENV_NAMELEN+1], *envval; + /* Varname # of chars */ + uint_fast8_t envcnt = input - varname_start - 1; + if (envcnt > CONFIG_SYS_ENV_NAMELEN) + envcnt = CONFIG_SYS_ENV_NAMELEN; + + memcpy(envname, varname_start, envcnt); + envname[envcnt] = '\0'; + + /* Get its value */ + envval = getenv(envname); + + /* Copy into the line if it exists */ + if (envval != NULL) + while ((*envval) && outputcnt) { + *(output++) = *(envval++); + outputcnt--; + } + /* Look for another '$' */ + state = 0; + } + break; + case 3: /* Waiting for ' */ + if ((c == '\'') && (prev != '\\')) { + state = 0; + } else { + *(output++) = c; + outputcnt--; + } + break; + } + prev = c; + } + + if (outputcnt) + *output = 0; + else + *(output - 1) = 0; + + debug_parser("[PROCESS_MACROS] OUTPUT len %d: \"%s\"\n", + strlen(output_start), output_start); +} + + /* + * WARNING: + * + * We must create a temporary copy of the command since the command we get + * may be the result from getenv(), which returns a pointer directly to + * the environment data, which may change magicly when the command we run + * creates or modifies environment variables (like "bootp" does). + */ +static int cli_run_command(const char *cmd, int flag) +{ + char *cmdbuf; /* working copy of cmd */ + char *token; /* start of token in cmdbuf */ + char *sep; /* end of token (separator) in cmdbuf */ + char *finaltoken; + char *str; + char *argv[CONFIG_SYS_MAXARGS + 1]; /* NULL terminated */ + int argc; + uint_fast8_t inquotes, repeatable = 1; + int rc = 0; + + debug_parser("[RUN_COMMAND] cmd[%p]=\"%s\"\n", + cmd, cmd ? cmd : "NULL"); + + clear_ctrlc(); /* forget any previous Control C */ + + if (!cmd || !*cmd) + return -1; /* empty command */ + + + cmdbuf = xmalloc(strlen(cmd) + 1); + finaltoken = xmalloc(CONFIG_SYS_CBSIZE); + + strcpy(cmdbuf, cmd); + str = cmdbuf; + + /* Process separators and check for invalid + * repeatable commands + */ + + debug_parser("[PROCESS_SEPARATORS] %s\n", cmd); + while (*str) { + /* + * Find separator, or string end + * Allow simple escape of ';' by writing "\;" + */ + for (inquotes = 0, sep = str; *sep; sep++) { + if ((*sep == '\'') && + (*(sep - 1) != '\\')) + inquotes = !inquotes; + + if (!inquotes && + (*sep == ';' || *sep == '\n') && /* separator */ + (sep != str) && /* past string start */ + (*(sep - 1) != '\\')) /* and NOT escaped */ + break; + } + + /* + * Limit the token to data between separators + */ + token = str; + if (*sep) { + str = sep + 1; /* start of command for next pass */ + *sep = '\0'; + } else { + str = sep; /* no more commands for next pass */ + } + debug_parser("token: \"%s\"\n", token); + + /* find macros in this token and replace them */ + process_macros(token, finaltoken); + + /* Extract arguments */ + argc = cli_parse_line(finaltoken, argv); + if (argc == 0) { + rc = -1; /* no command at all */ + continue; + } + + if (cmd_process(flag, argc, argv, &repeatable)) + rc = -1; + + /* Did the user stop this? */ + if (had_ctrlc()) { + rc = -1; /* if stopped then not repeatable */ + break; + } + } + + free(cmdbuf); + free(finaltoken); + + return rc ? rc : repeatable; +} + +static int cli_run_command_list(const char *cmd) +{ + return (cli_run_command(cmd, 0) < 0); +} + +/******************************************************************************/ + + +/* + * Run a command using the selected parser. + * + * @param cmd Command to run + * @param flag Execution flags (CMD_FLAG_...) + * @return 0 on success, or != 0 on error. + */ +int run_command(const char *cmd, int flag) +{ + /* + * cli_run_command can return 0 or 1 for success, so clean up + * its result. + */ + if (cli_run_command(cmd, flag) == -1) + return 1; + + return 0; +} + +/* + * Run a command using the selected parser, and check if it is repeatable. + * + * @param cmd Command to run + * @param flag Execution flags (CMD_FLAG_...) + * @return 0 (not repeatable) or 1 (repeatable) on success, -1 on error. + */ +static int run_command_repeatable(const char *cmd, int flag) +{ + return cli_run_command(cmd, flag); +} + +int run_command_list(const char *cmd, int len) +{ + (void) len; + + return cli_run_command_list(cmd); +} + +/****************************************************************************/ + +#define DYN_BUFFER 1 + +void cli_loop(void) +{ +#if DYN_BUFFER + char *lastcommand = NULL; +#else + static char lastcommand[CONFIG_SYS_CBSIZE]; +#endif + int len; + int flag; + int rc = 1; + + for (;;) { + len = cli_readline(PSTR(CONFIG_SYS_PROMPT)); + + flag = 0; /* assume no special flags for now */ + if (len > 0) { +#if DYN_BUFFER + lastcommand = (char *) xrealloc(lastcommand, len+1); + if (lastcommand != NULL) { + strncpy(lastcommand, console_buffer, len+1); + lastcommand[len] = '\0'; + } +#else + strcpy(lastcommand, console_buffer); +#endif + } else if (len == 0) + flag |= CMD_FLAG_REPEAT; + + if (len == -1) + my_puts_P(PSTR("\n")); + else + rc = run_command_repeatable(lastcommand, flag); + + if (rc <= 0) { + /* invalid command or not repeatable, forget it */ +#if DYN_BUFFER + free(lastcommand); + lastcommand = NULL; +#else + lastcommand[0] = 0; +#endif + } + } +} + + +int do_run(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + int i; + (void) cmdtp; + + if (argc < 2) + return CMD_RET_USAGE; + + for (i = 1; i < argc; ++i) { + char *arg; + + arg = getenv(argv[i]); + if (arg == NULL) { + printf_P(PSTR("## Error: \"%s\" not defined\n"), argv[i]); + return 1; + } + + if (run_command(arg, flag) != 0) + return 1; + } + return 0; +} + diff --git a/avr/cli.h b/avr/cli.h new file mode 100644 index 0000000..67ff63b --- /dev/null +++ b/avr/cli.h @@ -0,0 +1,64 @@ +#ifndef CLI_H +#define CLI_H + +/** + * Go into the command loop + * + * This will return if we get a timeout waiting for a command, but only for + * the simple parser (not hush). See CONFIG_BOOT_RETRY_TIME. + */ +void cli_loop(void); + +/** + * cli_simple_run_command() - Execute a command with the simple CLI + * + * @cmd: String containing the command to execute + * @flag Flag value - see CMD_FLAG_... + * @return 1 - command executed, repeatable + * 0 - command executed but not repeatable, interrupted commands are + * always considered not repeatable + * -1 - not executed (unrecognized, bootd recursion or too many args) + * (If cmd is NULL or "" or longer than CONFIG_SYS_CBSIZE-1 it is + * considered unrecognized) + */ +//int cli_simple_run_command(const char *cmd, int flag); + +/** + * cli_simple_run_command_list() - Execute a list of command + * + * The commands should be separated by ; or \n and will be executed + * by the built-in parser. + * + * This function cannot take a const char * for the command, since if it + * finds newlines in the string, it replaces them with \0. + * + * @param cmd String containing list of commands + * @param flag Execution flags (CMD_FLAG_...) + * @return 0 on success, or != 0 on error. + */ +//int cli_simple_run_command_list(char *cmd, int flag); + +/** + * parse_line() - split a command line down into separate arguments + * + * The argv[] array is filled with pointers into @line, and each argument + * is terminated by \0 (i.e. @line is changed in the process unless there + * is only one argument). + * + * #argv is terminated by a NULL after the last argument pointer. + * + * At most CONFIG_SYS_MAXARGS arguments are permited - if there are more + * than that then an error is printed, and this function returns + * CONFIG_SYS_MAXARGS, with argv[] set up to that point. + * + * @line: Command line to parse + * @args: Array to hold arguments + * @return number of arguments + */ +//int cli_simple_parse_line(char *line, char *argv[]); + +int run_command_list(const char *cmd, int len); + + +#endif /* CLI_H */ + diff --git a/avr/cli_readline.c b/avr/cli_readline.c new file mode 100644 index 0000000..17d5494 --- /dev/null +++ b/avr/cli_readline.c @@ -0,0 +1,595 @@ +/* + * (C) Copyright 2000 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * Add to readline cmdline-editing by + * (C) Copyright 2005 + * JinHua Luo, GuangDong Linux Center, + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include "common.h" + +#include +#include +#include + +#include "config.h" +#include "con-utils.h" +#include "command.h" +#include "cli_readline.h" + +char console_buffer[CONFIG_SYS_CBSIZE + 1]; /* console I/O buffer */ + +static const FLASH char erase_seq[] = "\b \b"; /* erase sequence */ +static const FLASH char tab_seq[] = " "; /* used to expand TABs */ + +#ifndef CONFIG_CMDLINE_EDITING +static char *delete_char (char *buffer, char *p, int *colp, int *np, int plen) +{ + char *s; + + if (*np == 0) + return p; + + if (*(--p) == '\t') { /* will retype the whole line */ + while (*colp > plen) { + my_puts_P(erase_seq); + (*colp)--; + } + for (s = buffer; s < p; ++s) { + if (*s == '\t') { + my_puts_P(tab_seq + ((*colp) & 07)); + *colp += 8 - ((*colp) & 07); + } else { + ++(*colp); + putchar(*s); + } + } + } else { + my_puts_P(erase_seq); + (*colp)--; + } + (*np)--; + + return p; +} +#endif /* CONFIG_CMDLINE_EDITING */ + + +#ifdef CONFIG_CMDLINE_EDITING + +/* + * cmdline-editing related codes from vivi. + * Author: Janghoon Lyu + */ + +#define putnstr(str, n) printf_P(PSTR("%.*s"), (int)n, str) + +#define CTL_CH(c) ((c) - 'a' + 1) +#define CTL_BACKSPACE ('\b') +#define DEL ((char)255) +#define DEL7 ((char)127) +#define CREAD_HIST_CHAR ('!') + +#define getcmd_putch(ch) putchar(ch) +#define getcmd_getch() my_getchar() +#define getcmd_cbeep() getcmd_putch('\a') + +#define HIST_MAX 5 +#define HIST_SIZE CONFIG_SYS_CBSIZE + +static int hist_max; +static int hist_add_idx; +static int hist_cur = -1; +static unsigned hist_num; + +static char *hist_list[HIST_MAX]; +static char hist_lines[HIST_MAX][HIST_SIZE + 1]; /* Save room for NULL */ + +#define add_idx_minus_one() ((hist_add_idx == 0) ? hist_max : hist_add_idx-1) + +static void hist_init(void) +{ + int i; + + hist_max = 0; + hist_add_idx = 0; + hist_cur = -1; + hist_num = 0; + + for (i = 0; i < HIST_MAX; i++) { + hist_list[i] = hist_lines[i]; + hist_list[i][0] = '\0'; + } +} + +static void cread_add_to_hist(char *line) +{ + strcpy(hist_list[hist_add_idx], line); + + if (++hist_add_idx >= HIST_MAX) + hist_add_idx = 0; + + if (hist_add_idx > hist_max) + hist_max = hist_add_idx; + + hist_num++; +} + +static char *hist_prev(void) +{ + char *ret; + int old_cur; + + if (hist_cur < 0) + return NULL; + + old_cur = hist_cur; + if (--hist_cur < 0) + hist_cur = hist_max; + + if (hist_cur == hist_add_idx) { + hist_cur = old_cur; + ret = NULL; + } else { + ret = hist_list[hist_cur]; + } + + return ret; +} + +static char *hist_next(void) +{ + char *ret; + + if (hist_cur < 0) + return NULL; + + if (hist_cur == hist_add_idx) + return NULL; + + if (++hist_cur > hist_max) + hist_cur = 0; + + if (hist_cur == hist_add_idx) + ret = ""; + else + ret = hist_list[hist_cur]; + + return ret; +} + +#ifndef CONFIG_CMDLINE_EDITING +static void cread_print_hist_list(void) +{ + int i; + unsigned n; + + n = hist_num - hist_max; + + i = hist_add_idx + 1; + while (1) { + if (i > hist_max) + i = 0; + if (i == hist_add_idx) + break; + printf_P(PSTR("%s\n"), hist_list[i]); + n++; + i++; + } +} +#endif /* CONFIG_CMDLINE_EDITING */ + +#define BEGINNING_OF_LINE() { \ + while (num) { \ + getcmd_putch(CTL_BACKSPACE); \ + num--; \ + } \ +} + +#define ERASE_TO_EOL() { \ + if (num < eol_num) { \ + printf_P(PSTR("%*S"), (int)(eol_num - num), PSTR("")); \ + do { \ + getcmd_putch(CTL_BACKSPACE); \ + } while (--eol_num > num); \ + } \ +} + +#define REFRESH_TO_EOL() { \ + if (num < eol_num) { \ + wlen = eol_num - num; \ + putnstr(buf + num, wlen); \ + num = eol_num; \ + } \ +} + +static void cread_add_char(char ichar, int insert, unsigned long *num, + unsigned long *eol_num, char *buf, unsigned long len) +{ + unsigned long wlen; + + /* room ??? */ + if (insert || *num == *eol_num) { + if (*eol_num > len - 1) { + getcmd_cbeep(); + return; + } + (*eol_num)++; + } + + if (insert) { + wlen = *eol_num - *num; + if (wlen > 1) + memmove(&buf[*num+1], &buf[*num], wlen-1); + + buf[*num] = ichar; + putnstr(buf + *num, wlen); + (*num)++; + while (--wlen) + getcmd_putch(CTL_BACKSPACE); + } else { + /* echo the character */ + wlen = 1; + buf[*num] = ichar; + putnstr(buf + *num, wlen); + (*num)++; + } +} + +static void cread_add_str(char *str, int strsize, int insert, + unsigned long *num, unsigned long *eol_num, + char *buf, unsigned long len) +{ + while (strsize--) { + cread_add_char(*str, insert, num, eol_num, buf, len); + str++; + } +} + +static int cread_line(const FLASH char *const prompt, char *buf, unsigned int *len) +{ + unsigned long num = 0; + unsigned long eol_num = 0; + unsigned long wlen; + char ichar; + int insert = 1; + int esc_len = 0; + char esc_save[8]; + int init_len = strlen(buf); + + (void) prompt; + + if (init_len) + cread_add_str(buf, init_len, 1, &num, &eol_num, buf, *len); + + while (1) { + ichar = getcmd_getch(); + + if ((ichar == '\n') || (ichar == '\r')) { + putchar('\n'); + break; + } + + /* + * handle standard linux xterm esc sequences for arrow key, etc. + */ + if (esc_len != 0) { + if (esc_len == 1) { + if (ichar == '[') { + esc_save[esc_len] = ichar; + esc_len = 2; + } else { + cread_add_str(esc_save, esc_len, + insert, &num, &eol_num, + buf, *len); + esc_len = 0; + } + continue; + } + + switch (ichar) { + case 'D': /* <- key */ + ichar = CTL_CH('b'); + esc_len = 0; + break; + case 'C': /* -> key */ + ichar = CTL_CH('f'); + esc_len = 0; + break; /* pass off to ^F handler */ + case 'H': /* Home key */ + ichar = CTL_CH('a'); + esc_len = 0; + break; /* pass off to ^A handler */ + case 'A': /* up arrow */ + ichar = CTL_CH('p'); + esc_len = 0; + break; /* pass off to ^P handler */ + case 'B': /* down arrow */ + ichar = CTL_CH('n'); + esc_len = 0; + break; /* pass off to ^N handler */ + default: + esc_save[esc_len++] = ichar; + cread_add_str(esc_save, esc_len, insert, + &num, &eol_num, buf, *len); + esc_len = 0; + continue; + } + } + + switch (ichar) { + case 0x1b: + if (esc_len == 0) { + esc_save[esc_len] = ichar; + esc_len = 1; + } else { + my_puts_P(PSTR("impossible condition #876\n")); + esc_len = 0; + } + break; + + case CTL_CH('a'): + BEGINNING_OF_LINE(); + break; + case CTL_CH('c'): /* ^C - break */ + *buf = '\0'; /* discard input */ + return -1; + case CTL_CH('f'): + if (num < eol_num) { + getcmd_putch(buf[num]); + num++; + } + break; + case CTL_CH('b'): + if (num) { + getcmd_putch(CTL_BACKSPACE); + num--; + } + break; + case CTL_CH('d'): + if (num < eol_num) { + wlen = eol_num - num - 1; + if (wlen) { + memmove(&buf[num], &buf[num+1], wlen); + putnstr(buf + num, wlen); + } + + getcmd_putch(' '); + do { + getcmd_putch(CTL_BACKSPACE); + } while (wlen--); + eol_num--; + } + break; + case CTL_CH('k'): + ERASE_TO_EOL(); + break; + case CTL_CH('e'): + REFRESH_TO_EOL(); + break; + case CTL_CH('o'): + insert = !insert; + break; + case CTL_CH('x'): + case CTL_CH('u'): + BEGINNING_OF_LINE(); + ERASE_TO_EOL(); + break; + case DEL: + case DEL7: + case 8: + if (num) { + wlen = eol_num - num; + num--; + memmove(&buf[num], &buf[num+1], wlen); + getcmd_putch(CTL_BACKSPACE); + putnstr(buf + num, wlen); + getcmd_putch(' '); + do { + getcmd_putch(CTL_BACKSPACE); + } while (wlen--); + eol_num--; + } + break; + case CTL_CH('p'): + case CTL_CH('n'): + { + char *hline; + + esc_len = 0; + + if (ichar == CTL_CH('p')) + hline = hist_prev(); + else + hline = hist_next(); + + if (!hline) { + getcmd_cbeep(); + continue; + } + + /* nuke the current line */ + /* first, go home */ + BEGINNING_OF_LINE(); + + /* erase to end of line */ + ERASE_TO_EOL(); + + /* copy new line into place and display */ + strcpy(buf, hline); + eol_num = strlen(buf); + REFRESH_TO_EOL(); + continue; + } +#ifdef CONFIG_AUTO_COMPLETE + case '\t': { + int num2, col; + + /* do not autocomplete when in the middle */ + if (num < eol_num) { + getcmd_cbeep(); + break; + } + + buf[num] = '\0'; + col = strlen_P(prompt) + eol_num; + num2 = num; + if (cmd_auto_complete(prompt, buf, &num2, &col)) { + col = num2 - num; + num += col; + eol_num += col; + } + break; + } +#endif + default: + cread_add_char(ichar, insert, &num, &eol_num, buf, + *len); + break; + } + } + *len = eol_num; + buf[eol_num] = '\0'; /* lose the newline */ + + if (buf[0] && buf[0] != CREAD_HIST_CHAR) + cread_add_to_hist(buf); + hist_cur = hist_add_idx; + + return 0; +} + +#endif /* CONFIG_CMDLINE_EDITING */ + +/****************************************************************************/ + +static int cli_readline_into_buffer(const FLASH char *const prompt, char *buffer) +{ + char *p = buffer; +#ifdef CONFIG_CMDLINE_EDITING + unsigned int len = CONFIG_SYS_CBSIZE; + int rc; + static int initted; + + if (!initted) { + hist_init(); + initted = 1; + } + + if (prompt) + my_puts_P(prompt); + + rc = cread_line(prompt, p, &len); + return rc < 0 ? rc : (int) len; + +#else /* CONFIG_CMDLINE_EDITING */ + char *p_buf = p; + int n = 0; /* buffer index */ + int plen = 0; /* prompt length */ + int col; /* output column cnt */ + char c; + + /* print prompt */ + if (prompt) { + plen = strlen_P(prompt); + my_puts_P(prompt); + } + col = plen; + + for (;;) { + + c = my_getchar(); + + /* + * Special character handling + */ + switch (c) { + case '\r': /* Enter */ + case '\n': + *p = '\0'; + my_puts_P(PSTR("\r\n")); + return p - p_buf; + + case '\0': /* nul */ + continue; + + case 0x03: /* ^C - break */ + p_buf[0] = '\0'; /* discard input */ + return -1; + + case 0x15: /* ^U - erase line */ + while (col > plen) { + my_puts_P(erase_seq); + --col; + } + p = p_buf; + n = 0; + continue; + + case 0x17: /* ^W - erase word */ + p = delete_char(p_buf, p, &col, &n, plen); + while ((n > 0) && (*p != ' ')) + p = delete_char(p_buf, p, &col, &n, plen); + continue; + + case 0x08: /* ^H - backspace */ + case 0x7F: /* DEL - backspace */ + p = delete_char(p_buf, p, &col, &n, plen); + continue; + + default: + /* + * Must be a normal character then + */ + if (n < CONFIG_SYS_CBSIZE-2) { + if (c == '\t') { /* expand TABs */ +#ifdef CONFIG_AUTO_COMPLETE + /* + * if auto completion triggered just + * continue + */ + *p = '\0'; + if (cmd_auto_complete(prompt, + console_buffer, + &n, &col)) { + p = p_buf + n; /* reset */ + continue; + } +#endif + my_puts_P(tab_seq + (col & 07)); + col += 8 - (col & 07); + } else { + char buf[2]; + + /* + * Echo input using puts() to force an + * LCD flush if we are using an LCD + */ + ++col; + buf[0] = c; + buf[1] = '\0'; + my_puts(buf); + } + *p++ = c; + ++n; + } else { /* Buffer full */ + putchar('\a'); + } + } + } +#endif /* CONFIG_CMDLINE_EDITING */ +} + +int cli_readline(const FLASH char *const prompt) +{ + /* + * If console_buffer isn't 0-length the user will be prompted to modify + * it instead of entering it from scratch as desired. + */ + console_buffer[0] = '\0'; + + return cli_readline_into_buffer(prompt, console_buffer); +} + diff --git a/avr/cli_readline.h b/avr/cli_readline.h new file mode 100644 index 0000000..5b25762 --- /dev/null +++ b/avr/cli_readline.h @@ -0,0 +1,50 @@ +/* + * (C) Copyright 2014 Google, Inc + * Simon Glass + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef CLI_READLINE_H +#define CLI_READLINE_H + +extern char console_buffer[]; /* console I/O buffer */ + +/** + * cli_readline() - read a line into the console_buffer + * + * This is a convenience function which calls cli_readline_into_buffer(). + * + * @prompt: Prompt to display + * @return command line length excluding terminator, or -ve on error + */ +int cli_readline(const FLASH char *const prompt); + +/** + * readline_into_buffer() - read a line into a buffer + * + * Display the prompt, then read a command line into @buffer. The + * maximum line length is CONFIG_SYS_CBSIZE including a \0 terminator, which + * will always be added. + * + * The command is echoed as it is typed. Command editing is supported if + * CONFIG_CMDLINE_EDITING is defined. Tab auto-complete is supported if + * CONFIG_AUTO_COMPLETE is defined. If CONFIG_BOOT_RETRY_TIME is defined, + * then a timeout will be applied. + * + * If CONFIG_BOOT_RETRY_TIME is defined and retry_time >= 0, + * time out when time goes past endtime (timebase time in ticks). + * + * @prompt: Prompt to display + * @buffer: Place to put the line that is entered + * @timeout: Timeout in milliseconds, 0 if none + * @return command line length excluding terminator, or -ve on error: of the + * timeout is exceeded (either CONFIG_BOOT_RETRY_TIME or the timeout + * parameter), then -2 is returned. If a break is detected (Ctrl-C) then + * -1 is returned. + */ +//int cli_readline_into_buffer(const char *const prompt, char *buffer, int timeout); + + +#endif /* CLI_READLINE_H */ + diff --git a/avr/cmd_echo.c b/avr/cmd_echo.c new file mode 100644 index 0000000..d142ab6 --- /dev/null +++ b/avr/cmd_echo.c @@ -0,0 +1,58 @@ +/* + * Copyright 2000-2009 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include "common.h" +#include "command.h" + + +int do_echo(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + uint_fast8_t putnl = 1; + + (void) cmdtp; (void) flag; + + for (uint_fast8_t i = 1; i < argc; i++) { + + uint_fast8_t backslash = 0; + char *p = argv[i]; + char c; + + if (i != 1) + putchar(' '); + while ((c = *p++) != '\0') { + + if(backslash) { + backslash = 0; + if (c == 'c') { + putnl = 0; + continue; + } else + putchar('\\'); + } else { + if (c == '\\') { + backslash = 1; + continue; + } + } + putchar(c); + } + } + + if (putnl) + putchar('\n'); + + return 0; +} + +#if 0 +U_BOOT_CMD( + echo, CONFIG_SYS_MAXARGS, 1, do_echo, + "echo args to console", + "[args..]\n" + " - echo args to console; \\c suppresses newline" +); +#endif diff --git a/avr/cmd_help.c b/avr/cmd_help.c new file mode 100644 index 0000000..317eb5d --- /dev/null +++ b/avr/cmd_help.c @@ -0,0 +1,32 @@ + +#include "common.h" +#include "command.h" + +int do_help(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + const int len = cmd_tbl_item_count(); + return _do_help(cmd_tbl, len, cmdtp, flag, argc, argv); +} + + +#if 0 + +U_BOOT_CMD( + help, CONFIG_SYS_MAXARGS, 1, do_help, + "print command description/usage", + "\n" + " - print brief description of all commands\n" + "help command ...\n" + " - print detailed usage of 'command'" +); + +/* This does not use the U_BOOT_CMD macro as ? can't be used in symbol names */ +ll_entry_declare(cmd_tbl_t, question_mark, cmd) = { + "?", CONFIG_SYS_MAXARGS, 1, do_help, + "alias for 'help'", +#ifdef CONFIG_SYS_LONGHELP + "" +#endif /* CONFIG_SYS_LONGHELP */ +}; + +#endif diff --git a/avr/command.c b/avr/command.c new file mode 100644 index 0000000..ad39006 --- /dev/null +++ b/avr/command.c @@ -0,0 +1,525 @@ + +/* + * Command Processor Table + */ + +#include "common.h" + +#include +#include +#include +#include + +#include "config.h" +#include "debug.h" +#include "con-utils.h" +#include "env.h" +#include "timer.h" +#include "command.h" + + +static void print_blanks(int_fast8_t count) +{ + while(count--) + putchar(' '); +} + +static void print_usage_line(const FLASH char *name, const FLASH char *usage) +{ + my_puts_P(name); + print_blanks(CONFIG_SYS_MAXARGS - strlen_P(name)); + my_puts_P(PSTR(" - ")); + my_puts_P(usage); + my_puts_P(PSTR("\n")); +} + +int strcmp_PP(const FLASH char *s1, const FLASH char *s2) +{ + unsigned char c1, c2; + + while ((c1 = *(const FLASH unsigned char *)s1++) + == (c2 = *(const FLASH unsigned char *)s2++)) + if (c1 == 0) + return 0; + + return c1 - c2; +} + +int cmpstringp(const void *p1, const void *p2) +{ + return strcmp_PP((*(const FLASH cmd_tbl_t **) p1)->name, + (*(const FLASH cmd_tbl_t **) p2)->name); +} + +int cmd_tbl_item_count(void) +{ + cmd_tbl_t * p = cmd_tbl; + int count = 0; + + while (p->name != NULL) { + p++; count++; + } + return count; +} + +/* + * Use puts() instead of printf() to avoid printf buffer overflow + * for long help messages + */ + +int _do_help (cmd_tbl_t *cmd_start, int cmd_items, cmd_tbl_t * cmdtp, + int flag, int argc, char * const argv[]) +{ + uint_fast8_t i; + int rcode = 0; + + (void) flag; + + if (argc == 1) { /*show list of commands */ + cmd_tbl_t *cmd_array[cmd_items]; + int i; + + /* Make array of commands from .uboot_cmd section */ + cmdtp = cmd_start; + for (i = 0; i < cmd_items; i++) { + cmd_array[i] = cmdtp++; + } + + /* Sort command list */ + qsort(cmd_array, cmd_items, sizeof (cmd_tbl_t *), cmpstringp); + + /* print short help (usage) */ + for (i = 0; i < cmd_items; i++) { + const FLASH char *usage = cmd_array[i]->usage; + + /* allow user abort */ + if (ctrlc ()) + return 1; + if (usage == NULL) + continue; +#ifdef GCC_BUG_61443 + print_usage_line(cmd_array[i]->name, usage); +#else + printf_P(PSTR("%-" stringify(CONFIG_SYS_MAXARGS) "S - %S\n"), + cmd_array[i]->name, usage); +#endif + } + return 0; + } + /* + * command help (long version) + */ + for (i = 1; i < argc; ++i) { + if ((cmdtp = find_cmd_tbl (argv[i], cmd_start, cmd_items )) != NULL) { + rcode |= cmd_usage(cmdtp); + } else { + printf_P(PSTR("Unknown command '%s' - try 'help'" + " without arguments.\n\n"), argv[i] + ); + rcode = 1; + } + } + return rcode; +} + +/*************************************************************************** + * find command table entry for a command + */ +cmd_tbl_t *find_cmd_tbl (const char *cmd, cmd_tbl_t *table, int table_len) +{ + cmd_tbl_t *cmdtp; + cmd_tbl_t *cmdtp_temp = table; /*Init value */ + int len; + uint_fast8_t n_found = 0; + + if (!cmd) + return NULL; + + len = strlen(cmd); + + for (cmdtp = table; + cmdtp != table + table_len; + cmdtp++) { + if (strncmp_P (cmd, cmdtp->name, len) == 0) { + if (len == strlen (cmdtp->name)) + return cmdtp; /* full match */ + + cmdtp_temp = cmdtp; /* abbreviated command ? */ + n_found++; + } + } + if (n_found == 1) { /* exactly one match */ + return cmdtp_temp; + } + + return NULL; /* not found or ambiguous command */ +} + + +cmd_tbl_t *find_cmd (const char *cmd) +{ + return find_cmd_tbl(cmd, cmd_tbl, cmd_tbl_item_count()); +} + + +int cmd_usage(const FLASH cmd_tbl_t *cmdtp) +{ +// printf("%s - %s\n\n", cmdtp->name, cmdtp->usage); + print_usage_line(cmdtp->name, cmdtp->usage); +#if 0 + my_puts_P(cmdtp->name); + print_blanks(CONFIG_SYS_MAXARGS - strlen_P(cmdtp->name)); + my_puts_P(PSTR(" - ")); + my_puts_P(cmdtp->usage); + my_puts_P(PSTR("\n\n")); +#endif +#ifdef CONFIG_SYS_LONGHELP +// printf("Usage:\n%s ", cmdtp->name); + my_puts_P(PSTR("Usage:\n")); + my_puts_P(cmdtp->name); + + if (!cmdtp->help) { + my_puts_P(PSTR(" - No additional help available.\n")); + return 1; + } + + my_puts_P(cmdtp->help); + my_puts_P(PSTR("\n")); +#endif /* CONFIG_SYS_LONGHELP */ + return 1; +} + +#ifdef CONFIG_AUTO_COMPLETE + +int var_complete(int argc, char * const argv[], char last_char, int maxv, char *cmdv[]) +{ + static char tmp_buf[512]; + int space; + + space = last_char == '\0' || isblank(last_char); + + if (space && argc == 1) + return env_complete("", maxv, cmdv, sizeof(tmp_buf), tmp_buf); + + if (!space && argc == 2) + return env_complete(argv[1], maxv, cmdv, sizeof(tmp_buf), tmp_buf); + + return 0; +} + +/*************************************************************************************/ + +static int complete_cmdv(int argc, char * const argv[], char last_char, int maxv, char *cmdv[]) +{ + cmd_tbl_t *cmdtp = cmd_tbl; +// const int count = ARRAY_SIZE(cmd_tbl); +// const cmd_tbl_t *cmdend = cmdtp + count; +// const char *p; + int len, clen; + int n_found = 0; + const char *cmd; + + /* sanity? */ + if (maxv < 2) + return -2; + + cmdv[0] = NULL; + + if (argc == 0) { + /* output full list of commands */ + for (; cmdtp->name[0] != '\0'; cmdtp++) { + if (n_found >= maxv - 2) { + cmdv[n_found++] = "..."; + break; + } + cmdv[n_found++] = cmdtp->name; + } + cmdv[n_found] = NULL; + return n_found; + } + + /* more than one arg or one but the start of the next */ + if (argc > 1 || (last_char == '\0' || isblank(last_char))) { + cmdtp = find_cmd(argv[0]); + if (cmdtp == NULL || cmdtp->complete == NULL) { + cmdv[0] = NULL; + return 0; + } + return (*cmdtp->complete)(argc, argv, last_char, maxv, cmdv); + } + + cmd = argv[0]; + + len = strlen(cmd); + + /* return the partial matches */ + for (; cmdtp->name[0] != '\0'; cmdtp++) { + + clen = strlen(cmdtp->name); + if (clen < len) + continue; + + if (memcmp(cmd, cmdtp->name, len) != 0) + continue; + + /* too many! */ + if (n_found >= maxv - 2) { + cmdv[n_found++] = "..."; + break; + } + + cmdv[n_found++] = cmdtp->name; + } + + cmdv[n_found] = NULL; + return n_found; +} + +static int make_argv(char *s, int argvsz, char *argv[]) +{ + int argc = 0; + + /* split into argv */ + while (argc < argvsz - 1) { + + /* skip any white space */ + while (isblank(*s)) + ++s; + + if (*s == '\0') /* end of s, no more args */ + break; + + argv[argc++] = s; /* begin of argument string */ + + /* find end of string */ + while (*s && !isblank(*s)) + ++s; + + if (*s == '\0') /* end of s, no more args */ + break; + + *s++ = '\0'; /* terminate current arg */ + } + argv[argc] = NULL; + + return argc; +} + +static void print_argv(const char *banner, const char *leader, const char *sep, int linemax, char * const argv[]) +{ + int ll = leader != NULL ? strlen(leader) : 0; + int sl = sep != NULL ? strlen(sep) : 0; + int len, i; + + if (banner) { + my_puts_P(PSTR("\n")); + my_puts(banner); + } + + i = linemax; /* force leader and newline */ + while (*argv != NULL) { + len = strlen(*argv) + sl; + if (i + len >= linemax) { + my_puts_P(PSTR("\n")); + if (leader) + my_puts(leader); + i = ll - sl; + } else if (sep) + my_puts(sep); + my_puts(*argv++); + i += len; + } + my_puts_P(PSTR("\n")); +} + +static int find_common_prefix(char * const argv[]) +{ + int i, len; + char *anchor, *s, *t; + + if (*argv == NULL) + return 0; + + /* begin with max */ + anchor = *argv++; + len = strlen(anchor); + while ((t = *argv++) != NULL) { + s = anchor; + for (i = 0; i < len; i++, t++, s++) { + if (*t != *s) + break; + } + len = s - anchor; + } + return len; +} + +static char tmp_buf[CONFIG_SYS_CBSIZE]; /* copy of console I/O buffer */ + +int cmd_auto_complete(const FLASH char *const prompt, char *buf, int *np, int *colp) +{ + int n = *np, col = *colp; + char *argv[CONFIG_SYS_MAXARGS + 1]; /* NULL terminated */ + char *cmdv[20]; + char *s, *t; + const char *sep; + int i, j, k, len, seplen, argc; + int cnt; + char last_char; + + if (strcmp_PP(prompt, CONFIG_SYS_PROMPT) != 0) + return 0; /* not in normal console */ + + cnt = strlen(buf); + if (cnt >= 1) + last_char = buf[cnt - 1]; + else + last_char = '\0'; + + /* copy to secondary buffer which will be affected */ + strcpy(tmp_buf, buf); + + /* separate into argv */ + argc = make_argv(tmp_buf, sizeof(argv)/sizeof(argv[0]), argv); + + /* do the completion and return the possible completions */ + i = complete_cmdv(argc, argv, last_char, sizeof(cmdv)/sizeof(cmdv[0]), cmdv); + + /* no match; bell and out */ + if (i == 0) { + if (argc > 1) /* allow tab for non command */ + return 0; + putchar('\a'); + return 1; + } + + s = NULL; + len = 0; + sep = NULL; + seplen = 0; + if (i == 1) { /* one match; perfect */ + k = strlen(argv[argc - 1]); + s = cmdv[0] + k; + len = strlen(s); + sep = " "; + seplen = 1; + } else if (i > 1 && (j = find_common_prefix(cmdv)) != 0) { /* more */ + k = strlen(argv[argc - 1]); + j -= k; + if (j > 0) { + s = cmdv[0] + k; + len = j; + } + } + + if (s != NULL) { + k = len + seplen; + /* make sure it fits */ + if (n + k >= CONFIG_SYS_CBSIZE - 2) { + putchar('\a'); + return 1; + } + + t = buf + cnt; + for (i = 0; i < len; i++) + *t++ = *s++; + if (sep != NULL) + for (i = 0; i < seplen; i++) + *t++ = sep[i]; + *t = '\0'; + n += k; + col += k; + my_puts(t - k); + if (sep == NULL) + putchar('\a'); + *np = n; + *colp = col; + } else { + print_argv(NULL, " ", " ", 78, cmdv); + + my_puts_P(prompt); + my_puts(buf); + } + return 1; +} + +#endif /* CONFIG_AUTO_COMPLETE */ + + + +/** + * Call a command function. This should be the only route in U-Boot to call + * a command, so that we can track whether we are waiting for input or + * executing a command. + * + * @param cmdtp Pointer to the command to execute + * @param flag Some flags normally 0 (see CMD_FLAG_.. above) + * @param argc Number of arguments (arg 0 must be the command text) + * @param argv Arguments + * @return 0 if command succeeded, else non-zero (CMD_RET_...) + */ +static int cmd_call(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + int result; + + result = (cmdtp->cmd)(cmdtp, flag, argc, argv); + if (result) + debug("Command failed, result=%d\n", result); + return result; +} + +enum command_ret_t cmd_process(int flag, int argc, char * const argv[], + uint_fast8_t *repeatable) +{ + enum command_ret_t rc = CMD_RET_SUCCESS; + cmd_tbl_t *cmdtp; + + /* Look up command in command table */ + cmdtp = find_cmd(argv[0]); + if (cmdtp == NULL) { + printf_P(PSTR("Unknown command '%s' - try 'help'\n"), argv[0]); + return 1; + } + if (!cmdtp->cmd) { + debug("### Command '%s' found, but ->cmd == NULL \n", argv[0]); + return 1; + } + + /* found - check max args */ + if (argc > cmdtp->maxargs) + rc = CMD_RET_USAGE; + +#if defined(CONFIG_CMD_BOOTD) + /* avoid "bootd" recursion */ + else if (cmdtp->cmd == do_bootd) { + if (flag & CMD_FLAG_BOOTD) { + my_puts_P(PSTR("'bootd' recursion detected\n")); + rc = CMD_RET_FAILURE; + } else { + flag |= CMD_FLAG_BOOTD; + } + } +#endif + + /* If OK so far, then do the command */ + if (!rc) { + rc = cmd_call(cmdtp, flag, argc, argv); + *repeatable &= cmdtp->repeatable; + } + if (rc == CMD_RET_USAGE) + rc = cmd_usage(cmdtp); + return rc; +} + +int cmd_process_error(cmd_tbl_t *cmdtp, int err) +{ + char buf[strlen_P(cmdtp->name) + 1]; + strcpy_P(buf, cmdtp->name); + + if (err) { + printf_P(PSTR("Command '%s' failed: Error %d\n"), buf, err); + return 1; + } + + return 0; +} diff --git a/avr/command.h b/avr/command.h new file mode 100644 index 0000000..82b83ba --- /dev/null +++ b/avr/command.h @@ -0,0 +1,157 @@ + +/* + * Definitions for Command Processor + */ +#ifndef __COMMAND_H +#define __COMMAND_H + +#include "common.h" +#include "config.h" + +#ifndef NULL +#define NULL 0 +#endif + +/* Default to a width of 8 characters for help message command width */ +#ifndef CONFIG_SYS_HELP_CMD_WIDTH +#define CONFIG_SYS_HELP_CMD_WIDTH 8 +#endif + +/* + * Monitor Command Table + */ + +struct cmd_tbl_s { + const FLASH char *name; /* Command Name */ + int maxargs; /* maximum number of arguments */ + int repeatable; /* autorepeat allowed? */ + /* Implementation function */ + int (*cmd)(const FLASH struct cmd_tbl_s *, int, int, char * const []); + const FLASH char *usage; /* Usage message (short) */ +#ifdef CONFIG_SYS_LONGHELP + const FLASH char *help; /* Help message (long) */ +#endif +#ifdef CONFIG_AUTO_COMPLETE + /* do auto completion on the arguments */ + int (*complete)(int argc, char * const argv[], char last_char, int maxv, char *cmdv[]); +#endif +}; + +typedef const FLASH struct cmd_tbl_s cmd_tbl_t; + +extern int do_run(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]); + +/* command.c */ +int _do_help (cmd_tbl_t *cmd_start, int cmd_items, cmd_tbl_t * cmdtp, int + flag, int argc, char * const argv[]); +cmd_tbl_t *find_cmd(const char *cmd); +cmd_tbl_t *find_cmd_tbl (const char *cmd, cmd_tbl_t *table, int table_len); + +int cmd_tbl_item_count(void); +int cmd_usage(cmd_tbl_t *cmdtp); + +#ifdef CONFIG_AUTO_COMPLETE +extern int var_complete(int argc, char * const argv[], char last_char, int maxv, char *cmdv[]); +extern int cmd_auto_complete(const char *const prompt, char *buf, int *np, int *colp); +#endif + +/** + * cmd_process_error() - report and process a possible error + * + * @cmdtp: Command which caused the error + * @err: Error code (0 if none, -ve for error, like -EIO) + * @return 0 if there is not error, 1 (CMD_RET_FAILURE) if an error is found + */ +int cmd_process_error(cmd_tbl_t *cmdtp, int err); + +/* + * Monitor Command + * + * All commands use a common argument format: + * + * void function (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]); + */ + + +#ifdef CONFIG_CMD_BOOTD +extern int do_bootd(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]); +#endif +#ifdef CONFIG_CMD_BOOTM +extern int do_bootm(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]); +extern int bootm_maybe_autostart(cmd_tbl_t *cmdtp, const char *cmd); +#else +static inline int bootm_maybe_autostart(cmd_tbl_t *cmdtp, const char *cmd) +{ + (void) cmdtp; (void) cmd; + + return 0; +} +#endif + +extern int common_diskboot(cmd_tbl_t *cmdtp, const char *intf, int argc, + char *const argv[]); + +extern int do_reset(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]); + +/* + * Error codes that commands return to cmd_process(). We use the standard 0 + * and 1 for success and failure, but add one more case - failure with a + * request to call cmd_usage(). But the cmd_process() function handles + * CMD_RET_USAGE itself and after calling cmd_usage() it will return 1. + * This is just a convenience for commands to avoid them having to call + * cmd_usage() all over the place. + */ +enum command_ret_t { + CMD_RET_SUCCESS, /* 0 = Success */ + CMD_RET_FAILURE, /* 1 = Failure */ + CMD_RET_USAGE = -1, /* Failure, please report 'usage' error */ +}; + +/** + * Process a command with arguments. We look up the command and execute it + * if valid. Otherwise we print a usage message. + * + * @param flag Some flags normally 0 (see CMD_FLAG_.. above) + * @param argc Number of arguments (arg 0 must be the command text) + * @param argv Arguments + * @param repeatable This function sets this to 0 if the command is not + * repeatable. If the command is repeatable, the value + * is left unchanged. + * @return 0 if the command succeeded, 1 if it failed + */ +int cmd_process(int flag, int argc, char * const argv[], uint_fast8_t *repeatable); + + +/* + * Command Flags: + */ +#define CMD_FLAG_REPEAT 0x0001 /* repeat last command */ +#define CMD_FLAG_BOOTD 0x0002 /* command is from bootd */ + +#ifdef CONFIG_AUTO_COMPLETE +# define _CMD_COMPLETE(x) x, +#else +# define _CMD_COMPLETE(x) +#endif +#ifdef CONFIG_SYS_LONGHELP +# define _CMD_HELP(x) x, +#else +# define _CMD_HELP(x) +#endif + + +#define CMD_TBL_ITEM_COMPLETE(_name, _maxargs, _rep, _cmd, \ + _usage, _help, _comp) \ + { FSTR(#_name), _maxargs, _rep, _cmd, FSTR(_usage), \ + _CMD_HELP(FSTR(_help)) _CMD_COMPLETE(_comp) } + +#define CMD_TBL_ITEM(_name, _maxargs, _rep, _cmd, _usage, _help) \ + CMD_TBL_ITEM_COMPLETE(_name, _maxargs, _rep, _cmd, \ + _usage, _help, NULL) + +typedef int (*do_cmd_t)(cmd_tbl_t *, int, int, char * const []); + +extern cmd_tbl_t cmd_tbl[]; + + +#endif /* __COMMAND_H */ diff --git a/avr/command_tbl.c b/avr/command_tbl.c new file mode 100644 index 0000000..c0579bc --- /dev/null +++ b/avr/command_tbl.c @@ -0,0 +1,76 @@ + +#include "common.h" + +#include "command.h" + + + +extern int do_help(cmd_tbl_t *, int, int, char * const []); +extern int do_echo(cmd_tbl_t *, int, int, char * const []); +extern int do_env_print(cmd_tbl_t *, int, int, char * const []); +extern int do_env_set(cmd_tbl_t *, int, int, char * const []); + + +cmd_tbl_t cmd_tbl[] = { + +CMD_TBL_ITEM( + echo, CONFIG_SYS_MAXARGS, 1, do_echo, + "echo args to console", + "[args..]\n" + " - echo args to console; \\c suppresses newline" +), +CMD_TBL_ITEM_COMPLETE( + run, CONFIG_SYS_MAXARGS, 1, do_run, + "run commands in an environment variable", + "var [...]\n" + " - run the commands in the environment variable(s) 'var'", + var_complete +), +CMD_TBL_ITEM_COMPLETE( + printenv, CONFIG_SYS_MAXARGS, 1, do_env_print, + "print environment variables", + "\n - print [all] values of all environment variables\n" + "printenv name ...\n" + " - print value of environment variable 'name'", + var_complete +), +CMD_TBL_ITEM_COMPLETE( + setenv, CONFIG_SYS_MAXARGS, 0, do_env_set, + "set environment variables", + "[-f] name value ...\n" + " - [forcibly] set environment variable 'name' to 'value ...'\n" + "setenv [-f] name\n" + " - [forcibly] delete environment variable 'name'", + var_complete +), +CMD_TBL_ITEM( + help, CONFIG_SYS_MAXARGS, 1, do_help, + "print command description/usage", + "\n" + " - print brief description of all commands\n" + "help command ...\n" + " - print detailed usage of 'command'" +), + +/* This does not use the U_BOOT_CMD macro as ? can't be used in symbol names */ + {FSTR("?"), CONFIG_SYS_MAXARGS, 1, do_help, + FSTR("alias for 'help'"), +#ifdef CONFIG_SYS_LONGHELP + FSTR(""), +#endif /* CONFIG_SYS_LONGHELP */ +#ifdef CONFIG_AUTO_COMPLETE + 0, +#endif +}, +/* Mark end of table */ +{ 0 }, +}; + + + + + + + + + diff --git a/avr/common.h b/avr/common.h new file mode 100644 index 0000000..ace0fcb --- /dev/null +++ b/avr/common.h @@ -0,0 +1,57 @@ +#ifndef COMMON_H +#define COMMON_H + +#ifdef __AVR__ +#include + +//TODO: +// Known to work: 4.8.4, 4.9.1 +// Known to fail: 4.8.3, 4.9.0 +#define GCC_BUG_61443 1 + +#else +// TODO: stm32 +#endif + +#include + +#ifdef __FLASH +#define FLASH __flash +#else +#define FLASH +#endif + +#define stringify(s) tostring(s) +#define tostring(s) #s + +#define FSTR(X) ((const FLASH char[]) { X } ) +#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) + + +#ifdef __AVR__ +#define Stat GPIOR0 +#else +extern volatile uint_least8_t Stat; +#endif + +#define S_10MS_TO (1<<0) + + +#include + +static inline +void my_puts(const char *s) +{ + fputs(s, stdout); +// _delay_ms(10); +} + +static inline +void my_puts_P(const char *s) +{ + fputs_P(s, stdout); +// _delay_ms(10); +} + +#endif /* COMMON_H */ + diff --git a/avr/con-utils.c b/avr/con-utils.c new file mode 100644 index 0000000..430ba98 --- /dev/null +++ b/avr/con-utils.c @@ -0,0 +1,91 @@ + +#include +#include + +#include "serial.h" +#include "con-utils.h" + + +uint_fast8_t tstc(void) +{ + return serial_tstc(); +} + +int my_getchar(void) +{ + int c; + + while((c = serial_getc()) < 0) + ; + return c; +} + + +/* test if ctrl-c was pressed */ + +static uint_fast8_t ctrlc_disabled = 0; /* see disable_ctrl() */ +static uint_fast8_t ctrlc_was_pressed = 0; + +uint_fast8_t ctrlc(void) +{ + if (!ctrlc_disabled) { + switch (serial_getc()) { + case 0x03: /* ^C - Control C */ + ctrlc_was_pressed = 1; + return 1; + default: + break; + } + } + return 0; +} + +/* Reads user's confirmation. + Returns 1 if user's input is "y", "Y", "yes" or "YES" +*/ +uint_fast8_t confirm_yesno(void) +{ + unsigned int i; + char str_input[5]; + + /* Flush input */ + while (serial_getc()) + ; + i = 0; + while (i < sizeof(str_input)) { + str_input[i] = my_getchar(); + putchar(str_input[i]); + if (str_input[i] == '\r') + break; + i++; + } + putchar('\n'); + if (strncmp(str_input, "y\r", 2) == 0 || + strncmp(str_input, "Y\r", 2) == 0 || + strncmp(str_input, "yes\r", 4) == 0 || + strncmp(str_input, "YES\r", 4) == 0) + return 1; + return 0; +} + +/* pass 1 to disable ctrlc() checking, 0 to enable. + * returns previous state + */ +uint_fast8_t disable_ctrlc(uint_fast8_t disable) +{ + uint_fast8_t prev = ctrlc_disabled; /* save previous state */ + + ctrlc_disabled = disable; + return prev; +} + +uint_fast8_t had_ctrlc (void) +{ + return ctrlc_was_pressed; +} + +void clear_ctrlc(void) +{ + ctrlc_was_pressed = 0; +} + diff --git a/avr/con-utils.h b/avr/con-utils.h new file mode 100644 index 0000000..f03dfb3 --- /dev/null +++ b/avr/con-utils.h @@ -0,0 +1,22 @@ +#ifndef CON_UTILS_H +#define CON_UTILS_H + +uint_fast8_t tstc(void); + +int my_getchar(void); + +/* test if ctrl-c was pressed */ +uint_fast8_t ctrlc(void); + + +/* pass 1 to disable ctrlc() checking, 0 to enable. + * returns previous state + */ +uint_fast8_t disable_ctrlc(uint_fast8_t disable); + +uint_fast8_t had_ctrlc (void); +void clear_ctrlc(void); + +#endif /* CON_UTILS_H */ + + diff --git a/avr/config.h b/avr/config.h new file mode 100644 index 0000000..9e6d4d7 --- /dev/null +++ b/avr/config.h @@ -0,0 +1,24 @@ +#ifndef CONFIG_H +#define CONFIG_H + +#define CONFIG_ENV_SIZE 2000 +#define CONFIG_ENV_OFFSET 0 +#define CONFIG_ENVVAR_MAX 20 + +#define CONFIG_BOOTDELAY 4 +//#define CONFIG_ZERO_BOOTDELAY_CHECK 1 + +#define CONFIG_SYS_CBSIZE 250 +#define CONFIG_SYS_ENV_NAMELEN 16 +#define CONFIG_SYS_MAXARGS 8 + +#define CONFIG_SYS_PROMPT "=> " + + +//#define CONFIG_CMDLINE_EDITING 1 +//#define CONFIG_AUTO_COMPLETE 1 + +#define CONFIG_SYS_LONGHELP 1 + +#endif /* CONFIG_H */ + diff --git a/avr/crc.h b/avr/crc.h new file mode 100644 index 0000000..c38bae4 --- /dev/null +++ b/avr/crc.h @@ -0,0 +1,12 @@ +#ifndef CRC_H +#define CRC_H + +#include + +static inline +uint16_t crc16(uint16_t crc, uint8_t data) +{ + return _crc_ccitt_update(crc, data); +} + +#endif /* CRC_H */ diff --git a/avr/debug.h b/avr/debug.h index ea67a99..1815166 100644 --- a/avr/debug.h +++ b/avr/debug.h @@ -2,13 +2,33 @@ #ifndef DEBUG_H_ #define DEBUG_H_ +#include "common.h" #include +#ifdef DEBUG +#define _DEBUG 1 +#else +#define _DEBUG 0 +#endif + +#define debug_cond(cond, fmt, args...) \ + do { \ + if (cond) \ + printf_P(PSTR(fmt), ##args); \ + } while (0) + +#define debug(fmt, args...) \ + debug_cond(_DEBUG, fmt, ##args) + + +#if 1 #ifdef DEBUG #define DBG_P(lvl, format, ...) if (DEBUG>=lvl) \ fprintf_P( stdout, PSTR(format), ##__VA_ARGS__ ) #else #define DBG_P(lvl, ...) #endif +#endif /* 0 */ #endif /* DEBUG_H_ */ + diff --git a/avr/env.c b/avr/env.c new file mode 100644 index 0000000..97bd2e3 --- /dev/null +++ b/avr/env.c @@ -0,0 +1,808 @@ +#include "common.h" +#include +#include + +#include + +#include "config.h" +#include "debug.h" +#include "xmalloc.h" +#include "crc.h" +#include "command.h" + +#include "env.h" + + + +#define DELIM "\0" +#define ENV_SIZE CONFIG_ENV_SIZE-2 + + +#define ENV_GET_VAL (1<<0) + + +/* + * Debugging + * + * TODO: move elsewhere + */ +#include + +static void print_blanks(uint_fast8_t count) +{ + while(count--) + putchar(' '); +} + + +void dump_ram(const uint8_t *startaddr, int len, char *title) +{ + uint8_t llen = 16; + uint8_t pre = (size_t) startaddr % 16; + const uint8_t *addr = (uint8_t *) ((size_t) startaddr & ~0x0f); + len += pre; + uint8_t i; + + if (title && *title) + printf_P(PSTR("%s\n"),title); + + while (len) { + if (len < 16) + llen = len; + + printf_P(PSTR(" %.4x:"), (size_t) addr); + print_blanks(3 * pre); + for (i = pre; i < llen; i++) + printf_P(PSTR(" %.2x"), addr[i]); + print_blanks(3 * (16 - i + 1) + pre); + for (i = pre; i < llen; i++) + printf_P(PSTR("%c"), isprint(addr[i]) ? addr[i] : '.'); + putchar('\n'); + + pre = 0; + addr += 16; + len -= llen; + } +} + +void dump_heap(void) +{ + extern unsigned int __brkval; + + dump_ram((uint8_t *) __malloc_heap_start, + __brkval - (unsigned int) __malloc_heap_start, + "=== Heap:"); +} + +/* TODO: combine with dump_ram() */ +void dump_eep(const uint8_t *addr, int len) +{ + int i; + uint8_t buf[16]; + + printf_P(PSTR("eeprom dump:")); + while (len) { + printf_P(PSTR("\n 0x%.4x:"), (unsigned int) addr); + for (i = 0; i<16; i++) + buf[i] = eeprom_read_byte(addr + i); + for (i = 0; i<16; i++) + printf_P(PSTR(" %.2x"), buf[i]); + printf_P(PSTR(" ")); + for (i = 0; i<16; i++) + printf_P(PSTR("%c"), isprint(buf[i]) ? buf[i] : '.'); + + addr += 16; + len -= len > 16 ? 16 : len; + } + putchar('\n'); +} + + +const FLASH char default_env[] = { + "bootdelay=" "3" DELIM + "bootcmd=" "echo hallo" DELIM + "baudrate=" "115200" DELIM + DELIM +}; + + +typedef struct environment_s { + uint16_t crc; /* CRC16 over data bytes */ + char data[ENV_SIZE]; /* Environment data */ +} env_t; + + + +typedef struct env_item_s { +#define EF_N_EEP (1<<7) +#define EF_V_EEP (1<<6) +#define EF_DIRTY (1<<0) + uint8_t flags; + union { + uint16_t eep; + char *ram; + } name; + union { + uint16_t eep; + char *ram; + } val; +} env_item_t; + + +static env_item_t env_list[CONFIG_ENVVAR_MAX]; +static int entrycount; + + +static char env_get_char(uint16_t index) +{ + unsigned int off = CONFIG_ENV_OFFSET; + + return (char) eeprom_read_byte((const uint8_t *)off + index + + offsetof(env_t, data)); +} + + +uint16_t ee_name_get(char *buf, env_item_t *ep) +{ + int i = 0; + char c; + + if (ep->flags & EF_N_EEP) { + + while ((c = env_get_char(ep->name.eep + i)) != '=' && + c != '\0' && i < CONFIG_SYS_ENV_NAMELEN) { + + buf[i] = c; + i++; + } + if (i > 0 && c != '=') { + debug("** ee_name: '%s' not '=' terminated!\n", buf); + } + } else { + strncpy(buf, ep->name.ram, CONFIG_SYS_ENV_NAMELEN); + i = strnlen(buf, CONFIG_SYS_ENV_NAMELEN); + } + buf[i] = '\0'; + return i; +} + + +uint16_t ee_val_get(char *buf, uint16_t index, int len) +{ + int i = 0; + char c; + + while ((c = env_get_char (index + i)) != '\0' && i < len) { + buf[i] = c; + i++; + }; + + buf[i] = '\0'; + + /* TODO: len check */ + + return i; +} + +static +int comp_env_key_item(const void *key, const void *item) +{ + char buf[CONFIG_SYS_ENV_NAMELEN+1]; + env_item_t *ep = (env_item_t *) item; + + ee_name_get(buf, ep); + + return strcmp((char *) key, buf); +} + +static +int comp_env_items(const void *m1, const void *m2) +{ + char b1[CONFIG_SYS_ENV_NAMELEN+1]; + char b2[CONFIG_SYS_ENV_NAMELEN+1]; + + env_item_t *ep1 = (env_item_t *) m1; + env_item_t *ep2 = (env_item_t *) m2; + + ee_name_get(b1, ep1); + ee_name_get(b2, ep2); + + return strcmp(b1, b2); +} + +#if 0 +env_item_t * ee_entry_get(const char *s, env_item_t *ep, uint_fast8_t flag) +{ + char name[CONFIG_SYS_ENV_NAMELEN+1]; + uint16_t idx = 0; + int nlen; + +//printf_P(PSTR("*** ee_entry_get: >>>>>>>>> ENTER <<<<<<<<<\n")); + + + while ((nlen = ee_name_get(name, idx)) != 0 && idx < CONFIG_ENV_SIZE) { + +//printf_P(PSTR("** idx: %d, name: '%s', s: '%s', nlen: %d, cpres:%d\n"), +// idx, name, s, nlen, strncmp(name, s, nlen)); + + if (strncmp(name, s, nlen) == 0) + break; + + idx += nlen + 1; + while (env_get_char(idx++) != 0) + ; + } + + if (nlen) { + char *vp; + ep->flags = EF_N_EEP; + ep->name.eep = idx; + if (flag & ENV_GET_VAL) { + vp = xmalloc(CONFIG_SYS_CBSIZE); + nlen = ee_val_get(vp, idx + nlen + 1, CONFIG_SYS_CBSIZE); + ep->val = xrealloc(vp, nlen + 1); + } else + ep->val = NULL; + + +//printf_P(PSTR("*** ee_entry_get: >>>>>> LEAVE 0x%.4x <<<<<\n"), +// (unsigned int) ep); + return ep; + } + +//printf_P(PSTR("*** ee_entry_get: >>>>>> LEAVE <<<<<\n")); + return NULL; +} +#endif + +#if 0 +static char p_env_name_buf[CONFIG_SYS_ENV_NAMELEN+1]; + +static char *dbg_p_env_name(env_item_t *p) +{ + if (p->flags & EF_N_EEP) { + if (ee_name_get(p_env_name_buf, p) != 0) + return p_env_name_buf; + else + return ""; + } + return ""; +} +#endif + +int env_item_print(env_item_t *ep) +{ + char buf[CONFIG_SYS_ENV_NAMELEN+1]; + int len; + env_item_t e = *ep; + + ee_name_get(buf, ep); + len = printf_P(PSTR("%s="), buf); + + if (e.val.ram != NULL) { + while (1) { + char c; + if (e.flags & EF_V_EEP) + c = env_get_char(e.val.eep++); + else + c = *e.val.ram++; + + if (c != '\0') { + putchar(c); + len++; + } else + break; + } + } + putchar('\n'); + len ++; + + return len; +} + + +static env_item_t *envlist_search(const char *name) +{ + return bsearch(name, env_list, entrycount, + sizeof(env_item_t), comp_env_key_item); +} + + +env_item_t *envlist_insert(const char *key, env_item_t *e) +{ + const size_t size = sizeof(env_item_t); + + if (entrycount < CONFIG_ENVVAR_MAX) { + env_list[entrycount++] = *e; + qsort(env_list, entrycount, size, comp_env_items); + + return bsearch(key, env_list, entrycount, + size, comp_env_key_item); + + } else + return NULL; +} + + +env_item_t *envlist_enter(env_item_t *e) +{ + char *key = e->name.ram; + const size_t size = sizeof(env_item_t); + env_item_t *ep; + + ep = bsearch(key, env_list, entrycount, + size, comp_env_key_item); + + if (ep == NULL) { + if (entrycount < CONFIG_ENVVAR_MAX) { + + env_list[entrycount++] = *e; + qsort(env_list, entrycount, size, comp_env_items); + ep = bsearch(key, env_list, entrycount, + size, comp_env_key_item); + } + } else { + if ((ep->flags & EF_V_EEP) == 0) { + free(ep->val.ram); + } + ep->val.ram = e->val.ram; + } + + if (ep != NULL) { + ep->flags |= EF_DIRTY; + ep->flags &= ~EF_V_EEP; + + if ((ep->flags & EF_N_EEP) == 0) { + int nlen = strnlen(key, CONFIG_SYS_ENV_NAMELEN); + char *name = xmalloc(nlen + 1); + if (name == NULL) { + printf_P(PSTR("## Can't malloc %d bytes\n"), + nlen + 1); + return NULL; + } + strcpy(name, key); + name[nlen] = '\0'; + ep->name.ram = name; + } + } + + return ep; +} + + +static env_item_t *envlist_get(const char *name, uint_fast8_t flag) +{ + env_item_t *ep; + + ep = envlist_search(name); + + if (ep != NULL && (flag & ENV_GET_VAL)) { + if (ep->flags & EF_V_EEP) { + char *vp; + uint_fast8_t len; + vp = xmalloc(CONFIG_SYS_CBSIZE); + len = ee_val_get(vp, ep->val.eep, CONFIG_SYS_CBSIZE); + ep->val.ram = xrealloc(vp, len + 1); + ep->flags &= ~EF_V_EEP; + } + } + + return ep; +} + + +static int envlist_delete(const char *name) +{ + size_t size = sizeof(env_item_t); + env_item_t *ep = bsearch(name, env_list, entrycount, + sizeof(env_item_t), comp_env_key_item); + int rc = 0; + + if (ep != NULL) { + + if ((ep->flags & EF_V_EEP) == 0) + free(ep->val.ram); + if ((ep->flags & EF_N_EEP) == 0) + free(ep->name.ram); + + entrycount--; +printf_P(PSTR("*** envlist_delete memmove: 0x%.4x, 0x%.4x, %d\n"), + (unsigned) ep, (unsigned) ep + size, + (env_list - ep)*size + entrycount*size); + memmove(ep, ep + 1, (env_list - ep)*size + entrycount*size); + + rc = 1; + } +#if 1 + dump_ram((uint8_t *) &env_list[0], entrycount * sizeof(env_item_t), + "=== env_list:"); + dump_heap(); + debug("** entrycount: %d\n", entrycount); + for (int i=0; ival.ram; + +#if 1 + dump_ram((uint8_t *) &env_list[0], entrycount * sizeof(env_item_t), + "=== env_list:"); + dump_heap(); + debug("** entrycount: %d\n", entrycount); + for (int i=0; i sizeof(buf)) + len -= sizeof(buf); + src += sizeof(buf); + eep += sizeof(buf); + } + +printf_P(PSTR("** crc adr: 0x%.4x, crc: 0x%.4x\n"), + (unsigned int) (uint16_t *) CONFIG_ENV_OFFSET + offsetof(env_t,crc), crc); + + eeprom_update_word((uint16_t *) CONFIG_ENV_OFFSET + offsetof(env_t,crc), crc); + +/**/ dump_eep(0, 128); + return 0; +} + + +int env_check(void) +{ + uint16_t crc, stored_crc; + uint16_t i; + char c, c0; + + debug("\n\n** env_check()\n"); + + + /* read old CRC */ + stored_crc = eeprom_read_word((const uint16_t *) CONFIG_ENV_OFFSET + + offsetof(env_t, crc)); + crc = 0xffff; + c = 0xff; + for (i = offsetof(env_t, data); + !(c == 0 && c0 == 0) && i < CONFIG_ENV_SIZE; + i++) + { + c0 = c; + c = eeprom_read_byte((const uint8_t *) CONFIG_ENV_OFFSET + i); + crc = crc16(crc, c); + } + debug("** crc eep: 0x%.4x, crc new: 0x%.4x\n", stored_crc, crc); + + return crc == stored_crc; +} + +int env_init(void) +{ + char name[CONFIG_SYS_ENV_NAMELEN+1]; + uint16_t idx = 0; + int nlen; + env_item_t e; + + e.flags = EF_N_EEP | EF_V_EEP; + e.name.eep = idx; + while ((nlen = ee_name_get(name, &e)) != 0 && idx < ENV_SIZE) { + + if (entrycount <= CONFIG_ENVVAR_MAX) { + e.val.eep = idx + nlen + 1; + + env_list[entrycount++] = e; + + idx += nlen + 1; + while (env_get_char(idx++) != 0 && idx < ENV_SIZE) + ; + e.name.eep = idx; + } else { + debug("** Too many environment variables!\n"); + break; + } + } + qsort(env_list, entrycount, sizeof(env_item_t), comp_env_items); + + return 0; +} + + +/* + * Command interface: print one or all environment variables + * + * Returns -1 in case of error, or length of printed string + */ +static int env_print(char *name) +{ + int len = -1; + + if (name) { /* print a single name */ + + env_item_t *ep = envlist_search(name); + if (ep != NULL) + len = env_item_print(ep); + + } else { /* print whole list */ + len = 0; + for (int i = 0 ; i < entrycount; i++) { + len += env_item_print(&env_list[i]); + } + } + return len; +} + + + +int do_env_print(cmd_tbl_t *cmdtp, int flag, int argc, + char * const argv[]) +{ + int i; + int rcode = 0; + + (void) cmdtp; (void) flag; + + if (argc == 1) { + /* print all env vars */ + rcode = env_print(NULL); + if (rcode < 0) + return 1; + printf_P(PSTR("\nEnvironment size: %d/%d bytes\n"), + rcode, ENV_SIZE); + return 0; + } + + /* print selected env vars */ + for (i = 1; i < argc; ++i) { + int rc = env_print(argv[i]); + if (rc < 0) { + printf_P(PSTR("## Error: \"%s\" not defined\n"), argv[i]); + ++rcode; + } + } + + return rcode; +} + + +/* + * Set a new environment variable, + * or replace or delete an existing one. + */ +static int _do_env_set(int flag, int argc, char * const argv[]) +{ + int i, len; + char *name, *value, *s; + env_item_t e, *ep; + + (void) flag; + debug("Initial value for argc=%d\n", argc); + + name = argv[1]; + value = argv[2]; + + if (strchr(name, '=')) { + printf_P(PSTR("## Error: illegal character '='" + "in variable name \"%s\"\n"), name); + return 1; + } + if (strlen(name) > CONFIG_SYS_ENV_NAMELEN) { + printf_P(PSTR("## Error: Variable name \"%s\" too long. " + "(max %d characters)\n"), name, CONFIG_SYS_ENV_NAMELEN); + return 1; + } +/* + env_id++; +*/ + /* Delete only ? */ + if (argc < 3 || argv[2] == NULL) { + int rc = envlist_delete(name); + return !rc; + } + + /* + * Insert / replace new value + */ + for (i = 2, len = 0; i < argc; ++i) + len += strlen(argv[i]) + 1; + + value = xmalloc(len); + if (value == NULL) { + printf_P(PSTR("## Can't malloc %d bytes\n"), len); + return 1; + } + for (i = 2, s = value; i < argc; ++i) { + char *v = argv[i]; + + while ((*s++ = *v++) != '\0') + ; + *(s - 1) = ' '; + } + if (s != value) + *--s = '\0'; + + e.flags = EF_DIRTY; + e.name.ram = name; + e.val.ram = value; + ep = envlist_enter(&e); + if (!ep) { + printf_P(PSTR("## Error inserting \"%s\" variable.\n"), + name); + return 1; + } + + return 0; +} + +int setenv(const char *varname, const char *varvalue) +{ + const char * const argv[3] = { NULL, varname, varvalue }; + int argc = 3; + + if (varvalue == NULL || varvalue[0] == '\0') + --argc; + + return _do_env_set(0, argc, (char * const *)argv); +} + +#if 0 +/** + * Set an environment variable to an integer value + * + * @param varname Environment variable to set + * @param value Value to set it to + * @return 0 if ok, 1 on error + */ +int setenv_ulong(const char *varname, unsigned long value) +{ + /* TODO: this should be unsigned */ + char *str = simple_itoa(value); + + return setenv(varname, str); +} +#endif + + +/** + * Set an environment variable to an value in hex + * + * @param varname Environment variable to set + * @param value Value to set it to + * @return 0 if ok, 1 on error + */ +int setenv_hex(const char *varname, unsigned long value) +{ + char str[sizeof(unsigned long) *2 + 1]; + + sprintf_P(str, PSTR("%lx"), value); + return setenv(varname, str); +} + +unsigned long getenv_hex(const char *varname, unsigned long default_val) +{ + const char *s; + unsigned long value; + char *endp; + + s = getenv(varname); + if (s) + value = strtoul(s, &endp, 16); + if (!s || endp == s) + return default_val; + + return value; +} + +int do_env_set(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + (void) cmdtp; + + if (argc < 2) + return CMD_RET_USAGE; + + return _do_env_set(flag, argc, argv); +} + + +#if defined(CONFIG_AUTO_COMPLETE) +int env_complete(char *var, int maxv, char *cmdv[], int bufsz, char *buf) +{ + ENTRY *match; + int found, idx; + + idx = 0; + found = 0; + cmdv[0] = NULL; + + while ((idx = hmatch_r(var, idx, &match, &env_htab))) { + int vallen = strlen(match->key) + 1; + + if (found >= maxv - 2 || bufsz < vallen) + break; + + cmdv[found++] = buf; + memcpy(buf, match->key, vallen); + buf += vallen; + bufsz -= vallen; + } + + qsort(cmdv, found, sizeof(cmdv[0]), strcmp_compar); + + if (idx) + cmdv[found++] = "..."; + + cmdv[found] = NULL; + return found; +} +#endif + diff --git a/avr/env.h b/avr/env.h new file mode 100644 index 0000000..ac66846 --- /dev/null +++ b/avr/env.h @@ -0,0 +1,12 @@ +#ifndef ENV_H +#define ENV_H + +int set_default_env(void); +int env_check(void); +int env_init(void); + +char *getenv(const char *name); +int env_complete(char *var, int maxv, char *cmdv[], int maxsz, char *buf); + +#endif /* ENV_H */ + diff --git a/avr/serial.c b/avr/serial.c index 2d2c551..b2fea51 100644 --- a/avr/serial.c +++ b/avr/serial.c @@ -106,7 +106,7 @@ int serial_getc(void) return ring_read_ch(&rx_ring); } -void serial_putc(uint8_t data) +void serial_putc(char data) { while (ring_write_ch(&tx_ring, data) < 0) ; @@ -115,3 +115,9 @@ void serial_putc(uint8_t data) UCSR0B = _BV(RXCIE0) | _BV(RXEN0) | _BV(TXEN0) | _BV(UDRIE0); } +uint_fast8_t serial_tstc(void) +{ + return !ring_is_empty(&rx_ring); +} + + diff --git a/avr/serial.h b/avr/serial.h index 1a0f510..7414ef1 100644 --- a/avr/serial.h +++ b/avr/serial.h @@ -2,7 +2,8 @@ #define SERIAL_H void serial_setup(void); -void serial_putc(uint8_t); +void serial_putc(char); int serial_getc(void); +uint_fast8_t serial_tstc(void); #endif /* SERIAL_H */ diff --git a/avr/timer.c b/avr/timer.c new file mode 100644 index 0000000..84a9737 --- /dev/null +++ b/avr/timer.c @@ -0,0 +1,87 @@ +/* + */ + + +#include "common.h" + +//#include +//#include +#include +#include + +//#include + + +#include "timer.h" + +/* timer interrupt/overflow counter */ +static volatile uint32_t timestamp; + + +/*---------------------------------------------------------*/ +/* 1000Hz timer interrupt generated by OC1A */ +/*---------------------------------------------------------*/ + +ISR(TIMER1_COMPA_vect) +{ + static int_fast8_t tick_10ms; + int_fast8_t i; + + + timestamp++; + + i = tick_10ms + 1; + if (i == 10) { + i = 0; + Stat |= S_10MS_TO; + + /* Drive timer procedure of low level disk I/O module */ + //disk_timerproc(); + } + tick_10ms = i; + +} + + + +/*--------------------------------------------------------------------------*/ + +#if 0 + +void do_10ms(void) +{ + if (to_counter) + to_counter--; +} + +#endif + +/*--------------------------------------------------------------------------*/ + + +void timer_setup(void) +{ + + /* Clock */ + CLKPR = _BV(CLKPCE); + CLKPR = 0; + + /* Timer */ + + OCR1A = F_CPU / 1000 - 1; // Timer1: 1000Hz interval (OC1A) + TCCR1B = 0b00001001; + TIMSK1 = _BV(OCIE1A); // Enable TC1.oca interrupt +} + + +uint32_t get_timer(uint32_t base) +{ + uint32_t ret; + + ATOMIC_BLOCK(ATOMIC_FORCEON) + { + ret = timestamp; + } + return ret - base; +} + diff --git a/avr/timer.h b/avr/timer.h new file mode 100644 index 0000000..bed3eb0 --- /dev/null +++ b/avr/timer.h @@ -0,0 +1,7 @@ +#ifndef TIMER_H +#define TIMER_H + +uint32_t get_timer(uint32_t); + +#endif /* TIMER_H */ + diff --git a/avr/xmalloc.c b/avr/xmalloc.c new file mode 100644 index 0000000..9bf2684 --- /dev/null +++ b/avr/xmalloc.c @@ -0,0 +1,27 @@ +#include + +#include "debug.h" +#include "xmalloc.h" + +void* xmalloc(size_t size) +{ + void *p; + + p = malloc(size); + + if (p == NULL) + debug("*** Out of memory!\n"); + + return p; +} + + +void* xrealloc(void *p, size_t size) +{ + p = realloc(p, size); + + if (p == NULL) + debug("*** Out of memory!\n"); + + return p; +} diff --git a/avr/xmalloc.h b/avr/xmalloc.h new file mode 100644 index 0000000..cb0019f --- /dev/null +++ b/avr/xmalloc.h @@ -0,0 +1,8 @@ + +#ifndef XMALLOC_H +#define XMALLOC_H + +void* xmalloc(size_t size); +void* xrealloc(void *p, size_t size); + +#endif /* XMALLOC_H */ diff --git a/avr/z180-stamp-avr.c b/avr/z180-stamp-avr.c index 09f323c..b5a3fb8 100644 --- a/avr/z180-stamp-avr.c +++ b/avr/z180-stamp-avr.c @@ -242,13 +242,13 @@ const FLASH struct msg_item z80_messages[] = { { 0, /* fct nr. */ 0, 0, /* sub fct nr. from, to */ - &do_msg_ini_msgfifo}, + do_msg_ini_msgfifo}, { 0, 1, 2, - &do_msg_ini_memfifo}, + do_msg_ini_memfifo}, { 1, 1, 1, - &do_msg_char_out}, + do_msg_char_out}, { 0xff, /* end mark */ 0, 0, 0}, diff --git a/configs/debug.config b/configs/debug.config index 04be440..1a54f76 100644 --- a/configs/debug.config +++ b/configs/debug.config @@ -1 +1 @@ -CONFIG_DEBUG=2 +CONFIG_DEBUG=0 diff --git a/configs/gcc.tup b/configs/gcc.tup new file mode 100644 index 0000000..ad2d788 --- /dev/null +++ b/configs/gcc.tup @@ -0,0 +1,40 @@ +CC = $(TOOLCHAIN)-gcc +LD = $(TOOLCHAIN)-gcc +AR = $(TOOLCHAIN)-ar +AS = $(TOOLCHAIN)-as +OBJCOPY = $(TOOLCHAIN)-objcopy +OBJDUMP = $(TOOLCHAIN)-objdump +SIZE = $(TOOLCHAIN)-size +GDB = $(TOOLCHAIN)-gdb + + +CFLAGS += -g -Os +CFLAGS += -std=gnu99 +CFLAGS += -Wall -Wextra +CFLAGS += -Wredundant-decls +#CFLAGS += -fno-common -ffunction-sections -fdata-sections + + +LDFLAGS += -Wl,--gc-sections +LDFLAGS += -Wl,--cref + +ifneq ($(LDSCRIPT),) +LDFLAGS += -T$(LDSCRIPT) +endif + + +!cc = |> ^ CC %f^ $(CC) $(CFLAGS) $(CPPFLAGS) -c %f -o %o |> %B.o +!LINK = |> ^ LINK %o^ $(LD) $(CFLAGS) $(LDFLAGS) -Wl,-Map=%O.map %f $(LDLIBS) -o %o |> | %O.map +!OBJCOPY= |> ^ OBJCOPY %o^ $(OBJCOPY) -Oihex %f %o |> +!OBJDUMP= |> ^ OBJDUMP %o^ $(OBJDUMP) -h -S %f > %o |> %O.lss +!SIZE = |> ^ SIZE %f^ $(SIZE) %f |> + + +: foreach $(SRC) | $(PREDEP) |> !cc |> {objs} +: $(SRC_Z) |> !cc $(CPPFLAGS_Z) |> {objs} + +: {objs} |> !LINK |> $(PROG).elf {elf} +: {elf} |> !OBJCOPY |> %B.hex {aux} +: {elf} |> !OBJDUMP |> %B.lss {aux} +: {elf} | {aux} |> !SIZE |> + -- 2.39.2