diff options
author | Simon Glass | 2023-06-01 10:23:01 -0600 |
---|---|---|
committer | Tom Rini | 2023-07-14 12:54:51 -0400 |
commit | 82cafee133ee5c087449761988c096fc26a17cf6 (patch) | |
tree | 8558fb8f832174e54cc1ed454b163e3b870b36ff /boot | |
parent | 7230fdb3837ad745adff4cf129dd04e893fe0a36 (diff) |
expo: Support building an expo from a description file
The only way to create an expo at present is by calling the functions to
create each object. It is useful to have more data-driven approach, where
the objects can be specified in a suitable file format and created from
that. This makes testing easier as well.
Add support for describing an expo in a devicetree node. This allows more
complex tests to be set up, as well as providing an easier format for
users. It also provides a better basis for the upcoming configuration
editor.
Signed-off-by: Simon Glass <sjg@chromium.org>
Diffstat (limited to 'boot')
-rw-r--r-- | boot/Makefile | 2 | ||||
-rw-r--r-- | boot/expo.c | 1 | ||||
-rw-r--r-- | boot/expo_build.c | 400 |
3 files changed, 402 insertions, 1 deletions
diff --git a/boot/Makefile b/boot/Makefile index f94c31d922d..28c4e55ca65 100644 --- a/boot/Makefile +++ b/boot/Makefile @@ -50,7 +50,7 @@ ifdef CONFIG_SPL_BUILD obj-$(CONFIG_SPL_LOAD_FIT) += common_fit.o endif -obj-$(CONFIG_$(SPL_TPL_)EXPO) += expo.o scene.o scene_menu.o +obj-$(CONFIG_$(SPL_TPL_)EXPO) += expo.o scene.o scene_menu.o expo_build.o obj-$(CONFIG_$(SPL_TPL_)BOOTMETH_VBE) += vbe.o obj-$(CONFIG_$(SPL_TPL_)BOOTMETH_VBE_REQUEST) += vbe_request.o diff --git a/boot/expo.c b/boot/expo.c index 8c6fbc0e30b..db837f7b492 100644 --- a/boot/expo.c +++ b/boot/expo.c @@ -58,6 +58,7 @@ void expo_destroy(struct expo *exp) uint resolve_id(struct expo *exp, uint id) { + log_debug("resolve id %d\n", id); if (!id) id = exp->next_id++; else if (id >= exp->next_id) diff --git a/boot/expo_build.c b/boot/expo_build.c new file mode 100644 index 00000000000..7e61ab06a8d --- /dev/null +++ b/boot/expo_build.c @@ -0,0 +1,400 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Building an expo from an FDT description + * + * Copyright 2022 Google LLC + * Written by Simon Glass <sjg@chromium.org> + */ + +#define LOG_CATEGORY LOGC_EXPO + +#include <common.h> +#include <expo.h> +#include <fdtdec.h> +#include <log.h> +#include <malloc.h> +#include <dm/ofnode.h> +#include <linux/libfdt.h> + +/** + * struct build_info - Information to use when building + * + * @str_for_id: String for each ID in use, NULL if empty. The string is NULL + * if there is nothing for this ID. Since ID 0 is never used, the first + * element of this array is always NULL + * @str_count: Number of entries in @str_for_id + */ +struct build_info { + const char **str_for_id; + int str_count; +}; + +/** + * add_txt_str - Add a string or lookup its ID, then add to expo + * + * @info: Build information + * @node: Node describing scene + * @scn: Scene to add to + * @find_name: Name to look for (e.g. "title"). This will find a property called + * "title" if it exists, else will look up the string for "title-id" + * Return: ID of added string, or -ve on error + */ +int add_txt_str(struct build_info *info, ofnode node, struct scene *scn, + const char *find_name, uint obj_id) +{ + const char *text; + uint str_id; + int ret; + + text = ofnode_read_string(node, find_name); + if (!text) { + char name[40]; + u32 id; + + snprintf(name, sizeof(name), "%s-id", find_name); + ret = ofnode_read_u32(node, name, &id); + if (ret) + return log_msg_ret("id", -EINVAL); + + if (id >= info->str_count) + return log_msg_ret("id", -E2BIG); + text = info->str_for_id[id]; + if (!text) + return log_msg_ret("id", -EINVAL); + } + + ret = expo_str(scn->expo, find_name, 0, text); + if (ret < 0) + return log_msg_ret("add", ret); + str_id = ret; + + ret = scene_txt_str(scn, find_name, obj_id, str_id, text, NULL); + if (ret < 0) + return log_msg_ret("add", ret); + + return ret; +} + +/** + * add_txt_str_list - Add a list string or lookup its ID, then add to expo + * + * @info: Build information + * @node: Node describing scene + * @scn: Scene to add to + * @find_name: Name to look for (e.g. "title"). This will find a string-list + * property called "title" if it exists, else will look up the string in the + * "title-id" string list. + * Return: ID of added string, or -ve on error + */ +int add_txt_str_list(struct build_info *info, ofnode node, struct scene *scn, + const char *find_name, int index, uint obj_id) +{ + const char *text; + uint str_id; + int ret; + + ret = ofnode_read_string_index(node, find_name, index, &text); + if (ret) { + char name[40]; + u32 id; + + snprintf(name, sizeof(name), "%s-id", find_name); + ret = ofnode_read_u32_index(node, name, index, &id); + if (ret) + return log_msg_ret("id", -ENOENT); + + if (id >= info->str_count) + return log_msg_ret("id", -E2BIG); + text = info->str_for_id[id]; + if (!text) + return log_msg_ret("id", -EINVAL); + } + + ret = expo_str(scn->expo, find_name, 0, text); + if (ret < 0) + return log_msg_ret("add", ret); + str_id = ret; + + ret = scene_txt_str(scn, find_name, obj_id, str_id, text, NULL); + if (ret < 0) + return log_msg_ret("add", ret); + + return ret; +} + +/* + * build_element() - Handle creating a text object from a label + * + * Look up a property called @label or @label-id and create a string for it + */ +int build_element(void *ldtb, int node, const char *label) +{ + return 0; +} + +/** + * read_strings() - Read in the list of strings + * + * Read the strings into an ID-indexed list, so they can be used for building + * an expo. The strings are in a /strings node and each has its own subnode + * containing the ID and the string itself: + * + * example { + * id = <123>; + * value = "This is a test"; + * }; + * + * Future work may add support for unicode and multiple languages + * + * @info: Build information + * @root: Root node to read from + * Returns: 0 if OK, -ENOMEM if out of memory, -EINVAL if there is a format + * error + */ +static int read_strings(struct build_info *info, ofnode root) +{ + ofnode strings, node; + + strings = ofnode_find_subnode(root, "strings"); + if (!ofnode_valid(strings)) + return log_msg_ret("str", -EINVAL); + + ofnode_for_each_subnode(node, strings) { + const char *val; + int ret; + u32 id; + + ret = ofnode_read_u32(node, "id", &id); + if (ret) + return log_msg_ret("id", -EINVAL); + val = ofnode_read_string(node, "value"); + if (!val) + return log_msg_ret("val", -EINVAL); + + if (id >= info->str_count) { + int new_count = info->str_count + 20; + void *new_arr; + + new_arr = realloc(info->str_for_id, + new_count * sizeof(char *)); + if (!new_arr) + return log_msg_ret("id", -ENOMEM); + memset(new_arr + info->str_count, '\0', + (new_count - info->str_count) * sizeof(char *)); + info->str_for_id = new_arr; + info->str_count = new_count; + } + + info->str_for_id[id] = val; + } + + return 0; +} + +/** + * list_strings() - List the available strings with their IDs + * + * @info: Build information + */ +static void list_strings(struct build_info *info) +{ + int i; + + for (i = 0; i < info->str_count; i++) { + if (info->str_for_id[i]) + printf("%3d %s\n", i, info->str_for_id[i]); + } +} + +/** + * menu_build() - Build a menu and add it to a scene + * + * See doc/developer/expo.rst for a description of the format + * + * @info: Build information + * @node: Node containing the menu description + * @scn: Scene to add the menu to + * Returns: 0 if OK, -ENOMEM if out of memory, -EINVAL if there is a format + * error, -ENOENT if there is a references to a non-existent string + */ +static int menu_build(struct build_info *info, ofnode node, struct scene *scn) +{ + struct scene_obj_menu *menu; + uint title_id, menu_id; + const u32 *item_ids; + int ret, size, i; + const char *name; + u32 id; + + name = ofnode_get_name(node); + ret = ofnode_read_u32(node, "id", &id); + if (ret) + return log_msg_ret("id", -EINVAL); + + ret = scene_menu(scn, name, id, &menu); + if (ret < 0) + return log_msg_ret("men", ret); + menu_id = ret; + + /* Set the title */ + ret = add_txt_str(info, node, scn, "title", 0); + if (ret < 0) + return log_msg_ret("tit", ret); + title_id = ret; + ret = scene_menu_set_title(scn, menu_id, title_id); + + item_ids = ofnode_read_prop(node, "item-id", &size); + if (!item_ids) + return log_msg_ret("itm", -EINVAL); + if (!size || size % sizeof(u32)) + return log_msg_ret("isz", -EINVAL); + size /= sizeof(u32); + + for (i = 0; i < size; i++) { + struct scene_menitem *item; + uint label, key, desc; + + ret = add_txt_str_list(info, node, scn, "item-label", i, 0); + if (ret < 0 && ret != -ENOENT) + return log_msg_ret("lab", ret); + label = max(0, ret); + + ret = add_txt_str_list(info, node, scn, "key-label", i, 0); + if (ret < 0 && ret != -ENOENT) + return log_msg_ret("key", ret); + key = max(0, ret); + + ret = add_txt_str_list(info, node, scn, "desc-label", i, 0); + if (ret < 0 && ret != -ENOENT) + return log_msg_ret("lab", ret); + desc = max(0, ret); + + ret = scene_menuitem(scn, menu_id, simple_xtoa(i), + fdt32_to_cpu(item_ids[i]), key, label, + desc, 0, 0, &item); + if (ret < 0) + return log_msg_ret("mi", ret); + } + + return 0; +} + +/** + * menu_build() - Build an expo object and add it to a scene + * + * See doc/developer/expo.rst for a description of the format + * + * @info: Build information + * @node: Node containing the object description + * @scn: Scene to add the object to + * Returns: 0 if OK, -ENOMEM if out of memory, -EINVAL if there is a format + * error, -ENOENT if there is a references to a non-existent string + */ +static int obj_build(struct build_info *info, ofnode node, struct scene *scn) +{ + const char *type; + u32 id; + int ret; + + log_debug("- object %s\n", ofnode_get_name(node)); + ret = ofnode_read_u32(node, "id", &id); + if (ret) + return log_msg_ret("id", -EINVAL); + + type = ofnode_read_string(node, "type"); + if (!type) + return log_msg_ret("typ", -EINVAL); + + if (!strcmp("menu", type)) + ret = menu_build(info, node, scn); + else + ret = -EINVAL; + if (ret) + return log_msg_ret("bld", ret); + + return 0; +} + +/** + * scene_build() - Build a scene and all its objects + * + * See doc/developer/expo.rst for a description of the format + * + * @info: Build information + * @node: Node containing the scene description + * @scn: Scene to add the object to + * Returns: 0 if OK, -ENOMEM if out of memory, -EINVAL if there is a format + * error, -ENOENT if there is a references to a non-existent string + */ +static int scene_build(struct build_info *info, ofnode scn_node, + struct expo *exp) +{ + const char *name; + struct scene *scn; + uint id, title_id; + ofnode node; + int ret; + + name = ofnode_get_name(scn_node); + log_debug("Building scene %s\n", name); + ret = ofnode_read_u32(scn_node, "id", &id); + if (ret) + return log_msg_ret("id", -EINVAL); + + ret = scene_new(exp, name, id, &scn); + if (ret < 0) + return log_msg_ret("scn", ret); + + ret = add_txt_str(info, scn_node, scn, "title", 0); + if (ret < 0) + return log_msg_ret("tit", ret); + title_id = ret; + scene_title_set(scn, title_id); + + ret = add_txt_str(info, scn_node, scn, "prompt", 0); + if (ret < 0) + return log_msg_ret("pr", ret); + + ofnode_for_each_subnode(node, scn_node) { + ret = obj_build(info, node, scn); + if (ret < 0) + return log_msg_ret("mit", ret); + } + + return 0; +} + +int expo_build(ofnode root, struct expo **expp) +{ + struct build_info info; + ofnode scenes, node; + struct expo *exp; + u32 dyn_start; + int ret; + + memset(&info, '\0', sizeof(info)); + ret = read_strings(&info, root); + if (ret) + return log_msg_ret("str", ret); + list_strings(&info); + + ret = expo_new("name", NULL, &exp); + if (ret) + return log_msg_ret("exp", ret); + + if (!ofnode_read_u32(root, "dynamic-start", &dyn_start)) + expo_set_dynamic_start(exp, dyn_start); + + scenes = ofnode_find_subnode(root, "scenes"); + if (!ofnode_valid(scenes)) + return log_msg_ret("sno", -EINVAL); + + ofnode_for_each_subnode(node, scenes) { + ret = scene_build(&info, node, exp); + if (ret < 0) + return log_msg_ret("scn", ret); + } + *expp = exp; + + return 0; +} |