expo: Add a configuration editor

Add a new 'cedit' command which allows editing configuration using an
expo. The configuration items appear as menus on the display.

This is extremely basic, only supporting menus and not providing any way
to load or save the configuration.

Signed-off-by: Simon Glass <sjg@chromium.org>
This commit is contained in:
Simon Glass 2023-06-01 10:23:02 -06:00 committed by Tom Rini
parent 82cafee133
commit a0874dc4ac
14 changed files with 370 additions and 4 deletions

View file

@ -1630,4 +1630,18 @@ config SAVE_PREV_BL_INITRAMFS_START_ADDR
If no initramfs was provided by previous bootloader, no env variables
will be created.
menu "Configuration editor"
config CEDIT
bool "Configuration editor"
depends on BOOTSTD
help
Provides a way to deal with board configuration and present it to
the user for adjustment.
This is intended to provide both graphical and text-based user
interfaces, but only graphical is support at present.
endmenu # Configuration editor
endmenu # Booting

View file

@ -33,6 +33,7 @@ ifdef CONFIG_$(SPL_TPL_)BOOTSTD_FULL
obj-$(CONFIG_CMD_BOOTEFI_BOOTMGR) += bootmeth_efi_mgr.o
obj-$(CONFIG_$(SPL_TPL_)EXPO) += bootflow_menu.o
obj-$(CONFIG_$(SPL_TPL_)BOOTSTD) += bootflow_menu.o
obj-$(CONFIG_$(SPL_TPL_)CEDIT) += cedit.o
endif
obj-$(CONFIG_$(SPL_TPL_)OF_LIBFDT) += image-fdt.o

163
boot/cedit.c Normal file
View file

@ -0,0 +1,163 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Implementation of configuration editor
*
* Copyright 2023 Google LLC
* Written by Simon Glass <sjg@chromium.org>
*/
#include <common.h>
#include <cli.h>
#include <dm.h>
#include <expo.h>
#include <menu.h>
#include <video.h>
#include <linux/delay.h>
#include "scene_internal.h"
int cedit_arange(struct expo *exp, struct video_priv *vpriv, uint scene_id)
{
struct scene_obj_txt *txt;
struct scene_obj *obj;
struct scene *scn;
int y;
scn = expo_lookup_scene_id(exp, scene_id);
if (!scn)
return log_msg_ret("scn", -ENOENT);
txt = scene_obj_find_by_name(scn, "prompt");
if (txt)
scene_obj_set_pos(scn, txt->obj.id, 0, vpriv->ysize - 50);
txt = scene_obj_find_by_name(scn, "title");
if (txt)
scene_obj_set_pos(scn, txt->obj.id, 200, 10);
y = 100;
list_for_each_entry(obj, &scn->obj_head, sibling) {
if (obj->type == SCENEOBJT_MENU) {
scene_obj_set_pos(scn, obj->id, 50, y);
scene_menu_arrange(scn, (struct scene_obj_menu *)obj);
y += 50;
}
}
return 0;
}
int cedit_run(struct expo *exp)
{
struct cli_ch_state s_cch, *cch = &s_cch;
struct video_priv *vid_priv;
uint scene_id;
struct udevice *dev;
struct scene *scn;
bool done;
int ret;
cli_ch_init(cch);
/* For now we only support a video console */
ret = uclass_first_device_err(UCLASS_VIDEO, &dev);
if (ret)
return log_msg_ret("vid", ret);
ret = expo_set_display(exp, dev);
if (ret)
return log_msg_ret("dis", ret);
ret = expo_first_scene_id(exp);
if (ret < 0)
return log_msg_ret("scn", ret);
scene_id = ret;
ret = expo_set_scene_id(exp, scene_id);
if (ret)
return log_msg_ret("sid", ret);
exp->popup = true;
/* This is not supported for now */
if (0)
expo_set_text_mode(exp, true);
vid_priv = dev_get_uclass_priv(dev);
scn = expo_lookup_scene_id(exp, scene_id);
scene_highlight_first(scn);
cedit_arange(exp, vid_priv, scene_id);
ret = expo_calc_dims(exp);
if (ret)
return log_msg_ret("dim", ret);
done = false;
do {
struct expo_action act;
int ichar, key;
ret = expo_render(exp);
if (ret)
break;
ichar = cli_ch_process(cch, 0);
if (!ichar) {
while (!ichar && !tstc()) {
schedule();
mdelay(2);
ichar = cli_ch_process(cch, -ETIMEDOUT);
}
if (!ichar) {
ichar = getchar();
ichar = cli_ch_process(cch, ichar);
}
}
key = 0;
if (ichar) {
key = bootmenu_conv_key(ichar);
if (key == BKEY_NONE)
key = ichar;
}
if (!key)
continue;
ret = expo_send_key(exp, key);
if (ret)
break;
ret = expo_action_get(exp, &act);
if (!ret) {
switch (act.type) {
case EXPOACT_POINT_OBJ:
scene_set_highlight_id(scn, act.select.id);
cedit_arange(exp, vid_priv, scene_id);
break;
case EXPOACT_OPEN:
scene_set_open(scn, act.select.id, true);
cedit_arange(exp, vid_priv, scene_id);
break;
case EXPOACT_CLOSE:
scene_set_open(scn, act.select.id, false);
cedit_arange(exp, vid_priv, scene_id);
break;
case EXPOACT_SELECT:
scene_set_open(scn, scn->highlight_id, false);
cedit_arange(exp, vid_priv, scene_id);
break;
case EXPOACT_QUIT:
log_debug("quitting\n");
done = true;
break;
default:
break;
}
}
} while (!done);
if (ret)
return log_msg_ret("end", ret);
return 0;
}

