From patchwork Thu Jun 23 18:27:33 2011 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jason Hobbs X-Patchwork-Id: 101668 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 87DA2B6F86 for ; Fri, 24 Jun 2011 04:29:02 +1000 (EST) Received: from localhost (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id 1A759280DF; Thu, 23 Jun 2011 20:28:38 +0200 (CEST) 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 GI8vkQafWDJH; Thu, 23 Jun 2011 20:28:37 +0200 (CEST) Received: from theia.denx.de (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id 21086280BC; Thu, 23 Jun 2011 20:28:18 +0200 (CEST) Received: from localhost (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id 41B8528086 for ; Thu, 23 Jun 2011 20:28:15 +0200 (CEST) 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 ONsHv8O2Addy for ; Thu, 23 Jun 2011 20:28:11 +0200 (CEST) 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 smtp125.dfw.emailsrvr.com (smtp125.dfw.emailsrvr.com [67.192.241.125]) by theia.denx.de (Postfix) with ESMTPS id A06202808D for ; Thu, 23 Jun 2011 20:28:00 +0200 (CEST) Received: from localhost (localhost.localdomain [127.0.0.1]) by smtp2.relay.dfw1a.emailsrvr.com (SMTP Server) with ESMTP id AF19B2A8420; Thu, 23 Jun 2011 14:27:56 -0400 (EDT) X-Virus-Scanned: OK Received: by smtp2.relay.dfw1a.emailsrvr.com (Authenticated sender: jason.hobbs-AT-calxeda.com) with ESMTPSA id 64E872A85C4; Thu, 23 Jun 2011 14:27:55 -0400 (EDT) Received: by jhobbs-laptop (sSMTP sendmail emulation); Thu, 23 Jun 2011 13:27:53 -0500 From: "Jason Hobbs" To: u-boot@lists.denx.de Date: Thu, 23 Jun 2011 13:27:33 -0500 Message-Id: <1308853655-12407-5-git-send-email-jason.hobbs@calxeda.com> X-Mailer: git-send-email 1.7.0.4 In-Reply-To: <1308853655-12407-1-git-send-email-jason.hobbs@calxeda.com> References: <1308853655-12407-1-git-send-email-jason.hobbs@calxeda.com> Cc: Jason Hobbs , wd@denx.edu Subject: [U-Boot] [PATCH v2 4/6] Add generic, reusable menu code X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.9 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Sender: u-boot-bounces@lists.denx.de Errors-To: u-boot-bounces@lists.denx.de This will be used first by the pxecfg code, but is intended to be generic and reusable for other jobs in U-boot. Signed-off-by: Jason Hobbs --- changes in v2: - new in v2 common/Makefile | 1 + common/menu.c | 338 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ include/menu.h | 40 +++++++ 3 files changed, 379 insertions(+), 0 deletions(-) create mode 100644 common/menu.c create mode 100644 include/menu.h diff --git a/common/Makefile b/common/Makefile index f81cff9..9b413cd 100644 --- a/common/Makefile +++ b/common/Makefile @@ -171,6 +171,7 @@ COBJS-$(CONFIG_CMD_KGDB) += kgdb.o kgdb_stubs.o COBJS-$(CONFIG_KALLSYMS) += kallsyms.o COBJS-$(CONFIG_LCD) += lcd.o COBJS-$(CONFIG_LYNXKDI) += lynxkdi.o +COBJS-$(CONFIG_MENU) += menu.o COBJS-$(CONFIG_MODEM_SUPPORT) += modem.o COBJS-$(CONFIG_UPDATE_TFTP) += update.o COBJS-$(CONFIG_USB_KEYBOARD) += usb_kbd.o diff --git a/common/menu.c b/common/menu.c new file mode 100644 index 0000000..e92d302 --- /dev/null +++ b/common/menu.c @@ -0,0 +1,338 @@ +/* + * Copyright 2010-2011 Calxeda, Inc. + * + * 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 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, see . + */ + +#include +#include +#include +#include + +#include "menu.h" + +struct menu_item { + char *key; + void *data; + struct list_head list; +}; + +struct menu { + struct menu_item *default_item; + int timeout; + char *title; + int prompt; + void (*item_data_print)(void *); + void (*item_data_destroy)(void *); + struct list_head items; +}; + +struct callback_with_data { + void *(*callback)(void *, void *); + void *data; +}; + +static inline void *menu_items_iter(struct menu *m, + void *(*callback)(struct menu *, struct menu_item *, void *), + void *extra) +{ + struct list_head *pos, *n; + struct menu_item *item; + void *ret; + + list_for_each_safe(pos, n, &m->items) { + item = list_entry(pos, struct menu_item, list); + + ret = callback(m, item, extra); + + if (ret) + return ret; + } + + return NULL; +} + +static inline void *menu_item_print(struct menu *m, + struct menu_item *item, + void *extra) +{ + if (!m->item_data_print) + printf("%s:\n", item->key); + else + m->item_data_print(item->data); + + return NULL; +} + +static inline void *menu_item_destroy(struct menu *m, + struct menu_item *item, + void *extra) +{ + if (m->item_data_destroy) + m->item_data_destroy(item->data); + + if (item->key) + free(item->key); + + free(item); + + return NULL; +} + +static inline void menu_display(struct menu *m) +{ + if (m->title) + printf("%s\n", m->title); + + menu_items_iter(m, menu_item_print, NULL); +} + +static inline void *menu_item_key_match(struct menu *m, + struct menu_item *item, + void *key) +{ + if (!key || !item->key) { + if (key == item) + return item; + + return NULL; + } + + if (strcmp(item->key, key) == 0) + return item; + + return NULL; +} + +static inline struct menu_item *menu_item_by_key(struct menu *m, char *key) +{ + return menu_items_iter(m, menu_item_key_match, key); +} + +static inline void *menu_item_data_cb(struct menu *m, + struct menu_item *item, + void *extra) +{ + + struct callback_with_data *cb_data = extra; + + return cb_data->callback(item->data, cb_data->data); +} + +static inline int delay_for_input(int timeout) +{ + if (!timeout) + return 0; + + if (abortboot(timeout/10)) { + printf("autoboot aborted!\n"); + return 1; + } + + return 0; +} + +void *menu_items_data_iter(struct menu *m, + void *(*callback)(void *, void *), + void *extra) +{ + struct callback_with_data cb_data; + + if (!m || !callback) + return NULL; + + cb_data.callback = callback; + cb_data.data = extra; + + return menu_items_iter(m, menu_item_data_cb, &cb_data); +} + +int menu_should_prompt(struct menu *m) +{ + if (!m) + return -EINVAL; + + return m->prompt || delay_for_input(m->timeout); +} + +int menu_default_set(struct menu *m, char *key) +{ + struct menu_item *item; + + if (!m) + return -EINVAL; + + item = menu_item_by_key(m, key); + + if (!item) + return -ENOENT; + + m->default_item = item; + + return 1; +} + +void *menu_default_choice(struct menu *m) +{ + if (!m) + return NULL; + + if (m->default_item) + return m->default_item->data; + + return NULL; +} + +void *menu_user_choice(struct menu *m) +{ + char cbuf[CONFIG_SYS_CBSIZE]; + struct menu_item *choice_item = NULL; + + if (!m) + return NULL; + + while (!choice_item) { + int readret; + + cbuf[0] = '\0'; + + menu_display(m); + + readret = readline_into_buffer("Enter choice: ", cbuf); + + if (readret >= 0) { + choice_item = menu_item_by_key(m, cbuf); + + if (!choice_item) + printf("%s not found\n", cbuf); + else + return choice_item->data; + } else { + printf("^C\n"); + return NULL; + } + } + + return NULL; +} + +/* + * note that this replaces the data of an item if it already exists, but + * doesn't change the order of the item. + */ +int menu_item_add(struct menu *m, char *item_key, void *item_data) +{ + struct menu_item *item; + + if (!m) + return -EINVAL; + + item = menu_item_by_key(m, item_key); + + if (item) { + m->item_data_destroy(item->data); + item->data = item_data; + return 1; + } + + item = malloc(sizeof *item); + + if (!item) + return -ENOMEM; + + item->key = strdup(item_key); + + if (!item->key) { + free(item); + return -ENOMEM; + } + + item->data = item_data; + + list_add_tail(&item->list, &m->items); + + return 1; +} + +struct menu *menu_create(void (*item_data_print)(void *), + void (*item_data_destroy)(void *)) +{ + struct menu *m; + + m = malloc(sizeof *m); + + if (!m) + return NULL; + + m->default_item = NULL; + m->timeout = 0; + m->title = NULL; + m->prompt = 0; + + m->item_data_print = item_data_print; + m->item_data_destroy = item_data_destroy; + + INIT_LIST_HEAD(&m->items); + + return m; +} + +int menu_title_set(struct menu *m, char *title) +{ + char *new_title; + + if (!m || !title) + return -EINVAL; + + new_title = strdup(title); + + if (!new_title) + return -ENOMEM; + + if (m->title) + free(m->title); + + m->title = new_title; + + return 1; +} + +void menu_destroy(struct menu *m) +{ + if (!m) + return; + + menu_items_iter(m, menu_item_destroy, NULL); + + if (m->title) + free(m->title); + + free(m); +} + +void menu_prompt_set(struct menu *m, int prompt) +{ + if (!m) + return; + + m->prompt = prompt; +} + +void menu_timeout_set(struct menu *m, int timeout) +{ + if (!m) + return; + + m->timeout = timeout; +} diff --git a/include/menu.h b/include/menu.h new file mode 100644 index 0000000..5ffb3b8 --- /dev/null +++ b/include/menu.h @@ -0,0 +1,40 @@ +/* + * Copyright 2010-2011 Calxeda, Inc. + * + * 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 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, see . + */ + +#ifndef __MENU_H__ +#define __MENU_H__ + +struct menu; +struct menu_item; + +void *menu_items_data_iter(struct menu *m, + void *(*callback)(void *, void *), + void *extra); + +int menu_should_prompt(struct menu *m); +int menu_default_set(struct menu *m, char *key); +void *menu_default_choice(struct menu *m); +void *menu_user_choice(struct menu *m); +int menu_item_add(struct menu *m, char *item_key, void *item_data); +struct menu *menu_create(void (*item_data_print)(void *), + void (*item_data_destroy)(void *)); +int menu_title_set(struct menu *m, char *title); +void menu_destroy(struct menu *m); +void menu_prompt_set(struct menu *m, int prompt); +void menu_timeout_set(struct menu *m, int timeout); + +#endif /* __MENU_H__ */