diff options
| author | Tom Rini <[email protected]> | 2023-08-25 17:52:59 -0400 |
|---|---|---|
| committer | Tom Rini <[email protected]> | 2023-08-25 17:52:59 -0400 |
| commit | 05763b71d2fcfe9729bf5ef0b14bd00c3af0c985 (patch) | |
| tree | 8a62bb1cda41f5fb2ff2cb0211e927e45645e18a /boot | |
| parent | 7c6b18fb5494a1da2421b16c42d31fc466c38362 (diff) | |
| parent | 84b08afcbb8f8b4402b940d87bf5822984eedb3d (diff) | |
Merge branch '2023-08-25-add-persistent-config-editor-via-expo' into next
To quote the author:
So far cedit does not support reading and writing the configuration.
This series add several features related to this:
First, it adds support for using a file on a filesystem. This is in
FDT format and provides enough information to reset the cedit back to
the saved settings.
Second, it adds support for using the U-Boot environment. Since the
environment is generally saved across reboots, this feature provides an
easy way of storing the state on most boards. The variables all have a
'c.' prefix to avoid confusion with other variables.
Finally it adds support for using CMOS RAM. This is commonly used on x86
devices to store BIOS settings. The expo schema provides information on
the register layout.
Some other minor tweaks and improvements are included along the way.
Diffstat (limited to 'boot')
| -rw-r--r-- | boot/cedit.c | 569 | ||||
| -rw-r--r-- | boot/expo.c | 15 | ||||
| -rw-r--r-- | boot/expo_build.c | 22 | ||||
| -rw-r--r-- | boot/scene.c | 18 | ||||
| -rw-r--r-- | boot/scene_internal.h | 50 | ||||
| -rw-r--r-- | boot/scene_menu.c | 20 |
6 files changed, 676 insertions, 18 deletions
diff --git a/boot/cedit.c b/boot/cedit.c index ee24658917b..73645f70b6c 100644 --- a/boot/cedit.c +++ b/boot/cedit.c @@ -6,15 +6,50 @@ * Written by Simon Glass <[email protected]> */ +#define LOG_CATEGORY LOGC_EXPO + #include <common.h> +#include <abuf.h> +#include <cedit.h> #include <cli.h> #include <dm.h> +#include <env.h> #include <expo.h> +#include <malloc.h> #include <menu.h> +#include <rtc.h> #include <video.h> #include <linux/delay.h> #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 + * @dev: RTC device to write to + */ +struct cedit_iter_priv { + struct abuf *buf; + ofnode node; + bool verbose; + u8 *mask; + u8 *value; + struct udevice *dev; +}; + int cedit_arange(struct expo *exp, struct video_priv *vpriv, uint scene_id) { struct scene_obj_txt *txt; @@ -46,18 +81,15 @@ int cedit_arange(struct expo *exp, struct video_priv *vpriv, uint scene_id) return 0; } -int cedit_run(struct expo *exp) +int cedit_prepare(struct expo *exp, struct video_priv **vid_privp, + struct scene **scnp) { - 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; + uint scene_id; int ret; - cli_ch_init(cch); - /* For now we only support a video console */ ret = uclass_first_device_err(UCLASS_VIDEO, &dev); if (ret) @@ -92,6 +124,27 @@ int cedit_run(struct expo *exp) if (ret) return log_msg_ret("dim", ret); + *vid_privp = vid_priv; + *scnp = scn; + + return scene_id; +} + +int cedit_run(struct expo *exp) +{ + struct cli_ch_state s_cch, *cch = &s_cch; + struct video_priv *vid_priv; + uint scene_id; + struct scene *scn; + bool done; + int ret; + + cli_ch_init(cch); + ret = cedit_prepare(exp, &vid_priv, &scn); + if (ret < 0) + return log_msg_ret("prep", ret); + scene_id = ret; + done = false; do { struct expo_action act; @@ -161,3 +214,507 @@ 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 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; + 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 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); + + 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; + 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; +} + +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; +} + +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; +} + +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; + + 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; +} + +/** + * 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; +} + +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/expo.c b/boot/expo.c index db837f7b492..139d684f8e6 100644 --- a/boot/expo.c +++ b/boot/expo.c @@ -266,3 +266,18 @@ int expo_apply_theme(struct expo *exp, ofnode node) return 0; } + +int expo_iter_scene_objs(struct expo *exp, expo_scene_obj_iterator iter, + void *priv) +{ + struct scene *scn; + int ret; + + list_for_each_entry(scn, &exp->scene_head, sibling) { + ret = scene_iter_objs(scn, iter, priv); + if (ret) + return log_msg_ret("wr", ret); + } + + return 0; +} diff --git a/boot/expo_build.c b/boot/expo_build.c index 22f62eb54bc..bb33cc2a33f 100644 --- a/boot/expo_build.c +++ b/boot/expo_build.c @@ -214,22 +214,21 @@ static void list_strings(struct build_info *info) * @info: Build information * @node: Node containing the menu description * @scn: Scene to add the menu to + * @id: ID for the menu + * @objp: Returns the object pointer * 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) +static int menu_build(struct build_info *info, ofnode node, struct scene *scn, + uint id, struct scene_obj **objp) { 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) @@ -275,12 +274,13 @@ static int menu_build(struct build_info *info, ofnode node, struct scene *scn) if (ret < 0) return log_msg_ret("mi", ret); } + *objp = &menu->obj; return 0; } /** - * menu_build() - Build an expo object and add it to a scene + * obj_build() - Build an expo object and add it to a scene * * See doc/developer/expo.rst for a description of the format * @@ -292,8 +292,9 @@ static int menu_build(struct build_info *info, ofnode node, struct scene *scn) */ 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)); @@ -306,12 +307,17 @@ static int obj_build(struct build_info *info, ofnode node, struct scene *scn) return log_msg_ret("typ", -EINVAL); if (!strcmp("menu", type)) - ret = menu_build(info, node, scn); + ret = menu_build(info, node, scn, id, &obj); else ret = -EINVAL; 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/boot/scene.c b/boot/scene.c index e52333371f9..6c52948eb69 100644 --- a/boot/scene.c +++ b/boot/scene.c @@ -79,7 +79,7 @@ int scene_obj_count(struct scene *scn) return count; } -void *scene_obj_find(struct scene *scn, uint id, enum scene_obj_t type) +void *scene_obj_find(const struct scene *scn, uint id, enum scene_obj_t type) { struct scene_obj *obj; @@ -681,3 +681,19 @@ int scene_set_open(struct scene *scn, uint id, bool open) return 0; } + +int scene_iter_objs(struct scene *scn, expo_scene_obj_iterator iter, + void *priv) +{ + struct scene_obj *obj; + + list_for_each_entry(obj, &scn->obj_head, sibling) { + int ret; + + ret = iter(obj, priv); + if (ret) + return log_msg_ret("itr", ret); + } + + return 0; +} diff --git a/boot/scene_internal.h b/boot/scene_internal.h index fb1ea5533b9..695a907dc6a 100644 --- a/boot/scene_internal.h +++ b/boot/scene_internal.h @@ -9,6 +9,8 @@ #ifndef __SCENE_INTERNAL_H #define __SCENE_INTERNAL_H +typedef int (*expo_scene_obj_iterator)(struct scene_obj *obj, void *priv); + /** * expo_lookup_scene_id() - Look up a scene ID * @@ -38,7 +40,7 @@ uint resolve_id(struct expo *exp, uint id); * @type: Type of the object, or SCENEOBJT_NONE to match any type * Returns: Object found, or NULL if not found */ -void *scene_obj_find(struct scene *scn, uint id, enum scene_obj_t type); +void *scene_obj_find(const struct scene *scn, uint id, enum scene_obj_t type); /** * scene_obj_find_by_name() - Find an object in a scene by name @@ -198,4 +200,50 @@ int scene_menu_render_deps(struct scene *scn, struct scene_obj_menu *menu); */ int scene_menu_calc_dims(struct scene_obj_menu *menu); +/** + * scene_iter_objs() - Iterate through all scene objects + * + * @scn: Scene to process + * @iter: Iterator to call on each object + * @priv: Private data to pass to the iterator, in addition to the object + * Return: 0 if OK, -ve on error + */ +int scene_iter_objs(struct scene *scn, expo_scene_obj_iterator iter, + void *priv); + +/** + * expo_iter_scene_objects() - Iterate through all scene objects + * + * @exp: Expo to process + * @iter: Iterator to call on each object + * @priv: Private data to pass to the iterator, in addition to the object + * Return: 0 if OK, -ve on error + */ +int expo_iter_scene_objs(struct expo *exp, expo_scene_obj_iterator iter, + void *priv); + +/** + * scene_menuitem_find() - Find the menu item for an ID + * + * Looks up the menu to find the item with the given ID + * + * @menu: Menu to check + * @id: ID to look for + * Return: Menu item, or NULL if not found + */ +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 57ffb523ff3..e0dcd0a4e04 100644 --- a/boot/scene_menu.c +++ b/boot/scene_menu.c @@ -33,8 +33,8 @@ void scene_menu_destroy(struct scene_obj_menu *menu) scene_menuitem_destroy(item); } -static struct scene_menitem *scene_menuitem_find(struct scene_obj_menu *menu, - int id) +struct scene_menitem *scene_menuitem_find(const struct scene_obj_menu *menu, + int id) { struct scene_menitem *item; @@ -46,6 +46,22 @@ static struct scene_menitem *scene_menuitem_find(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 * |
