From 040b04685ea4c47a5148c2fcc2ff6dfdd83bc714 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 14 Aug 2023 16:40:25 -0600 Subject: expo: Split out cedit into its own header Before adding more functions to this interface, create a new header for the configuration editor. Fix up the expo header guard while we are here. Signed-off-by: Simon Glass --- cmd/cedit.c | 1 + 1 file changed, 1 insertion(+) (limited to 'cmd') diff --git a/cmd/cedit.c b/cmd/cedit.c index 0cae304c4ad..5f0e84403f5 100644 --- a/cmd/cedit.c +++ b/cmd/cedit.c @@ -7,6 +7,7 @@ */ #include +#include #include #include #include -- cgit v1.3.1 From 2045ca5c1f51d054579d0886184b6f245b8a134e Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 14 Aug 2023 16:40:30 -0600 Subject: expo: Move cedit theme under bootstd This is related to standard boot, so put it under the same node. This may simplify schema upstreaming later. Mention themes in the documentation while we are here. Signed-off-by: Simon Glass --- arch/sandbox/dts/sandbox.dtsi | 12 ++++++------ arch/sandbox/dts/test.dts | 12 ++++++------ cmd/cedit.c | 2 +- doc/develop/cedit.rst | 7 +++++++ 4 files changed, 20 insertions(+), 13 deletions(-) (limited to 'cmd') diff --git a/arch/sandbox/dts/sandbox.dtsi b/arch/sandbox/dts/sandbox.dtsi index 8aaf911a41a..ff7e5584c55 100644 --- a/arch/sandbox/dts/sandbox.dtsi +++ b/arch/sandbox/dts/sandbox.dtsi @@ -16,12 +16,6 @@ stdout-path = "/serial"; }; - cedit-theme { - font-size = <30>; - menu-inset = <3>; - menuitem-gap-y = <1>; - }; - alarm_wdt: alarm-wdt { compatible = "sandbox,alarm-wdt"; timeout-sec = <5>; @@ -36,6 +30,12 @@ bootstd { compatible = "u-boot,boot-std"; filename-prefixes = "./"; + + cedit-theme { + font-size = <30>; + menu-inset = <3>; + menuitem-gap-y = <1>; + }; }; buttons { diff --git a/arch/sandbox/dts/test.dts b/arch/sandbox/dts/test.dts index f351d5cb84b..084cb88a232 100644 --- a/arch/sandbox/dts/test.dts +++ b/arch/sandbox/dts/test.dts @@ -100,6 +100,12 @@ menuitem-gap-y = <1>; }; + cedit-theme { + font-size = <30>; + menu-inset = <3>; + menuitem-gap-y = <1>; + }; + /* * This is used for the VBE OS-request tests. A FAT filesystem * created in a partition with the VBE information appearing @@ -144,12 +150,6 @@ cedit: cedit { }; - cedit-theme { - font-size = <30>; - menu-inset = <3>; - menuitem-gap-y = <1>; - }; - fuzzing-engine { compatible = "sandbox,fuzzing-engine"; }; diff --git a/cmd/cedit.c b/cmd/cedit.c index 5f0e84403f5..e98121b067b 100644 --- a/cmd/cedit.c +++ b/cmd/cedit.c @@ -65,7 +65,7 @@ static int do_cedit_run(struct cmd_tbl *cmdtp, int flag, int argc, return CMD_RET_FAILURE; } - node = ofnode_path("/cedit-theme"); + node = ofnode_path("/bootstd/cedit-theme"); if (ofnode_valid(node)) { ret = expo_apply_theme(cur_exp, node); if (ret) diff --git a/doc/develop/cedit.rst b/doc/develop/cedit.rst index 48262ee535e..8f0a554ae91 100644 --- a/doc/develop/cedit.rst +++ b/doc/develop/cedit.rst @@ -145,3 +145,10 @@ Multiple scenes Expo supports multiple scenes but has no pre-determined way of moving between them. You could use selection of a menu item as a signal to change the scene, but this is not currently implemented in the cedit code (see `cedit_run()`). + + +Themes +------ + +The configuration editor uses simple expo themes. The theme is read from +`/bootstd/cedit-theme` in the devicetree. -- cgit v1.3.1 From 2dee81fe5f4a6427ba48fe17ff017930ddf3b4e4 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 14 Aug 2023 16:40:33 -0600 Subject: expo: cedit: Support writing settings to a file Support writing settings from an expo into a file in FDT format. It consists of a single node with a two properties for each sceneitem, one with tag ID chosen by the user and another for its text value. Signed-off-by: Simon Glass --- boot/cedit.c | 136 ++++++++++++++++++++++++++++++++++++++++++++++++ cmd/cedit.c | 51 ++++++++++++++++-- doc/usage/cmd/cedit.rst | 19 +++++++ include/cedit.h | 22 ++++++++ test/boot/cedit.c | 45 ++++++++++++++++ 5 files changed, 270 insertions(+), 3 deletions(-) (limited to 'cmd') diff --git a/boot/cedit.c b/boot/cedit.c index 6c10b211454..4dd79a2263d 100644 --- a/boot/cedit.c +++ b/boot/cedit.c @@ -9,6 +9,7 @@ #define LOG_CATEGORY LOGC_EXPO #include +#include #include #include #include @@ -18,6 +19,15 @@ #include #include "scene_internal.h" +/** + * struct cedit_iter_priv - private data for cedit operations + * + * @buf: Buffer to use when writing settings to the devicetree + */ +struct cedit_iter_priv { + struct abuf *buf; +}; + int cedit_arange(struct expo *exp, struct video_priv *vpriv, uint scene_id) { struct scene_obj_txt *txt; @@ -182,3 +192,129 @@ int cedit_run(struct expo *exp) return 0; } + +static int check_space(int ret, struct abuf *buf) +{ + if (ret == -FDT_ERR_NOSPACE) { + if (!abuf_realloc_inc(buf, CEDIT_SIZE_INC)) + return log_msg_ret("spc", -ENOMEM); + ret = fdt_resize(abuf_data(buf), abuf_data(buf), + abuf_size(buf)); + if (ret) + return log_msg_ret("res", -EFAULT); + } + + return 0; +} + +static int h_write_settings(struct scene_obj *obj, void *vpriv) +{ + struct cedit_iter_priv *priv = vpriv; + struct abuf *buf = priv->buf; + + switch (obj->type) { + case SCENEOBJT_NONE: + case SCENEOBJT_IMAGE: + case SCENEOBJT_TEXT: + break; + case SCENEOBJT_MENU: { + const struct scene_obj_menu *menu; + const struct scene_obj_txt *txt; + struct scene *scn = obj->scene; + const struct scene_menitem *mi; + const char *str; + char name[80]; + int ret, i; + + menu = (struct scene_obj_menu *)obj; + ret = -EAGAIN; + for (i = 0; ret && i < 2; i++) { + ret = fdt_property_u32(abuf_data(buf), obj->name, + menu->cur_item_id); + if (!i) { + ret = check_space(ret, buf); + if (ret) + return log_msg_ret("res", -ENOMEM); + } + } + /* this should not happen */ + if (ret) + return log_msg_ret("wrt", -EFAULT); + + mi = scene_menuitem_find(menu, menu->cur_item_id); + if (!mi) + return log_msg_ret("mi", -ENOENT); + + txt = scene_obj_find(scn, mi->label_id, SCENEOBJT_TEXT); + if (!txt) + return log_msg_ret("txt", -ENOENT); + + str = expo_get_str(scn->expo, txt->str_id); + if (!str) + return log_msg_ret("str", -ENOENT); + + snprintf(name, sizeof(name), "%s-str", obj->name); + ret = -EAGAIN; + for (i = 0; ret && i < 2; i++) { + ret = fdt_property_string(abuf_data(buf), name, str); + if (!i) { + ret = check_space(ret, buf); + if (ret) + return log_msg_ret("rs2", -ENOMEM); + } + } + + /* this should not happen */ + if (ret) + return log_msg_ret("wr2", -EFAULT); + + break; + } + } + + return 0; +} + +int cedit_write_settings(struct expo *exp, struct abuf *buf) +{ + struct cedit_iter_priv priv; + void *fdt; + int ret; + + abuf_init(buf); + if (!abuf_realloc(buf, CEDIT_SIZE_INC)) + return log_msg_ret("buf", -ENOMEM); + + fdt = abuf_data(buf); + ret = fdt_create(fdt, abuf_size(buf)); + if (!ret) + ret = fdt_finish_reservemap(fdt); + if (!ret) + ret = fdt_begin_node(fdt, ""); + if (!ret) + ret = fdt_begin_node(fdt, CEDIT_NODE_NAME); + if (ret) { + log_debug("Failed to start FDT (err=%d)\n", ret); + return log_msg_ret("sta", -EINVAL); + } + + /* write out the items */ + priv.buf = buf; + ret = expo_iter_scene_objs(exp, h_write_settings, &priv); + if (ret) { + log_debug("Failed to write settings (err=%d)\n", ret); + return log_msg_ret("set", ret); + } + + ret = fdt_end_node(fdt); + if (!ret) + ret = fdt_end_node(fdt); + if (!ret) + ret = fdt_finish(fdt); + if (ret) { + log_debug("Failed to finish FDT (err=%d)\n", ret); + return log_msg_ret("fin", -EINVAL); + } + + return 0; +} diff --git a/cmd/cedit.c b/cmd/cedit.c index e98121b067b..18cc8ba191b 100644 --- a/cmd/cedit.c +++ b/cmd/cedit.c @@ -7,15 +7,28 @@ */ #include +#include #include #include #include #include +#include +#include #include #include struct expo *cur_exp; +static int check_cur_expo(void) +{ + if (!cur_exp) { + printf("No expo loaded\n"); + return -ENOENT; + } + + return 0; +} + static int do_cedit_load(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) { @@ -54,16 +67,46 @@ static int do_cedit_load(struct cmd_tbl *cmdtp, int flag, int argc, return 0; } +static int do_cedit_write_fdt(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + const char *fname; + struct abuf buf; + loff_t bytes; + int ret; + + if (argc < 4) + return CMD_RET_USAGE; + fname = argv[3]; + + if (check_cur_expo()) + return CMD_RET_FAILURE; + + ret = cedit_write_settings(cur_exp, &buf); + if (ret) { + printf("Failed to write settings: %dE\n", ret); + return CMD_RET_FAILURE; + } + + if (fs_set_blk_dev(argv[1], argv[2], FS_TYPE_ANY)) + return CMD_RET_FAILURE; + + ret = fs_write(fname, map_to_sysmem(abuf_data(&buf)), 0, + abuf_size(&buf), &bytes); + if (ret) + return CMD_RET_FAILURE; + + 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"); + if (check_cur_expo()) return CMD_RET_FAILURE; - } node = ofnode_path("/bootstd/cedit-theme"); if (ofnode_valid(node)) { @@ -85,10 +128,12 @@ static int do_cedit_run(struct cmd_tbl *cmdtp, int flag, int argc, #ifdef CONFIG_SYS_LONGHELP static char cedit_help_text[] = "load - load config editor\n" + "cedit write_fdt - write settings\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(write_fdt, 5, 1, do_cedit_write_fdt), U_BOOT_SUBCMD_MKENT(run, 1, 1, do_cedit_run), ); diff --git a/doc/usage/cmd/cedit.rst b/doc/usage/cmd/cedit.rst index d34a220797e..0581594831f 100644 --- a/doc/usage/cmd/cedit.rst +++ b/doc/usage/cmd/cedit.rst @@ -10,6 +10,7 @@ Synopis cedit load cedit run + cedit write_fdt Description ----------- @@ -38,6 +39,12 @@ Runs the default configuration-editor event loop. This is very simple, just accepting character input and moving through the objects under user control. The implementation is at `cedit_run()`. +cedit write_fdt +~~~~~~~~~~~~~~~ + +Writes the current user settings to a devicetree file. For each menu item the +selected ID and its text string are written. + Example ------- @@ -46,3 +53,15 @@ Example => cedit load hostfs - fred.dtb => cedit run + => cedit write_fdt hostfs - settings.dtb + +That results in:: + + / { + cedit-values { + cpu-speed = <0x00000006>; + cpu-speed-str = "2 GHz"; + power-loss = <0x0000000a>; + power-loss-str = "Always Off"; + }; + } diff --git a/include/cedit.h b/include/cedit.h index 851d8e83564..6086e302006 100644 --- a/include/cedit.h +++ b/include/cedit.h @@ -7,10 +7,21 @@ #ifndef __CEDIT_H #define __CEDIT_H +struct abuf; struct expo; struct scene; struct video_priv; +enum { + /* size increment for writing FDT */ + CEDIT_SIZE_INC = 1024, +}; + +/* Name of the cedit node in the devicetree */ +#define CEDIT_NODE_NAME "cedit-values" + +extern struct expo *cur_exp; + /** * cedit_arange() - Arrange objects in a configuration-editor scene * @@ -45,4 +56,15 @@ int cedit_run(struct expo *exp); int cedit_prepare(struct expo *exp, struct video_priv **vid_privp, struct scene **scnp); +/** + * cedit_write_settings() - Write settings in FDT format + * + * Sets up an FDT with the settings + * + * @exp: Expo to write settings from + * @buf: Returns abuf containing the settings FDT (inited by this function) + * Return: 0 if OK, -ve on error + */ +int cedit_write_settings(struct expo *exp, struct abuf *buf); + #endif /* __CEDIT_H */ diff --git a/test/boot/cedit.c b/test/boot/cedit.c index f3411f734fa..1dd78c64158 100644 --- a/test/boot/cedit.c +++ b/test/boot/cedit.c @@ -7,6 +7,8 @@ #include #include #include +#include +#include #include #include "bootstd_common.h" #include @@ -51,3 +53,46 @@ static int cedit_base(struct unit_test_state *uts) return 0; } BOOTSTD_TEST(cedit_base, 0); + +/* Check the cedit write_fdt commands */ +static int cedit_fdt(struct unit_test_state *uts) +{ + struct video_priv *vid_priv; + extern struct expo *cur_exp; + ulong addr = 0x1000; + struct ofprop prop; + struct scene *scn; + oftree tree; + ofnode node; + void *fdt; + int i; + + console_record_reset_enable(); + ut_assertok(run_command("cedit load hostfs - cedit.dtb", 0)); + + ut_asserteq(ID_SCENE1, cedit_prepare(cur_exp, &vid_priv, &scn)); + + ut_assertok(run_command("cedit write_fdt hostfs - settings.dtb", 0)); + ut_assertok(run_commandf("load hostfs - %lx settings.dtb", addr)); + ut_assert_nextlinen("1024 bytes read"); + + fdt = map_sysmem(addr, 1024); + tree = oftree_from_fdt(fdt); + node = ofnode_find_subnode(oftree_root(tree), CEDIT_NODE_NAME); + + ut_asserteq(ID_CPU_SPEED_1, + ofnode_read_u32_default(node, "cpu-speed", 0)); + ut_asserteq_str("2 GHz", ofnode_read_string(node, "cpu-speed-str")); + ut_assert(ofnode_valid(node)); + + /* There should only be 4 properties */ + for (i = 0, ofnode_first_property(node, &prop); ofprop_valid(&prop); + i++, ofnode_next_property(&prop)) + ; + ut_asserteq(4, i); + + ut_assert_console_end(); + + return 0; +} +BOOTSTD_TEST(cedit_fdt, 0); -- cgit v1.3.1 From 472317cb12e534f56b631365987934960dfb0a3f Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 14 Aug 2023 16:40:34 -0600 Subject: expo: cedit: Support reading settings from a file Add a command to read cedit settings from a devicetree file. Signed-off-by: Simon Glass --- boot/cedit.c | 52 +++++++++++++++++++++++++++++++++++++++++++++++++ cmd/cedit.c | 39 +++++++++++++++++++++++++++++++++++++ doc/usage/cmd/cedit.rst | 8 ++++++++ include/cedit.h | 13 +++++++++++++ test/boot/cedit.c | 22 ++++++++++++++++++--- 5 files changed, 131 insertions(+), 3 deletions(-) (limited to 'cmd') diff --git a/boot/cedit.c b/boot/cedit.c index 4dd79a2263d..6a74a380989 100644 --- a/boot/cedit.c +++ b/boot/cedit.c @@ -23,9 +23,11 @@ * struct cedit_iter_priv - private data for cedit operations * * @buf: Buffer to use when writing settings to the devicetree + * @node: Node to read from when reading settings from devicetree */ struct cedit_iter_priv { struct abuf *buf; + ofnode node; }; int cedit_arange(struct expo *exp, struct video_priv *vpriv, uint scene_id) @@ -318,3 +320,53 @@ int cedit_write_settings(struct expo *exp, struct abuf *buf) return 0; } + +static int h_read_settings(struct scene_obj *obj, void *vpriv) +{ + struct cedit_iter_priv *priv = vpriv; + ofnode node = priv->node; + + switch (obj->type) { + case SCENEOBJT_NONE: + case SCENEOBJT_IMAGE: + case SCENEOBJT_TEXT: + break; + case SCENEOBJT_MENU: { + struct scene_obj_menu *menu; + uint val; + + if (ofnode_read_u32(node, obj->name, &val)) + return log_msg_ret("rd", -ENOENT); + menu = (struct scene_obj_menu *)obj; + menu->cur_item_id = val; + + break; + } + } + + return 0; +} + +int cedit_read_settings(struct expo *exp, oftree tree) +{ + struct cedit_iter_priv priv; + ofnode root, node; + int ret; + + root = oftree_root(tree); + if (!ofnode_valid(root)) + return log_msg_ret("roo", -ENOENT); + node = ofnode_find_subnode(root, CEDIT_NODE_NAME); + if (!ofnode_valid(node)) + return log_msg_ret("pat", -ENOENT); + + /* read in the items */ + priv.node = node; + ret = expo_iter_scene_objs(exp, h_read_settings, &priv); + if (ret) { + log_debug("Failed to read settings (err=%d)\n", ret); + return log_msg_ret("set", ret); + } + + return 0; +} diff --git a/cmd/cedit.c b/cmd/cedit.c index 18cc8ba191b..a155e080b1f 100644 --- a/cmd/cedit.c +++ b/cmd/cedit.c @@ -99,6 +99,43 @@ static int do_cedit_write_fdt(struct cmd_tbl *cmdtp, int flag, int argc, return 0; } +static int do_cedit_read_fdt(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + const char *fname; + void *buf; + oftree tree; + ulong size; + 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)) { + free(buf); + printf("Cannot create oftree\n"); + return CMD_RET_FAILURE; + } + + ret = cedit_read_settings(cur_exp, tree); + oftree_dispose(tree); + free(buf); + if (ret) { + printf("Failed to read settings: %dE\n", ret); + return CMD_RET_FAILURE; + } + + return 0; +} + static int do_cedit_run(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) { @@ -128,12 +165,14 @@ static int do_cedit_run(struct cmd_tbl *cmdtp, int flag, int argc, #ifdef CONFIG_SYS_LONGHELP static char cedit_help_text[] = "load - load config editor\n" + "cedit read_fdt - read settings\n" "cedit write_fdt - write settings\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(read_fdt, 5, 1, do_cedit_read_fdt), U_BOOT_SUBCMD_MKENT(write_fdt, 5, 1, do_cedit_write_fdt), U_BOOT_SUBCMD_MKENT(run, 1, 1, do_cedit_run), ); diff --git a/doc/usage/cmd/cedit.rst b/doc/usage/cmd/cedit.rst index 0581594831f..0a9f620b59b 100644 --- a/doc/usage/cmd/cedit.rst +++ b/doc/usage/cmd/cedit.rst @@ -11,6 +11,7 @@ Synopis cedit load cedit run cedit write_fdt + cedit read_fdt Description ----------- @@ -45,6 +46,11 @@ cedit write_fdt Writes the current user settings to a devicetree file. For each menu item the selected ID and its text string are written. +cedit read_fdt +~~~~~~~~~~~~~~ + +Reads the user settings from a devicetree file and updates the cedit with those +settings. Example ------- @@ -65,3 +71,5 @@ That results in:: power-loss-str = "Always Off"; }; } + + => cedit read_fdt hostfs - settings.dtb diff --git a/include/cedit.h b/include/cedit.h index 6086e302006..bb6e87d4af7 100644 --- a/include/cedit.h +++ b/include/cedit.h @@ -7,6 +7,8 @@ #ifndef __CEDIT_H #define __CEDIT_H +#include + struct abuf; struct expo; struct scene; @@ -67,4 +69,15 @@ int cedit_prepare(struct expo *exp, struct video_priv **vid_privp, */ int cedit_write_settings(struct expo *exp, struct abuf *buf); +/** + * cedit_read_settings() - Read settings in FDT format + * + * Read an FDT with the settings + * + * @exp: Expo to read settings into + * @tree: Tree to read from + * Return: 0 if OK, -ve on error + */ +int cedit_read_settings(struct expo *exp, oftree tree); + #endif /* __CEDIT_H */ diff --git a/test/boot/cedit.c b/test/boot/cedit.c index 1dd78c64158..659c47ed35f 100644 --- a/test/boot/cedit.c +++ b/test/boot/cedit.c @@ -54,11 +54,12 @@ static int cedit_base(struct unit_test_state *uts) } BOOTSTD_TEST(cedit_base, 0); -/* Check the cedit write_fdt commands */ +/* Check the cedit write_fdt and read_fdt commands */ static int cedit_fdt(struct unit_test_state *uts) { struct video_priv *vid_priv; extern struct expo *cur_exp; + struct scene_obj_menu *menu; ulong addr = 0x1000; struct ofprop prop; struct scene *scn; @@ -72,6 +73,11 @@ static int cedit_fdt(struct unit_test_state *uts) ut_asserteq(ID_SCENE1, cedit_prepare(cur_exp, &vid_priv, &scn)); + /* get a menu to fiddle with */ + menu = scene_obj_find(scn, ID_CPU_SPEED, SCENEOBJT_MENU); + ut_assertnonnull(menu); + menu->cur_item_id = ID_CPU_SPEED_2; + ut_assertok(run_command("cedit write_fdt hostfs - settings.dtb", 0)); ut_assertok(run_commandf("load hostfs - %lx settings.dtb", addr)); ut_assert_nextlinen("1024 bytes read"); @@ -80,9 +86,9 @@ static int cedit_fdt(struct unit_test_state *uts) tree = oftree_from_fdt(fdt); node = ofnode_find_subnode(oftree_root(tree), CEDIT_NODE_NAME); - ut_asserteq(ID_CPU_SPEED_1, + ut_asserteq(ID_CPU_SPEED_2, ofnode_read_u32_default(node, "cpu-speed", 0)); - ut_asserteq_str("2 GHz", ofnode_read_string(node, "cpu-speed-str")); + ut_asserteq_str("2.5 GHz", ofnode_read_string(node, "cpu-speed-str")); ut_assert(ofnode_valid(node)); /* There should only be 4 properties */ @@ -93,6 +99,16 @@ static int cedit_fdt(struct unit_test_state *uts) ut_assert_console_end(); + /* reset the expo */ + menu->cur_item_id = ID_CPU_SPEED_1; + + /* load in the settings and make sure they update */ + ut_assertok(run_command("cedit read_fdt hostfs - settings.dtb", 0)); + ut_asserteq(ID_CPU_SPEED_2, menu->cur_item_id); + + ut_assertnonnull(menu); + ut_assert_console_end(); + return 0; } BOOTSTD_TEST(cedit_fdt, 0); -- cgit v1.3.1 From fc9c0e0771cad76b24f73bb64c105b6ea39721ca Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 14 Aug 2023 16:40:35 -0600 Subject: expo: cedit: Support writing settings to environment vars Add a command to write cedit settings to environment variables so that they can be stored with 'saveenv'. Signed-off-by: Simon Glass --- boot/cedit.c | 97 ++++++++++++++++++++++++++++++++++++++++++------- cmd/cedit.c | 22 +++++++++++ doc/usage/cmd/cedit.rst | 25 +++++++++++++ include/cedit.h | 9 +++++ test/boot/cedit.c | 33 +++++++++++++++++ 5 files changed, 172 insertions(+), 14 deletions(-) (limited to 'cmd') diff --git a/boot/cedit.c b/boot/cedit.c index 6a74a380989..9399c01cda9 100644 --- a/boot/cedit.c +++ b/boot/cedit.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -24,10 +25,12 @@ * * @buf: Buffer to use when writing settings to the devicetree * @node: Node to read from when reading settings from devicetree + * @verbose: true to show writing to environment variables */ struct cedit_iter_priv { struct abuf *buf; ofnode node; + bool verbose; }; int cedit_arange(struct expo *exp, struct video_priv *vpriv, uint scene_id) @@ -209,6 +212,30 @@ static int check_space(int ret, struct abuf *buf) return 0; } +static int get_cur_menuitem_text(const struct scene_obj_menu *menu, + const char **strp) +{ + struct scene *scn = menu->obj.scene; + const struct scene_menitem *mi; + const struct scene_obj_txt *txt; + const char *str; + + mi = scene_menuitem_find(menu, menu->cur_item_id); + if (!mi) + return log_msg_ret("mi", -ENOENT); + + txt = scene_obj_find(scn, mi->label_id, SCENEOBJT_TEXT); + if (!txt) + return log_msg_ret("txt", -ENOENT); + + str = expo_get_str(scn->expo, txt->str_id); + if (!str) + return log_msg_ret("str", -ENOENT); + *strp = str; + + return 0; +} + static int h_write_settings(struct scene_obj *obj, void *vpriv) { struct cedit_iter_priv *priv = vpriv; @@ -221,9 +248,6 @@ static int h_write_settings(struct scene_obj *obj, void *vpriv) break; case SCENEOBJT_MENU: { const struct scene_obj_menu *menu; - const struct scene_obj_txt *txt; - struct scene *scn = obj->scene; - const struct scene_menitem *mi; const char *str; char name[80]; int ret, i; @@ -243,17 +267,9 @@ static int h_write_settings(struct scene_obj *obj, void *vpriv) if (ret) return log_msg_ret("wrt", -EFAULT); - mi = scene_menuitem_find(menu, menu->cur_item_id); - if (!mi) - return log_msg_ret("mi", -ENOENT); - - txt = scene_obj_find(scn, mi->label_id, SCENEOBJT_TEXT); - if (!txt) - return log_msg_ret("txt", -ENOENT); - - str = expo_get_str(scn->expo, txt->str_id); - if (!str) - return log_msg_ret("str", -ENOENT); + ret = get_cur_menuitem_text(menu, &str); + if (ret) + return log_msg_ret("mis", ret); snprintf(name, sizeof(name), "%s-str", obj->name); ret = -EAGAIN; @@ -370,3 +386,56 @@ int cedit_read_settings(struct expo *exp, oftree tree) return 0; } + +static int h_write_settings_env(struct scene_obj *obj, void *vpriv) +{ + const struct scene_obj_menu *menu; + struct cedit_iter_priv *priv = vpriv; + char name[80], var[60]; + const char *str; + int val, ret; + + if (obj->type != SCENEOBJT_MENU) + return 0; + + menu = (struct scene_obj_menu *)obj; + val = menu->cur_item_id; + snprintf(var, sizeof(var), "c.%s", obj->name); + + if (priv->verbose) + printf("%s=%d\n", var, val); + + ret = env_set_ulong(var, val); + if (ret) + return log_msg_ret("set", ret); + + ret = get_cur_menuitem_text(menu, &str); + if (ret) + return log_msg_ret("mis", ret); + + snprintf(name, sizeof(name), "c.%s-str", obj->name); + if (priv->verbose) + printf("%s=%s\n", name, str); + + ret = env_set(name, str); + if (ret) + return log_msg_ret("st2", ret); + + return 0; +} + +int cedit_write_settings_env(struct expo *exp, bool verbose) +{ + struct cedit_iter_priv priv; + int ret; + + /* write out the items */ + priv.verbose = verbose; + ret = expo_iter_scene_objs(exp, h_write_settings_env, &priv); + if (ret) { + log_debug("Failed to write settings to env (err=%d)\n", ret); + return log_msg_ret("set", ret); + } + + return 0; +} diff --git a/cmd/cedit.c b/cmd/cedit.c index a155e080b1f..85629f7b83c 100644 --- a/cmd/cedit.c +++ b/cmd/cedit.c @@ -136,6 +136,26 @@ static int do_cedit_read_fdt(struct cmd_tbl *cmdtp, int flag, int argc, return 0; } +static int do_cedit_write_env(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + bool verbose; + int ret; + + if (check_cur_expo()) + return CMD_RET_FAILURE; + + verbose = argc > 1 && !strcmp(argv[1], "-v"); + + ret = cedit_write_settings_env(cur_exp, verbose); + if (ret) { + printf("Failed to write settings to environment: %dE\n", ret); + return CMD_RET_FAILURE; + } + + return 0; +} + static int do_cedit_run(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) { @@ -167,6 +187,7 @@ static char cedit_help_text[] = "load - load config editor\n" "cedit read_fdt - read settings\n" "cedit write_fdt - write settings\n" + "cedit write_env [-v] - write settings to env vars\n" "cedit run - run config editor"; #endif /* CONFIG_SYS_LONGHELP */ @@ -174,5 +195,6 @@ 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(read_fdt, 5, 1, do_cedit_read_fdt), U_BOOT_SUBCMD_MKENT(write_fdt, 5, 1, do_cedit_write_fdt), + U_BOOT_SUBCMD_MKENT(write_env, 2, 1, do_cedit_write_env), U_BOOT_SUBCMD_MKENT(run, 1, 1, do_cedit_run), ); diff --git a/doc/usage/cmd/cedit.rst b/doc/usage/cmd/cedit.rst index 0a9f620b59b..426470a82ac 100644 --- a/doc/usage/cmd/cedit.rst +++ b/doc/usage/cmd/cedit.rst @@ -12,6 +12,7 @@ Synopis cedit run cedit write_fdt cedit read_fdt + cedit write_env [-v] Description ----------- @@ -52,6 +53,19 @@ cedit read_fdt Reads the user settings from a devicetree file and updates the cedit with those settings. +cedit write_env +~~~~~~~~~~~~~~~ + +Writes the settings to environment variables. For each menu item the selected +ID and its text string are written, similar to: + + setenv c. + setenv c.-str + +The `-v` flag enables verbose mode, where each variable is printed before it is +set. + + Example ------- @@ -73,3 +87,14 @@ That results in:: } => cedit read_fdt hostfs - settings.dtb + +This shows settings being stored in the environment:: + + => cedit write_env -v + => print + ... + c.cpu-speed=6 + c.cpu-speed-str=2 GHz + c.power-loss=10 + c.power-loss-str=Always Off + ... diff --git a/include/cedit.h b/include/cedit.h index bb6e87d4af7..f261207e209 100644 --- a/include/cedit.h +++ b/include/cedit.h @@ -80,4 +80,13 @@ int cedit_write_settings(struct expo *exp, struct abuf *buf); */ int cedit_read_settings(struct expo *exp, oftree tree); +/** + * cedit_write_settings_env() - Write settings to envrionment variables + * + * @exp: Expo to write settings from + * @verbose: true to print each var as it is set + * Return: 0 if OK, -ve on error + */ +int cedit_write_settings_env(struct expo *exp, bool verbose); + #endif /* __CEDIT_H */ diff --git a/test/boot/cedit.c b/test/boot/cedit.c index 659c47ed35f..26a69f0323f 100644 --- a/test/boot/cedit.c +++ b/test/boot/cedit.c @@ -6,6 +6,7 @@ #include #include +#include #include #include #include @@ -112,3 +113,35 @@ static int cedit_fdt(struct unit_test_state *uts) return 0; } BOOTSTD_TEST(cedit_fdt, 0); + +/* Check the cedit write_env command */ +static int cedit_env(struct unit_test_state *uts) +{ + struct video_priv *vid_priv; + extern struct expo *cur_exp; + struct scene_obj_menu *menu; + struct scene *scn; + + console_record_reset_enable(); + ut_assertok(run_command("cedit load hostfs - cedit.dtb", 0)); + + ut_asserteq(ID_SCENE1, cedit_prepare(cur_exp, &vid_priv, &scn)); + + /* get a menu to fiddle with */ + menu = scene_obj_find(scn, ID_CPU_SPEED, SCENEOBJT_MENU); + ut_assertnonnull(menu); + menu->cur_item_id = ID_CPU_SPEED_2; + + ut_assertok(run_command("cedit write_env -v", 0)); + ut_assert_nextlinen("c.cpu-speed=7"); + ut_assert_nextlinen("c.cpu-speed-str=2.5 GHz"); + ut_assert_nextlinen("c.power-loss=10"); + ut_assert_nextlinen("c.power-loss-str=Always Off"); + ut_assert_console_end(); + + ut_asserteq(7, env_get_ulong("c.cpu-speed", 10, 0)); + ut_asserteq_str("2.5 GHz", env_get("c.cpu-speed-str")); + + return 0; +} +BOOTSTD_TEST(cedit_env, 0); -- cgit v1.3.1 From bcf2b7202e960e7fb3df8d5e8ed0d6fa00a5a9fa Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 14 Aug 2023 16:40:36 -0600 Subject: expo: cedit: Support reading settings from environment vars Add a command to read cedit settings from environment variables so that they can be restored as part of the environment. Signed-off-by: Simon Glass --- boot/cedit.c | 45 +++++++++++++++++++++++++++++++++++++++++++++ cmd/cedit.c | 22 ++++++++++++++++++++++ doc/usage/cmd/cedit.rst | 19 +++++++++++++++++++ include/cedit.h | 8 ++++++++ test/boot/cedit.c | 12 +++++++++++- 5 files changed, 105 insertions(+), 1 deletion(-) (limited to 'cmd') diff --git a/boot/cedit.c b/boot/cedit.c index 9399c01cda9..e3f6dc00399 100644 --- a/boot/cedit.c +++ b/boot/cedit.c @@ -439,3 +439,48 @@ int cedit_write_settings_env(struct expo *exp, bool verbose) return 0; } + +static int h_read_settings_env(struct scene_obj *obj, void *vpriv) +{ + struct cedit_iter_priv *priv = vpriv; + struct scene_obj_menu *menu; + char var[60]; + int val, ret; + + if (obj->type != SCENEOBJT_MENU) + return 0; + + menu = (struct scene_obj_menu *)obj; + val = menu->cur_item_id; + snprintf(var, sizeof(var), "c.%s", obj->name); + + val = env_get_ulong(var, 10, 0); + if (priv->verbose) + printf("%s=%d\n", var, val); + if (!val) + return log_msg_ret("get", -ENOENT); + + /* + * note that no validation is done here, to make sure the ID is valid + * and actually points to a menu item + */ + menu->cur_item_id = val; + + return 0; +} + +int cedit_read_settings_env(struct expo *exp, bool verbose) +{ + struct cedit_iter_priv priv; + int ret; + + /* write out the items */ + priv.verbose = verbose; + ret = expo_iter_scene_objs(exp, h_read_settings_env, &priv); + if (ret) { + log_debug("Failed to read settings from env (err=%d)\n", ret); + return log_msg_ret("set", ret); + } + + return 0; +} diff --git a/cmd/cedit.c b/cmd/cedit.c index 85629f7b83c..b2548f44b57 100644 --- a/cmd/cedit.c +++ b/cmd/cedit.c @@ -156,6 +156,26 @@ static int do_cedit_write_env(struct cmd_tbl *cmdtp, int flag, int argc, return 0; } +static int do_cedit_read_env(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + bool verbose; + int ret; + + if (check_cur_expo()) + return CMD_RET_FAILURE; + + verbose = argc > 1 && !strcmp(argv[1], "-v"); + + ret = cedit_read_settings_env(cur_exp, verbose); + if (ret) { + printf("Failed to read settings from environment: %dE\n", ret); + return CMD_RET_FAILURE; + } + + return 0; +} + static int do_cedit_run(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) { @@ -187,6 +207,7 @@ static char cedit_help_text[] = "load - load config editor\n" "cedit read_fdt - read settings\n" "cedit write_fdt - write settings\n" + "cedit read_env [-v] - read settings from env vars\n" "cedit write_env [-v] - write settings to env vars\n" "cedit run - run config editor"; #endif /* CONFIG_SYS_LONGHELP */ @@ -195,6 +216,7 @@ 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(read_fdt, 5, 1, do_cedit_read_fdt), U_BOOT_SUBCMD_MKENT(write_fdt, 5, 1, do_cedit_write_fdt), + U_BOOT_SUBCMD_MKENT(read_env, 2, 1, do_cedit_read_env), U_BOOT_SUBCMD_MKENT(write_env, 2, 1, do_cedit_write_env), U_BOOT_SUBCMD_MKENT(run, 1, 1, do_cedit_run), ); diff --git a/doc/usage/cmd/cedit.rst b/doc/usage/cmd/cedit.rst index 426470a82ac..1f92b7306a7 100644 --- a/doc/usage/cmd/cedit.rst +++ b/doc/usage/cmd/cedit.rst @@ -13,6 +13,7 @@ Synopis cedit write_fdt cedit read_fdt cedit write_env [-v] + cedit read_env [-v] Description ----------- @@ -53,6 +54,16 @@ cedit read_fdt Reads the user settings from a devicetree file and updates the cedit with those settings. +cedit read_env +~~~~~~~~~~~~~~ + +Reads the settings from the environment variables. For each menu item ``, +cedit looks for a variable called `c.` with the ID of the selected menu +item. + +The `-v` flag enables verbose mode, where each variable is printed after it is +read. + cedit write_env ~~~~~~~~~~~~~~~ @@ -91,6 +102,10 @@ That results in:: This shows settings being stored in the environment:: => cedit write_env -v + c.cpu-speed=7 + c.cpu-speed-str=2.5 GHz + c.power-loss=12 + c.power-loss-str=Memory => print ... c.cpu-speed=6 @@ -98,3 +113,7 @@ This shows settings being stored in the environment:: c.power-loss=10 c.power-loss-str=Always Off ... + + => cedit read_env -v + c.cpu-speed=7 + c.power-loss=12 diff --git a/include/cedit.h b/include/cedit.h index f261207e209..fe10e6c829c 100644 --- a/include/cedit.h +++ b/include/cedit.h @@ -89,4 +89,12 @@ int cedit_read_settings(struct expo *exp, oftree tree); */ int cedit_write_settings_env(struct expo *exp, bool verbose); +/* + * cedit_read_settings_env() - Read settings from the environment + * + * @exp: Expo to read settings into + * @verbose: true to print each var before it is read + */ +int cedit_read_settings_env(struct expo *exp, bool verbose); + #endif /* __CEDIT_H */ diff --git a/test/boot/cedit.c b/test/boot/cedit.c index 26a69f0323f..7cf0c3e4e93 100644 --- a/test/boot/cedit.c +++ b/test/boot/cedit.c @@ -114,7 +114,7 @@ static int cedit_fdt(struct unit_test_state *uts) } BOOTSTD_TEST(cedit_fdt, 0); -/* Check the cedit write_env command */ +/* Check the cedit write_env and read_env commands */ static int cedit_env(struct unit_test_state *uts) { struct video_priv *vid_priv; @@ -142,6 +142,16 @@ static int cedit_env(struct unit_test_state *uts) ut_asserteq(7, env_get_ulong("c.cpu-speed", 10, 0)); ut_asserteq_str("2.5 GHz", env_get("c.cpu-speed-str")); + /* reset the expo */ + menu->cur_item_id = ID_CPU_SPEED_1; + + ut_assertok(run_command("cedit read_env -v", 0)); + ut_assert_nextlinen("c.cpu-speed=7"); + ut_assert_nextlinen("c.power-loss=10"); + ut_assert_console_end(); + + ut_asserteq(ID_CPU_SPEED_2, menu->cur_item_id); + return 0; } BOOTSTD_TEST(cedit_env, 0); -- cgit v1.3.1 From eb6c71b56282d3054dbffb83793e7d2c6745578e Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 14 Aug 2023 16:40:37 -0600 Subject: expo: cedit: Support writing settings to CMOS RAM Add a command to write cedit settings to CMOS RAM so that it can be preserved across a reboot. This uses a simple bit-encoding, where each field has a 'bit position' and a 'bit length' in the schema. Signed-off-by: Simon Glass --- boot/cedit.c | 137 +++++++++++++++++++++++++++++++++++++++- boot/expo_build.c | 7 +- cmd/cedit.c | 36 +++++++++++ doc/develop/expo.rst | 13 ++++ doc/usage/cmd/cedit.rst | 22 +++++++ include/cedit.h | 13 ++++ include/expo.h | 6 +- test/boot/cedit.c | 30 +++++++++ test/boot/files/expo_layout.dts | 5 ++ 9 files changed, 266 insertions(+), 3 deletions(-) (limited to 'cmd') diff --git a/boot/cedit.c b/boot/cedit.c index e3f6dc00399..725745aba55 100644 --- a/boot/cedit.c +++ b/boot/cedit.c @@ -15,22 +15,37 @@ #include #include #include +#include #include +#include #include #include #include "scene_internal.h" +enum { + CMOS_MAX_BITS = 2048, + CMOS_MAX_BYTES = CMOS_MAX_BITS / 8, +}; + +#define CMOS_BYTE(bit) ((bit) / 8) +#define CMOS_BIT(bit) ((bit) % 8) + /** * struct cedit_iter_priv - private data for cedit operations * * @buf: Buffer to use when writing settings to the devicetree * @node: Node to read from when reading settings from devicetree * @verbose: true to show writing to environment variables + * @mask: Mask bits for the CMOS RAM. If a bit is set the byte containing it + * will be written + * @value: Value bits for CMOS RAM. This is the actual value written */ struct cedit_iter_priv { struct abuf *buf; ofnode node; bool verbose; + u8 *mask; + u8 *value; }; int cedit_arange(struct expo *exp, struct video_priv *vpriv, uint scene_id) @@ -445,7 +460,7 @@ static int h_read_settings_env(struct scene_obj *obj, void *vpriv) struct cedit_iter_priv *priv = vpriv; struct scene_obj_menu *menu; char var[60]; - int val, ret; + int val; if (obj->type != SCENEOBJT_MENU) return 0; @@ -484,3 +499,123 @@ int cedit_read_settings_env(struct expo *exp, bool verbose) return 0; } + +/** + * get_cur_menuitem_seq() - Get the sequence number of a menu's current item + * + * Enumerates the items of a menu (0, 1, 2) and returns the sequence number of + * the currently selected item. If the first item is selected, this returns 0; + * if the second, 1; etc. + * + * @menu: Menu to check + * Return: Sequence number on success, else -ve error value + */ +static int get_cur_menuitem_seq(const struct scene_obj_menu *menu) +{ + const struct scene_menitem *mi; + int seq, found; + + seq = 0; + found = -1; + list_for_each_entry(mi, &menu->item_head, sibling) { + if (mi->id == menu->cur_item_id) { + found = seq; + break; + } + seq++; + } + + if (found == -1) + return log_msg_ret("nf", -ENOENT); + + return found; +} + +static int h_write_settings_cmos(struct scene_obj *obj, void *vpriv) +{ + const struct scene_obj_menu *menu; + struct cedit_iter_priv *priv = vpriv; + int val, ret; + uint i, seq; + + if (obj->type != SCENEOBJT_MENU) + return 0; + + menu = (struct scene_obj_menu *)obj; + val = menu->cur_item_id; + + ret = get_cur_menuitem_seq(menu); + if (ret < 0) + return log_msg_ret("cur", ret); + seq = ret; + log_debug("%s: seq=%d\n", menu->obj.name, seq); + + /* figure out where to place this item */ + if (!obj->bit_length) + return log_msg_ret("len", -EINVAL); + if (obj->start_bit + obj->bit_length > CMOS_MAX_BITS) + return log_msg_ret("bit", -E2BIG); + + for (i = 0; i < obj->bit_length; i++, seq >>= 1) { + uint bitnum = obj->start_bit + i; + + priv->mask[CMOS_BYTE(bitnum)] |= 1 << CMOS_BIT(bitnum); + if (seq & 1) + priv->value[CMOS_BYTE(bitnum)] |= BIT(CMOS_BIT(bitnum)); + log_debug("bit %x %x %x\n", bitnum, + priv->mask[CMOS_BYTE(bitnum)], + priv->value[CMOS_BYTE(bitnum)]); + } + + return 0; +} + +int cedit_write_settings_cmos(struct expo *exp, struct udevice *dev, + bool verbose) +{ + struct cedit_iter_priv priv; + int ret, i, count, first, last; + + /* write out the items */ + priv.mask = calloc(1, CMOS_MAX_BYTES); + if (!priv.mask) + return log_msg_ret("mas", -ENOMEM); + priv.value = calloc(1, CMOS_MAX_BYTES); + if (!priv.value) { + free(priv.mask); + return log_msg_ret("val", -ENOMEM); + } + + ret = expo_iter_scene_objs(exp, h_write_settings_cmos, &priv); + if (ret) { + log_debug("Failed to write CMOS (err=%d)\n", ret); + ret = log_msg_ret("set", ret); + goto done; + } + + /* write the data to the RTC */ + first = CMOS_MAX_BYTES; + last = -1; + for (i = 0, count = 0; i < CMOS_MAX_BYTES; i++) { + if (priv.mask[i]) { + log_debug("Write byte %x: %x\n", i, priv.value[i]); + ret = rtc_write8(dev, i, priv.value[i]); + if (ret) { + ret = log_msg_ret("wri", ret); + goto done; + } + count++; + first = min(first, i); + last = max(last, i); + } + } + if (verbose) { + printf("Write %d bytes from offset %x to %x\n", count, first, + last); + } + +done: + free(priv.mask); + free(priv.value); + return ret; +} diff --git a/boot/expo_build.c b/boot/expo_build.c index e8c4a40d3f0..bb33cc2a33f 100644 --- a/boot/expo_build.c +++ b/boot/expo_build.c @@ -294,7 +294,7 @@ static int obj_build(struct build_info *info, ofnode node, struct scene *scn) { struct scene_obj *obj; const char *type; - u32 id; + u32 id, val; int ret; log_debug("- object %s\n", ofnode_get_name(node)); @@ -313,6 +313,11 @@ static int obj_build(struct build_info *info, ofnode node, struct scene *scn) if (ret) return log_msg_ret("bld", ret); + if (!ofnode_read_u32(node, "start-bit", &val)) + obj->start_bit = val; + if (!ofnode_read_u32(node, "bit-length", &val)) + obj->bit_length = val; + return 0; } diff --git a/cmd/cedit.c b/cmd/cedit.c index b2548f44b57..95d5c22c2f7 100644 --- a/cmd/cedit.c +++ b/cmd/cedit.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -176,6 +177,39 @@ static int do_cedit_read_env(struct cmd_tbl *cmdtp, int flag, int argc, return 0; } +static int do_cedit_write_cmos(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + struct udevice *dev; + bool verbose = false; + int ret; + + if (check_cur_expo()) + return CMD_RET_FAILURE; + + if (argc > 1 && !strcmp(argv[1], "-v")) { + verbose = true; + argc--; + argv++; + } + + if (argc > 1) + ret = uclass_get_device_by_name(UCLASS_RTC, argv[1], &dev); + else + ret = uclass_first_device_err(UCLASS_RTC, &dev); + if (ret) { + printf("Failed to get RTC device: %dE\n", ret); + return CMD_RET_FAILURE; + } + + if (cedit_write_settings_cmos(cur_exp, dev, verbose)) { + printf("Failed to write settings to CMOS\n"); + return CMD_RET_FAILURE; + } + + return 0; +} + static int do_cedit_run(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) { @@ -209,6 +243,7 @@ static char cedit_help_text[] = "cedit write_fdt - write settings\n" "cedit read_env [-v] - read settings from env vars\n" "cedit write_env [-v] - write settings to env vars\n" + "cedit write_cmos [-v] [dev] - write settings to CMOS RAM\n" "cedit run - run config editor"; #endif /* CONFIG_SYS_LONGHELP */ @@ -218,5 +253,6 @@ U_BOOT_CMD_WITH_SUBCMDS(cedit, "Configuration editor", cedit_help_text, U_BOOT_SUBCMD_MKENT(write_fdt, 5, 1, do_cedit_write_fdt), U_BOOT_SUBCMD_MKENT(read_env, 2, 1, do_cedit_read_env), U_BOOT_SUBCMD_MKENT(write_env, 2, 1, do_cedit_write_env), + U_BOOT_SUBCMD_MKENT(write_cmos, 2, 1, do_cedit_write_cmos), U_BOOT_SUBCMD_MKENT(run, 1, 1, do_cedit_run), ); diff --git a/doc/develop/expo.rst b/doc/develop/expo.rst index fde91494799..61b6855c72f 100644 --- a/doc/develop/expo.rst +++ b/doc/develop/expo.rst @@ -317,6 +317,18 @@ id Specifies the ID of the object. This is used when referring to the object. +Where CMOS RAM is used for reading and writing settings, the following +additional properties are required: + +start-bit + Specifies the first bit in the CMOS RAM to use for this setting. For a RAM + with 0x100 bytes, there are 0x800 bit locations. For example, register 0x80 + holds bits 0x400 to 0x407. + +bit-length + Specifies the number of CMOS RAM bits to use for this setting. The bits + extend from `start-bit` to `start-bit + bit-length - 1`. Note that the bits + must be contiguous. Menu nodes have the following additional properties: @@ -474,6 +486,7 @@ Some ideas for future work: - 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 +- Update expo.py tool to check for overlapping names and CMOS locations .. Simon Glass .. 7-Oct-22 diff --git a/doc/usage/cmd/cedit.rst b/doc/usage/cmd/cedit.rst index 1f92b7306a7..3d6f26e631d 100644 --- a/doc/usage/cmd/cedit.rst +++ b/doc/usage/cmd/cedit.rst @@ -14,6 +14,7 @@ Synopis cedit read_fdt cedit write_env [-v] cedit read_env [-v] + cedit write_cmos [-v] [dev] Description ----------- @@ -76,6 +77,18 @@ ID and its text string are written, similar to: The `-v` flag enables verbose mode, where each variable is printed before it is set. +cedit write_cmos +~~~~~~~~~~~~~~~~ + +Writes the settings to locations in the CMOS RAM. The locations used are +specified by the schema. See `expo_format_`. + +The `-v` flag enables verbose mode, which shows which CMOS locations were +updated. + +Normally the first RTC device is used to hold the data. You can specify a +different device by name using the `dev` parameter. + Example ------- @@ -117,3 +130,12 @@ This shows settings being stored in the environment:: => cedit read_env -v c.cpu-speed=7 c.power-loss=12 + +This shows writing to CMOS RAM. Notice that the bytes at 80 and 84 change:: + + => rtc read 80 8 + 00000080: 00 00 00 00 00 2f 2a 08 ...../*. + => cedit write_cmos + Write 2 bytes from offset 80 to 84 + => rtc read 80 8 + 00000080: 01 00 00 00 08 2f 2a 08 ...../*. diff --git a/include/cedit.h b/include/cedit.h index fe10e6c829c..2970965b5f6 100644 --- a/include/cedit.h +++ b/include/cedit.h @@ -97,4 +97,17 @@ int cedit_write_settings_env(struct expo *exp, bool verbose); */ int cedit_read_settings_env(struct expo *exp, bool verbose); +/** + * cedit_write_settings_cmos() - Write settings to CMOS RAM + * + * Write settings to the defined places in CMOS RAM + * + * @exp: Expo to write settings from + * @dev: UCLASS_RTC device containing space for this information + * Returns 0 if OK, -ve on error + * @verbose: true to print a summary at the end + */ +int cedit_write_settings_cmos(struct expo *exp, struct udevice *dev, + bool verbose); + #endif /* __CEDIT_H */ diff --git a/include/expo.h b/include/expo.h index da151074d20..a2b3a71c159 100644 --- a/include/expo.h +++ b/include/expo.h @@ -187,6 +187,8 @@ enum scene_obj_flags_t { * @type: Type of this object * @dim: Dimensions for this object * @flags: Flags for this object + * @bit_length: Number of bits used for this object in CMOS RAM + * @start_bit: Start bit to use for this object in CMOS RAM * @sibling: Node to link this object to its siblings */ struct scene_obj { @@ -195,7 +197,9 @@ struct scene_obj { uint id; enum scene_obj_t type; struct scene_dim dim; - int flags; + u8 flags; + u8 bit_length; + u16 start_bit; struct list_head sibling; }; diff --git a/test/boot/cedit.c b/test/boot/cedit.c index 7cf0c3e4e93..010aae615b9 100644 --- a/test/boot/cedit.c +++ b/test/boot/cedit.c @@ -155,3 +155,33 @@ static int cedit_env(struct unit_test_state *uts) return 0; } BOOTSTD_TEST(cedit_env, 0); + +/* Check the cedit write_cmos and read_cmos commands */ +static int cedit_cmos(struct unit_test_state *uts) +{ + struct scene_obj_menu *menu, *menu2; + struct video_priv *vid_priv; + extern struct expo *cur_exp; + struct scene *scn; + + console_record_reset_enable(); + ut_assertok(run_command("cedit load hostfs - cedit.dtb", 0)); + + ut_asserteq(ID_SCENE1, cedit_prepare(cur_exp, &vid_priv, &scn)); + + /* get the menus to fiddle with */ + menu = scene_obj_find(scn, ID_CPU_SPEED, SCENEOBJT_MENU); + ut_assertnonnull(menu); + menu->cur_item_id = ID_CPU_SPEED_2; + + menu2 = scene_obj_find(scn, ID_POWER_LOSS, SCENEOBJT_MENU); + ut_assertnonnull(menu2); + menu2->cur_item_id = ID_AC_MEMORY; + + ut_assertok(run_command("cedit write_cmos -v", 0)); + ut_assert_nextlinen("Write 2 bytes from offset 80 to 84"); + ut_assert_console_end(); + + return 0; +} +BOOTSTD_TEST(cedit_cmos, 0); diff --git a/test/boot/files/expo_layout.dts b/test/boot/files/expo_layout.dts index 913140bace9..cb2a674d9d5 100644 --- a/test/boot/files/expo_layout.dts +++ b/test/boot/files/expo_layout.dts @@ -38,6 +38,9 @@ /* IDs for the menu items */ item-id = ; + + start-bit = <0x400>; + bit-length = <2>; }; power-loss { @@ -49,6 +52,8 @@ "Memory"; item-id = ; + start-bit = <0x422>; + bit-length = <2>; }; }; }; -- cgit v1.3.1 From cfc402db3954d7c852c322b232ad6d8842af6bf1 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 14 Aug 2023 16:40:38 -0600 Subject: expo: cedit: Support reading settings from CMOS RAM Add a command to read edit settings from CMOS RAM, using the cedit definition to indicate which registers and bits are used. Signed-off-by: Simon Glass --- boot/cedit.c | 99 +++++++++++++++++++++++++++++++++++++++++++++++++ boot/scene_internal.h | 12 ++++++ boot/scene_menu.c | 16 ++++++++ cmd/cedit.c | 36 ++++++++++++++++++ doc/usage/cmd/cedit.rst | 9 ++++- include/cedit.h | 12 ++++++ test/boot/cedit.c | 11 ++++++ 7 files changed, 194 insertions(+), 1 deletion(-) (limited to 'cmd') diff --git a/boot/cedit.c b/boot/cedit.c index 725745aba55..73645f70b6c 100644 --- a/boot/cedit.c +++ b/boot/cedit.c @@ -39,6 +39,7 @@ enum { * @mask: Mask bits for the CMOS RAM. If a bit is set the byte containing it * will be written * @value: Value bits for CMOS RAM. This is the actual value written + * @dev: RTC device to write to */ struct cedit_iter_priv { struct abuf *buf; @@ -46,6 +47,7 @@ struct cedit_iter_priv { bool verbose; u8 *mask; u8 *value; + struct udevice *dev; }; int cedit_arange(struct expo *exp, struct video_priv *vpriv, uint scene_id) @@ -619,3 +621,100 @@ done: free(priv.value); return ret; } + +static int h_read_settings_cmos(struct scene_obj *obj, void *vpriv) +{ + struct cedit_iter_priv *priv = vpriv; + const struct scene_menitem *mi; + struct scene_obj_menu *menu; + int val, ret; + uint i; + + if (obj->type != SCENEOBJT_MENU) + return 0; + + menu = (struct scene_obj_menu *)obj; + + /* figure out where to place this item */ + if (!obj->bit_length) + return log_msg_ret("len", -EINVAL); + if (obj->start_bit + obj->bit_length > CMOS_MAX_BITS) + return log_msg_ret("bit", -E2BIG); + + val = 0; + for (i = 0; i < obj->bit_length; i++) { + uint bitnum = obj->start_bit + i; + uint offset = CMOS_BYTE(bitnum); + + /* read the byte if not already read */ + if (!priv->mask[offset]) { + ret = rtc_read8(priv->dev, offset); + if (ret < 0) + return log_msg_ret("rea", ret); + priv->value[offset] = ret; + + /* mark it as read */ + priv->mask[offset] = 0xff; + } + + if (priv->value[offset] & BIT(CMOS_BIT(bitnum))) + val |= BIT(i); + log_debug("bit %x %x\n", bitnum, val); + } + + /* update the current item */ + mi = scene_menuitem_find_seq(menu, val); + if (!mi) + return log_msg_ret("seq", -ENOENT); + + menu->cur_item_id = mi->id; + log_debug("Update menu %d cur_item_id %d\n", menu->obj.id, mi->id); + + return 0; +} + +int cedit_read_settings_cmos(struct expo *exp, struct udevice *dev, + bool verbose) +{ + struct cedit_iter_priv priv; + int ret, i, count, first, last; + + /* read in the items */ + priv.mask = calloc(1, CMOS_MAX_BYTES); + if (!priv.mask) + return log_msg_ret("mas", -ENOMEM); + priv.value = calloc(1, CMOS_MAX_BYTES); + if (!priv.value) { + free(priv.mask); + return log_msg_ret("val", -ENOMEM); + } + priv.dev = dev; + + ret = expo_iter_scene_objs(exp, h_read_settings_cmos, &priv); + if (ret) { + log_debug("Failed to read CMOS (err=%d)\n", ret); + ret = log_msg_ret("set", ret); + goto done; + } + + /* read the data to the RTC */ + first = CMOS_MAX_BYTES; + last = -1; + for (i = 0, count = 0; i < CMOS_MAX_BYTES; i++) { + if (priv.mask[i]) { + log_debug("Read byte %x: %x\n", i, priv.value[i]); + count++; + first = min(first, i); + last = max(last, i); + } + } + if (verbose) { + printf("Read %d bytes from offset %x to %x\n", count, first, + last); + } + +done: + free(priv.mask); + free(priv.value); + return ret; +} diff --git a/boot/scene_internal.h b/boot/scene_internal.h index 23e29cb349b..695a907dc6a 100644 --- a/boot/scene_internal.h +++ b/boot/scene_internal.h @@ -234,4 +234,16 @@ int expo_iter_scene_objs(struct expo *exp, expo_scene_obj_iterator iter, struct scene_menitem *scene_menuitem_find(const struct scene_obj_menu *menu, int id); +/** + * scene_menuitem_find_seq() - Find the menu item at a sequential position + * + * This numbers the items from 0 and returns the seq'th one + * + * @menu: Menu to check + * @seq: Sequence number to look for + * Return: menu item if found, else NULL + */ +struct scene_menitem *scene_menuitem_find_seq(const struct scene_obj_menu *menu, + uint seq); + #endif /* __SCENE_INTERNAL_H */ diff --git a/boot/scene_menu.c b/boot/scene_menu.c index 602fe24580a..e0dcd0a4e04 100644 --- a/boot/scene_menu.c +++ b/boot/scene_menu.c @@ -46,6 +46,22 @@ struct scene_menitem *scene_menuitem_find(const struct scene_obj_menu *menu, return NULL; } +struct scene_menitem *scene_menuitem_find_seq(const struct scene_obj_menu *menu, + uint seq) +{ + struct scene_menitem *item; + uint i; + + i = 0; + list_for_each_entry(item, &menu->item_head, sibling) { + if (i == seq) + return item; + i++; + } + + return NULL; +} + /** * update_pointers() - Update the pointer object and handle highlights * diff --git a/cmd/cedit.c b/cmd/cedit.c index 95d5c22c2f7..2ff284f4cde 100644 --- a/cmd/cedit.c +++ b/cmd/cedit.c @@ -210,6 +210,40 @@ static int do_cedit_write_cmos(struct cmd_tbl *cmdtp, int flag, int argc, return 0; } +static int do_cedit_read_cmos(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + struct udevice *dev; + bool verbose = false; + int ret; + + if (check_cur_expo()) + return CMD_RET_FAILURE; + + if (argc > 1 && !strcmp(argv[1], "-v")) { + verbose = true; + argc--; + argv++; + } + + if (argc > 1) + ret = uclass_get_device_by_name(UCLASS_RTC, argv[1], &dev); + else + ret = uclass_first_device_err(UCLASS_RTC, &dev); + if (ret) { + printf("Failed to get RTC device: %dE\n", ret); + return CMD_RET_FAILURE; + } + + ret = cedit_read_settings_cmos(cur_exp, dev, verbose); + if (ret) { + printf("Failed to read settings from CMOS: %dE\n", ret); + return CMD_RET_FAILURE; + } + + return 0; +} + static int do_cedit_run(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) { @@ -243,6 +277,7 @@ static char cedit_help_text[] = "cedit write_fdt - write settings\n" "cedit read_env [-v] - read settings from env vars\n" "cedit write_env [-v] - write settings to env vars\n" + "cedit read_cmos [-v] [dev] - read settings from CMOS RAM\n" "cedit write_cmos [-v] [dev] - write settings to CMOS RAM\n" "cedit run - run config editor"; #endif /* CONFIG_SYS_LONGHELP */ @@ -253,6 +288,7 @@ U_BOOT_CMD_WITH_SUBCMDS(cedit, "Configuration editor", cedit_help_text, U_BOOT_SUBCMD_MKENT(write_fdt, 5, 1, do_cedit_write_fdt), U_BOOT_SUBCMD_MKENT(read_env, 2, 1, do_cedit_read_env), U_BOOT_SUBCMD_MKENT(write_env, 2, 1, do_cedit_write_env), + U_BOOT_SUBCMD_MKENT(read_cmos, 2, 1, do_cedit_read_cmos), U_BOOT_SUBCMD_MKENT(write_cmos, 2, 1, do_cedit_write_cmos), U_BOOT_SUBCMD_MKENT(run, 1, 1, do_cedit_run), ); diff --git a/doc/usage/cmd/cedit.rst b/doc/usage/cmd/cedit.rst index 3d6f26e631d..f415b48699e 100644 --- a/doc/usage/cmd/cedit.rst +++ b/doc/usage/cmd/cedit.rst @@ -135,7 +135,14 @@ This shows writing to CMOS RAM. Notice that the bytes at 80 and 84 change:: => rtc read 80 8 00000080: 00 00 00 00 00 2f 2a 08 ...../*. - => cedit write_cmos + => cedit write_cmos -v Write 2 bytes from offset 80 to 84 => rtc read 80 8 00000080: 01 00 00 00 08 2f 2a 08 ...../*. + => cedit read_cmos -v + Read 2 bytes from offset 80 to 84 + +Here is an example with the device specified:: + + => cedit write_cmos rtc@43 + => diff --git a/include/cedit.h b/include/cedit.h index 2970965b5f6..f43cafa5aa2 100644 --- a/include/cedit.h +++ b/include/cedit.h @@ -110,4 +110,16 @@ int cedit_read_settings_env(struct expo *exp, bool verbose); int cedit_write_settings_cmos(struct expo *exp, struct udevice *dev, bool verbose); +/** + * cedit_read_settings_cmos() - Read settings from CMOS RAM + * + * Read settings from the defined places in CMO RAM + * + * @exp: Expo to read settings into + * @dev: RTC device to read settings from + * @verbose: true to print a summary at the end + */ +int cedit_read_settings_cmos(struct expo *exp, struct udevice *dev, + bool verbose); + #endif /* __CEDIT_H */ diff --git a/test/boot/cedit.c b/test/boot/cedit.c index 010aae615b9..ab2b8a1f9ff 100644 --- a/test/boot/cedit.c +++ b/test/boot/cedit.c @@ -182,6 +182,17 @@ static int cedit_cmos(struct unit_test_state *uts) ut_assert_nextlinen("Write 2 bytes from offset 80 to 84"); ut_assert_console_end(); + /* reset the expo */ + menu->cur_item_id = ID_CPU_SPEED_1; + menu2->cur_item_id = ID_AC_OFF; + + ut_assertok(run_command("cedit read_cmos -v", 0)); + ut_assert_nextlinen("Read 2 bytes from offset 80 to 84"); + ut_assert_console_end(); + + ut_asserteq(ID_CPU_SPEED_2, menu->cur_item_id); + ut_asserteq(ID_AC_MEMORY, menu2->cur_item_id); + return 0; } BOOTSTD_TEST(cedit_cmos, 0); -- cgit v1.3.1