From patchwork Thu Nov 1 11:39:11 2012 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Pali_Roh=C3=A1r?= X-Patchwork-Id: 196171 X-Patchwork-Delegate: agust@denx.de Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from theia.denx.de (theia.denx.de [85.214.87.163]) by ozlabs.org (Postfix) with ESMTP id ACDE72C0326 for ; Thu, 1 Nov 2012 22:40:19 +1100 (EST) Received: from localhost (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id 43D4F4A56A; Thu, 1 Nov 2012 12:39:59 +0100 (CET) X-Virus-Scanned: Debian amavisd-new at theia.denx.de Received: from theia.denx.de ([127.0.0.1]) by localhost (theia.denx.de [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id xY5ckrjeAFuA; Thu, 1 Nov 2012 12:39:59 +0100 (CET) Received: from theia.denx.de (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id 519E54A57F; Thu, 1 Nov 2012 12:39:37 +0100 (CET) Received: from localhost (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id 8DBE04A562 for ; Thu, 1 Nov 2012 12:39:34 +0100 (CET) X-Virus-Scanned: Debian amavisd-new at theia.denx.de Received: from theia.denx.de ([127.0.0.1]) by localhost (theia.denx.de [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id gp0UgI0lZqEE for ; Thu, 1 Nov 2012 12:39:32 +0100 (CET) X-policyd-weight: NOT_IN_SBL_XBL_SPAMHAUS=-1.5 NOT_IN_SPAMCOP=-1.5 NOT_IN_BL_NJABL=-1.5 (only DNSBL check requested) Received: from mail-ee0-f44.google.com (mail-ee0-f44.google.com [74.125.83.44]) by theia.denx.de (Postfix) with ESMTPS id 424654A563 for ; Thu, 1 Nov 2012 12:39:29 +0100 (CET) Received: by mail-ee0-f44.google.com with SMTP id d4so1397704eek.3 for ; Thu, 01 Nov 2012 04:39:28 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=from:to:cc:subject:date:message-id:x-mailer:in-reply-to:references :mime-version:content-type:content-transfer-encoding; bh=tVGThFt6M8d5Zzd9fA2uIeYXeCspbOp/uL7SdDJjrsA=; b=MO7LAouBPO7i+wzfgDZhMzaM/5LveeNN6+GmyIkX9/6vAhoytQfLFJQK1/UJZa9miI V0zkYI+B7aIm7MEhtJkjzcN3xw2rqLglWyjR9kZdSh41Rzubyc9ITDpecYnTtAXcTcHy rKRw886JT/7ZWS/qZjELb3WCp2pEInU0DQPUHgWZDbqbkeBNNyzcReaRWx3/zXkN6TQU 7VbHopsTOfWD5uuQIK+a78px3I0sxiIlilHoO67tMk7u1aw4VvGLAp3DV7tTXFaUI5al 4zGuyAEP8F/zK8e0cviYgP1wzdDnWDvgeWIIVPn9TwGIya3f0h5XiEBQK0U8RLmDBtQx fo0A== Received: by 10.14.173.195 with SMTP id v43mr95429700eel.39.1351769968871; Thu, 01 Nov 2012 04:39:28 -0700 (PDT) Received: from Pali-EliteBook.kolej.mff.cuni.cz (pali.kolej.mff.cuni.cz. [78.128.193.202]) by mx.google.com with ESMTPS id d44sm13984543eeo.10.2012.11.01.04.39.27 (version=TLSv1/SSLv3 cipher=OTHER); Thu, 01 Nov 2012 04:39:28 -0700 (PDT) From: =?UTF-8?q?Pali=20Roh=C3=A1r?= To: u-boot@lists.denx.de Date: Thu, 1 Nov 2012 12:39:11 +0100 Message-Id: <1351769953-13560-3-git-send-email-pali.rohar@gmail.com> X-Mailer: git-send-email 1.7.10.4 In-Reply-To: <1351769953-13560-1-git-send-email-pali.rohar@gmail.com> References: <1338136729-3907-1-git-send-email-pali.rohar@gmail.com> <1351769953-13560-1-git-send-email-pali.rohar@gmail.com> MIME-Version: 1.0 Cc: =?UTF-8?q?Pali=20Roh=C3=A1r?= Subject: [U-Boot] [PATCH v2 2/4] New command bootmenu: ANSI terminal Boot Menu support X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.11 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: u-boot-bounces@lists.denx.de Errors-To: u-boot-bounces@lists.denx.de This patch adding ANSI terminal bootmenu command. It is extension to generic menu which provide output for ANSI terminals. Signed-off-by: Pali Rohár --- Changes in v2: - Added commit message - Removed bootmenu from include/config_cmd_all.h - Moved ANSI escape codes from include/common.h to include/ansi.h - Fixed style and indentation problems - Use mdelay instead udelay - Removed autoboot delay message when some key is pressed common/Makefile | 1 + common/cmd_bootmenu.c | 471 +++++++++++++++++++++++++++++++++++++++++++++++++ doc/README.bootmenu | 61 +++++++ include/ansi.h | 42 +++++ 4 files changed, 575 insertions(+) create mode 100644 common/cmd_bootmenu.c create mode 100644 doc/README.bootmenu create mode 100644 include/ansi.h diff --git a/common/Makefile b/common/Makefile index a4eb477..1435992 100644 --- a/common/Makefile +++ b/common/Makefile @@ -67,6 +67,7 @@ COBJS-$(CONFIG_CMD_SOURCE) += cmd_source.o COBJS-$(CONFIG_CMD_BDI) += cmd_bdinfo.o COBJS-$(CONFIG_CMD_BEDBUG) += bedbug.o cmd_bedbug.o COBJS-$(CONFIG_CMD_BMP) += cmd_bmp.o +COBJS-$(CONFIG_CMD_BOOTMENU) += cmd_bootmenu.o COBJS-$(CONFIG_CMD_BOOTLDR) += cmd_bootldr.o COBJS-$(CONFIG_CMD_CACHE) += cmd_cache.o COBJS-$(CONFIG_CMD_CONSOLE) += cmd_console.o diff --git a/common/cmd_bootmenu.c b/common/cmd_bootmenu.c new file mode 100644 index 0000000..908e13f --- /dev/null +++ b/common/cmd_bootmenu.c @@ -0,0 +1,471 @@ +/* + * (C) Copyright 2011-2012 Pali Rohár + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +struct bootmenu_entry { + int num; /* unique number 0..99 */ + char key[3]; /* key idetifier of number */ + char *title; /* title of entry */ + char *command; /* hush command of entry */ + struct bootmenu_data *menu; /* this bootmenu */ + struct bootmenu_entry *next; /* next menu entry (num+1) */ +}; + +struct bootmenu_data { + int delay; /* delay for autoboot */ + int active; /* active menu entry */ + int count; /* total count of menu entries */ + struct bootmenu_entry *first; /* first menu entry */ +}; + +static char *bootmenu_getoption(int n) +{ + char name[] = "bootmenu_\0\0"; + + if (n < 0 || n > 99) + return NULL; + + sprintf(name+9, "%d", n); + return getenv(name); +} + +static void bootmenu_print_entry(void *data) +{ + struct bootmenu_entry *entry = data; + int reverse = (entry->menu->active == entry->num); + + /* + * Move cursor to line where entry will be drown (entry->num) + * First 3 lines contains Bootmenu header + 1 empty line + */ + printf(ANSI_CURSOR_POSITION, entry->num + 4, 1); + + if (reverse) + puts(ANSI_COLOR_REVERSE); + + puts(" "); + puts(entry->title); + puts(ANSI_CLEAR_LINE_TO_END); + + if (reverse) + puts(ANSI_COLOR_RESET); +} + +static char *bootmenu_choice_entry(void *data) +{ + struct bootmenu_data *menu = data; + int i; + + int key = 0; /* 0 - NONE, 1 - UP, 2 - DOWN, 3 - SELECT */ + int esc = 0; + + while (1) { + + /* Autoboot was not stopped */ + if (menu->delay >= 0) { + + if (menu->delay > 0) { + printf(ANSI_CURSOR_POSITION, menu->count+5, 1); + printf(" Hit any key to stop autoboot: %2d ", + menu->delay); + } + + while (menu->delay > 0) { + + for (i = 0; i < 100; ++i) { + + if (!tstc()) { + WATCHDOG_RESET(); + mdelay(10); + continue; + } + + menu->delay = -1; + key = getc(); + + if (key == '\e') { + esc = 1; + key = 0; + } else if (key == '\r') + key = 3; + else + key = 0; + + break; + + } + + if (menu->delay < 0) + break; + + --menu->delay; + printf("\b\b\b%2d ", menu->delay); + + } + + printf(ANSI_CURSOR_POSITION, menu->count+5, 1); + puts(ANSI_CLEAR_LINE); + + if (menu->delay == 0) + key = 3; + + /* Some key was pressed, so autoboot was stopped */ + } else { + + while (!tstc()) { + WATCHDOG_RESET(); + mdelay(10); + } + + key = getc(); + + if (esc == 0) { + + /* Start of ANSI escape sequence */ + if (key == '\e') { + esc = 1; + key = 0; + } + + } else if (esc == 1) { + + if (key == '[') { + esc = 2; + key = 0; + } else + esc = 0; + + } else if (esc == 2 || esc == 3) { + + if (esc == 2 && key == '1') { + esc = 3; + key = 0; + } else + esc = 0; + + /* key up was pressed */ + if (key == 'A') + key = 1; + /* key down was pressed */ + else if (key == 'B') + key = 2; + /* other key was pressed */ + else + key = 0; + + } + + /* enter key was pressed */ + if (key == '\r') + key = 3; + + } + + /* key up */ + if (key == 1) { + if (menu->active > 0) + --menu->active; + return ""; /* invalid menu entry, regenerate menu */ + /* key down */ + } else if (key == 2) { + if (menu->active < menu->count-1) + ++menu->active; + return ""; /* invalid menu entry, regenerate menu */ + /* enter */ + } else if (key == 3) { + int i; + struct bootmenu_entry *iter = menu->first; + for (i = 0; i < menu->active; ++i) + iter = iter->next; + return iter->key; + } + + } + + /* never happends */ + debug("bootmenu: this should not happen"); + return NULL; +} + +static struct bootmenu_data *bootmenu_create(int delay) +{ + int i = 0; + const char *option; + struct bootmenu_data *menu; + struct bootmenu_entry *iter = NULL; + + int len; + char *sep; + struct bootmenu_entry *entry; + + menu = malloc(sizeof(struct bootmenu_data)); + if (!menu) + return NULL; + + menu->delay = delay; + menu->active = 0; + menu->first = NULL; + + while ((option = bootmenu_getoption(i))) { + + sep = strchr(option, '='); + if (!sep) + break; + + entry = malloc(sizeof(struct bootmenu_entry)); + if (!entry) + goto cleanup; + + len = sep-option; + entry->title = malloc(len+1); + if (!entry->title) { + free(entry); + goto cleanup; + } + memcpy(entry->title, option, len); + entry->title[len] = 0; + + len = strlen(sep+1); + entry->command = malloc(len+1); + if (!entry->command) { + free(entry->title); + free(entry); + goto cleanup; + } + memcpy(entry->command, sep+1, len); + entry->command[len] = 0; + + sprintf(entry->key, "%d", i); + + entry->num = i; + entry->menu = menu; + entry->next = NULL; + + if (!iter) + menu->first = entry; + else + iter->next = entry; + + iter = entry; + ++i; + + if (i >= 100) + break; + + } + + /* Add U-Boot console entry at the end */ + if (i < 100) { + entry = malloc(sizeof(struct bootmenu_entry)); + if (!entry) + goto cleanup; + + entry->title = strdup("U-Boot console"); + if (!entry->title) { + free(entry); + goto cleanup; + } + + entry->command = strdup(""); + if (!entry->command) { + free(entry->title); + free(entry); + goto cleanup; + } + + entry->num = i; + entry->menu = menu; + entry->next = NULL; + + if (!iter) + menu->first = entry; + else + iter->next = entry; + + iter = entry; + ++i; + + } + + menu->count = i; + return menu; + +cleanup: + iter = menu->first; + while (iter) { + entry = iter->next; + free(iter->title); + free(iter->command); + free(iter); + iter = entry; + } + free(menu); + return NULL; +} + +static void bootmenu_destroy(struct bootmenu_data *menu) +{ + struct bootmenu_entry *iter = menu->first; + struct bootmenu_entry *next; + while (iter) { + next = iter->next; + free(iter->title); + free(iter->command); + free(iter); + iter = next; + } + free(menu); +} + +static void bootmenu_show(int delay) +{ + int init = 0; + void *choice = NULL; + char *title = NULL; + char *command = NULL; + struct menu *menu; + struct bootmenu_data *bootmenu; + struct bootmenu_entry *iter; + char *option, *sep; + + /* If delay is 0 do not create menu, just run first entry */ + if (delay == 0) { + option = bootmenu_getoption(0); + if (!option) + return; + sep = strchr(option, '='); + if (!sep) + return; + run_command(sep+1, 0); + return; + } + + bootmenu = bootmenu_create(delay); + if (!bootmenu) + return; + + menu = menu_create(NULL, bootmenu->delay, (bootmenu->delay >= 0), + bootmenu_print_entry, bootmenu_choice_entry, bootmenu); + if (!menu) + return; + + for (iter = bootmenu->first; iter; iter = iter->next) + if (!menu_item_add(menu, iter->key, iter)) + goto cleanup; + + /* Default menu entry is always first */ + menu_default_set(menu, "0"); + + puts(ANSI_CURSOR_HIDE); + puts(ANSI_CLEAR_CONSOLE); + printf(ANSI_CURSOR_POSITION, 1, 1); + + init = 1; + + if (menu_get_choice(menu, &choice)) { + iter = choice; + title = strdup(iter->title); + command = strdup(iter->command); + } + +cleanup: + menu_destroy(menu); + bootmenu_destroy(bootmenu); + + if (init) { + puts(ANSI_CURSOR_SHOW); + puts(ANSI_CLEAR_CONSOLE); + printf(ANSI_CURSOR_POSITION, 1, 1); + } + + if (title && command) { + printf("Starting entry '%s'\n", title); + free(title); + run_command(command, 0); + free(command); + } + +#ifdef CONFIG_POSTBOOTMENU + run_command(CONFIG_POSTBOOTMENU, 0); +#endif +} + +void menu_display_statusline(void *data) +{ + struct bootmenu_data *menu = data; + + printf(ANSI_CURSOR_POSITION, 1, 1); + puts(ANSI_CLEAR_LINE); + printf(ANSI_CURSOR_POSITION, 2, 1); + puts(" *** U-Boot BOOT MENU ***"); + puts(ANSI_CLEAR_LINE_TO_END); + printf(ANSI_CURSOR_POSITION, 3, 1); + puts(ANSI_CLEAR_LINE); + + /* First 3 lines are bootmenu header + 2 empty lines between entries */ + printf(ANSI_CURSOR_POSITION, menu->count + 5, 1); + puts(ANSI_CLEAR_LINE); + printf(ANSI_CURSOR_POSITION, menu->count + 6, 1); + puts(" Press UP/DOWN to move, ENTER to select"); + puts(ANSI_CLEAR_LINE_TO_END); + printf(ANSI_CURSOR_POSITION, menu->count + 7, 1); + puts(ANSI_CLEAR_LINE); +} + +int menu_show(int bootdelay) +{ + bootmenu_show(bootdelay); + return -1; /* -1 - abort boot and run monitor code */ +} + +int do_bootmenu(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[]) +{ + char *delay_str = NULL; + int delay = 10; + + if (argc >= 2) + delay_str = argv[1]; + + if (!delay_str) + delay_str = getenv("bootmenu_delay"); + + if (delay_str) + delay = (int)simple_strtol(delay_str, NULL, 10); + + bootmenu_show(delay); + return 0; +} + +U_BOOT_CMD( + bootmenu, 2, 1, do_bootmenu, + "ANSI terminal bootmenu", + "[delay]\n" + " - show ANSI terminal bootmenu with autoboot delay (default 10s)" +); diff --git a/doc/README.bootmenu b/doc/README.bootmenu new file mode 100644 index 0000000..69ef93b --- /dev/null +++ b/doc/README.bootmenu @@ -0,0 +1,61 @@ +/* + * (C) Copyright 2011-2012 Pali Rohár + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +This is ANSI terminal BootMenu command. It is extension to generic menu, +which provide output for ANSI terminals. + +Configuration is done via env variables bootmenu_delay and bootmenu_: + + bootmenu_delay= + bootmenu_="=<commands>" + + (title and commands are separated by first char '=') + + <delay> is delay in seconds of autobooting first entry + <num> is boot menu entry, starting from zero + <title> is text shown in boot screen + <commands> are commands which will be executed when menu entry is selected + +First argument of bootmenu command override bootmenu_delay env +If env bootmenu_delay or bootmenu arg is not specified, delay is 10 seconds +If delay is 0, no entry will be shown on screen and first will be called +If delay is less then 0, no autoboot delay will be used +Boot Menu will stop finding next menu entry if last was not defined +Boot Menu always add menu entry "U-Boot console" at end of all entries + +Example using: + + setenv bootmenu_0 Boot 1. kernel=bootm 0x82000000 # Set first menu entry + setenv bootmenu_1 Boot 2. kernel=bootm 0x83000000 # Set second menu entry + setenv bootmenu_2 Reset board=reset # Set third menu entry + setenv bootmenu_3 U-Boot boot order=boot # Set fourth menu entry + setenv bootmenu_4 # Empty string is end of all bootmenu entries + bootmenu 20 # Run bootmenu with autoboot delay 20s + + +To enable ANSI bootmenu comamnd add these definitions to board code: + + #define CONFIG_BOOTDELAY 30 + #define CONFIG_AUTOBOOT_KEYED + #define CONFIG_MENU + #define CONFIG_MENU_SHOW + #define CONFIG_CMD_BOOTMENU diff --git a/include/ansi.h b/include/ansi.h new file mode 100644 index 0000000..0e40b1d --- /dev/null +++ b/include/ansi.h @@ -0,0 +1,42 @@ +/* + * (C) Copyright 2012 + * Pali Rohár <pali.rohar@gmail.com> + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +/* + * ANSI terminal + */ + +#define ANSI_CURSOR_UP "\e[%dA" +#define ANSI_CURSOR_DOWN "\e[%dB" +#define ANSI_CURSOR_FORWARD "\e[%dC" +#define ANSI_CURSOR_BACK "\e[%dD" +#define ANSI_CURSOR_NEXTLINE "\e[%dE" +#define ANSI_CURSOR_PREVIOUSLINE "\e[%dF" +#define ANSI_CURSOR_COLUMN "\e[%dG" +#define ANSI_CURSOR_POSITION "\e[%d;%dH" +#define ANSI_CURSOR_SHOW "\e[?25h" +#define ANSI_CURSOR_HIDE "\e[?25l" +#define ANSI_CLEAR_CONSOLE "\e[2J" +#define ANSI_CLEAR_LINE_TO_END "\e[0K" +#define ANSI_CLEAR_LINE "\e[2K" +#define ANSI_COLOR_RESET "\e[0m" +#define ANSI_COLOR_REVERSE "\e[7m"