View file

@ -376,7 +376,8 @@ int expo_build(ofnode root, struct expo **expp)
ret = read_strings(&info, root);
if (ret)
return log_msg_ret("str", ret);
list_strings(&info);
if (_DEBUG)
list_strings(&info);
ret = expo_new("name", NULL, &exp);
if (ret)

View file

@ -92,6 +92,18 @@ void *scene_obj_find(struct scene *scn, uint id, enum scene_obj_t type)
return NULL;
}
void *scene_obj_find_by_name(struct scene *scn, const char *name)
{
struct scene_obj *obj;
list_for_each_entry(obj, &scn->obj_head, sibling) {
if (!strcmp(name, obj->name))
return obj;
}
return NULL;
}
int scene_obj_add(struct scene *scn, const char *name, uint id,
enum scene_obj_t type, uint size, struct scene_obj **objp)
{

View file

@ -40,6 +40,14 @@ uint resolve_id(struct expo *exp, uint id);
*/
void *scene_obj_find(struct scene *scn, uint id, enum scene_obj_t type);
/**
* scene_obj_find_by_name() - Find an object in a scene by name
*
* @scn: Scene to search
* @name: Name to search for
*/
void *scene_obj_find_by_name(struct scene *scn, const char *name);
/**
* scene_obj_add() - Add a new object to a scene
*

View file

@ -428,6 +428,15 @@ config CMD_ABOOTIMG
See doc/android/boot-image.rst for details.
config CMD_CEDIT
bool "cedit - Configuration editor"
depends on CEDIT
default y
help
Provides a command to allow editing of board configuration and
providing a UI for the user to adjust settings. Subcommands allow
loading and saving of configuration as well as showing an editor.
config CMD_ELF
bool "bootelf, bootvx"
default y

View file

@ -43,6 +43,7 @@ obj-$(CONFIG_CMD_BUTTON) += button.o
obj-$(CONFIG_CMD_CAT) += cat.o
obj-$(CONFIG_CMD_CACHE) += cache.o
obj-$(CONFIG_CMD_CBFS) += cbfs.o
obj-$(CONFIG_CMD_CEDIT) += cedit.o
obj-$(CONFIG_CMD_CLK) += clk.o
obj-$(CONFIG_CMD_CLS) += cls.o
obj-$(CONFIG_CMD_CONFIG) += config.o

93
cmd/cedit.c Normal file
View file

@ -0,0 +1,93 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* 'cedit' command
*
* Copyright 2023 Google LLC
* Written by Simon Glass <sjg@chromium.org>
*/
#include <common.h>
#include <command.h>
#include <expo.h>
#include <fs.h>
#include <dm/ofnode.h>
#include <linux/sizes.h>
struct expo *cur_exp;
static int do_cedit_load(struct cmd_tbl *cmdtp, int flag, int argc,
char *const argv[])
{
const char *fname;
struct expo *exp;
oftree tree;
ulong size;
void *buf;
int ret;
if (argc < 4)
return CMD_RET_USAGE;
fname = argv[3];
ret = fs_load_alloc(argv[1], argv[2], argv[3], SZ_1M, 0, &buf, &size);
if (ret) {
printf("File not found\n");
return CMD_RET_FAILURE;
}
tree = oftree_from_fdt(buf);
if (!oftree_valid(tree)) {
printf("Cannot create oftree\n");
return CMD_RET_FAILURE;
}
ret = expo_build(oftree_root(tree), &exp);
oftree_dispose(tree);
if (ret) {
printf("Failed to build expo: %dE\n", ret);
return CMD_RET_FAILURE;
}
cur_exp = exp;
return 0;
}
static int do_cedit_run(struct cmd_tbl *cmdtp, int flag, int argc,
char *const argv[])
{
ofnode node;
int ret;
if (!cur_exp) {
printf("No expo loaded\n");
return CMD_RET_FAILURE;
}
node = ofnode_path("/cedit-theme");
if (ofnode_valid(node)) {
ret = expo_apply_theme(cur_exp, node);
if (ret)
return CMD_RET_FAILURE;
} else {
log_warning("No theme found\n");
}
ret = cedit_run(cur_exp);
if (ret) {
log_err("Failed (err=%dE)\n", ret);
return CMD_RET_FAILURE;
}
return 0;
}
#ifdef CONFIG_SYS_LONGHELP
static char cedit_help_text[] =
"load <interface> <dev[:part]> <filename> - load config editor\n"
"cedit run - run config editor";
#endif /* CONFIG_SYS_LONGHELP */
U_BOOT_CMD_WITH_SUBCMDS(cedit, "Configuration editor", cedit_help_text,
U_BOOT_SUBCMD_MKENT(load, 5, 1, do_cedit_load),
U_BOOT_SUBCMD_MKENT(run, 1, 1, do_cedit_run),
);

View file

@ -30,6 +30,7 @@ CONFIG_AUTOBOOT_STOP_STR_ENABLE=y
CONFIG_AUTOBOOT_STOP_STR_CRYPT="$5$rounds=640000$HrpE65IkB8CM5nCL$BKT3QdF98Bo8fJpTr9tjZLZQyzqPASBY20xuK5Rent9"
CONFIG_IMAGE_PRE_LOAD=y
CONFIG_IMAGE_PRE_LOAD_SIG=y
CONFIG_CEDIT=y
CONFIG_CONSOLE_RECORD=y
CONFIG_CONSOLE_RECORD_OUT_SIZE=0x6000
CONFIG_PRE_CONSOLE_BUFFER=y

View file

@ -171,8 +171,6 @@ menu-inset
menuitem-gap-y
Number of pixels between menu items
.. _expo_format:
Pop-up mode
-----------
@ -202,6 +200,8 @@ just as its title and label, as with the `CPU Speed` and `AC Power` menus here::
UP and DOWN to choose, ENTER to select
.. _expo_format:
Expo Format
-----------
@ -211,7 +211,8 @@ makes it easier and faster to create and edit the description. An expo builder
is provided to convert this format into an expo structure.
Layout of the expo scenes is handled automatically, based on a set of simple
rules.
rules. The :doc:`../usage/cmd/cedit` can be used to load a configuration
and create an expo from it.
Top-level node
~~~~~~~~~~~~~~
@ -464,6 +465,7 @@ Some ideas for future work:
- Support unicode
- Support curses for proper serial-terminal menus
- Add support for large menus which need to scroll
- Add support for reading and writing configuration settings with cedit
.. Simon Glass <sjg@chromium.org>
.. 7-Oct-22

31
doc/usage/cmd/cedit.rst Normal file
View file

@ -0,0 +1,31 @@
.. SPDX-License-Identifier: GPL-2.0+:
cedit command
=============
Synopis
-------
::
cedit load <interface> <dev[:part]> <filename>
cedit run
Description
-----------
The *cedit* command is used to load a configuration-editor description and allow
the user to interact with it.
It makes use of the expo subsystem.
The description is in the form of a devicetree file, as documented at
:ref:`expo_format`.
Example
-------
::
=> cedit load hostfs - fred.dtb
=> cedit run

View file

@ -39,6 +39,7 @@ Shell commands
cmd/bootz
cmd/cat
cmd/cbsysinfo
cmd/cedit
cmd/cls
cmd/cmp
cmd/coninfo

View file

@ -11,6 +11,7 @@
#include <linux/list.h>
struct udevice;
struct video_priv;
/**
* enum expoact_type - types of actions reported by the expo
@ -378,6 +379,14 @@ int expo_calc_dims(struct expo *exp);
*/
int expo_set_scene_id(struct expo *exp, uint scene_id);
/**
* expo_first_scene_id() - Get the ID of the first scene
*
* @exp: Expo to check
* Returns: Scene ID of first scene, or -ENOENT if there are no scenes
*/
int expo_first_scene_id(struct expo *exp);
/**
* expo_render() - render the expo on the display / console
*
@ -667,4 +676,24 @@ int expo_apply_theme(struct expo *exp, ofnode node);
*/
int expo_build(ofnode root, struct expo **expp);
/**
* cedit_arange() - Arrange objects in a configuration-editor scene
*
* @exp: Expo to update
* @vid_priv: Private info of the video device
* @scene_id: scene ID to arrange
* Returns: 0 if OK, -ve on error
*/
int cedit_arange(struct expo *exp, struct video_priv *vid_priv, uint scene_id);
/**
* cedit_run() - Run a configuration editor
*
* This accepts input until the user quits with Escape
*
* @exp: Expo to use
* Returns: 0 if OK, -ve on error
*/
int cedit_run(struct expo *exp);
#endif /*__SCENE_H */