aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--arch/sandbox/dts/sandbox.dtsi12
-rw-r--r--arch/sandbox/dts/test.dts12
-rw-r--r--boot/cedit.c569
-rw-r--r--boot/expo.c15
-rw-r--r--boot/expo_build.c22
-rw-r--r--boot/scene.c18
-rw-r--r--boot/scene_internal.h50
-rw-r--r--boot/scene_menu.c20
-rw-r--r--cmd/cedit.c209
-rw-r--r--doc/develop/cedit.rst169
-rw-r--r--doc/develop/expo.rst48
-rw-r--r--doc/develop/index.rst1
-rw-r--r--doc/usage/cmd/cedit.rst117
-rw-r--r--include/abuf.h9
-rw-r--r--include/cedit.h125
-rw-r--r--include/expo.h33
-rw-r--r--lib/abuf.c5
-rw-r--r--test/boot/Makefile1
-rw-r--r--test/boot/cedit.c198
-rw-r--r--test/boot/expo.c88
-rw-r--r--test/boot/files/expo_ids.h25
-rw-r--r--test/boot/files/expo_layout.dts28
-rw-r--r--test/lib/abuf.c25
-rw-r--r--test/py/tests/test_ut.py4
-rwxr-xr-xtools/expo.py22
25 files changed, 1679 insertions, 146 deletions
diff --git a/arch/sandbox/dts/sandbox.dtsi b/arch/sandbox/dts/sandbox.dtsi
index 8aaf911..ff7e558 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 f351d5c..084cb88 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/boot/cedit.c b/boot/cedit.c
index ee24658..73645f7 100644
--- a/boot/cedit.c
+++ b/boot/cedit.c
@@ -6,15 +6,50 @@
* Written by Simon Glass <sjg@chromium.org>
*/
+#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 db837f7..139d684 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 22f62eb..bb33cc2 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 e523333..6c52948 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 fb1ea55..695a907 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 57ffb52..e0dcd0a 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
*
diff --git a/cmd/cedit.c b/cmd/cedit.c
index 0cae304..2ff284f 100644
--- a/cmd/cedit.c
+++ b/cmd/cedit.c
@@ -7,14 +7,29 @@
*/
#include <common.h>
+#include <abuf.h>
+#include <cedit.h>
#include <command.h>
+#include <dm.h>
#include <expo.h>
#include <fs.h>
+#include <malloc.h>
+#include <mapmem.h>
#include <dm/ofnode.h>
#include <linux/sizes.h>
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[])
{
@@ -53,18 +68,192 @@ 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_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_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_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_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_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[])
{
ofnode node;
int ret;
- if (!cur_exp) {
- printf("No expo loaded\n");
+ if (check_cur_expo())
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)
@@ -84,10 +273,22 @@ static int do_cedit_run(struct cmd_tbl *cmdtp, int flag, int argc,
#ifdef CONFIG_SYS_LONGHELP
static char cedit_help_text[] =
"load <interface> <dev[:part]> <filename> - load config editor\n"
+ "cedit read_fdt <i/f> <dev[:part]> <filename> - read settings\n"
+ "cedit write_fdt <i/f> <dev[:part]> <filename> - 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 */
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(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/develop/cedit.rst b/doc/develop/cedit.rst
new file mode 100644
index 0000000..63dff9d
--- /dev/null
+++ b/doc/develop/cedit.rst
@@ -0,0 +1,169 @@
+.. SPDX-License-Identifier: GPL-2.0+
+
+Configuration Editor
+====================
+
+Introduction
+------------
+
+U-Boot provides a configuration editor which allows settings to be changed in
+a GUI or text environment.
+
+
+This feature is still in development and has a number of limitations. For
+example, cedit only supports menu items (there is no numeric or text entry),
+provides no support for colour text and does not support scrolling. Still it is
+possible to use it for simple applications.
+
+
+Overview
+--------
+
+The configuration editor makes use of :doc:`expo` to build a description of the
+configuration screens and allow user to interact with it.
+
+To create a single-scene cedit for your application:
+
+#. Design the scene, i.e. the objects that need to be present and what their
+ possible values are
+
+#. Enter this in .dts format
+
+#. Create a header file containing the IDs
+
+#. Run the 'expo.py' tool to generate a .dtb file containing the layout, which
+ can be used by U-Boot
+
+#. Use the :doc:`../usage/cmd/cedit` to create the cedit, read the settings,
+ present the cedit to the user and save the settings afterwards.
+
+Each of these is described in a separate section. See :ref:`expo_example` for
+an example file.
+
+
+Design a scene
+--------------
+
+Using a piece of paper or a drawing tool, lay out the objects you want in your
+scene. Typically you will use the default layout engine, which simply puts items
+one after the other from top to bottom. So use a single column and show the
+prompt and value for each object.
+
+For menu items, show one of the values, but keep in mind what else you need.
+
+
+Create an expo-format file
+--------------------------
+
+The description is in the form of a devicetree file, as documented at
+:ref:`expo_format`. Since everything in an expo has an ID number (an integer
+greater than 1) the description is written terms of these IDs. They each have
+an enum value. which is typically taken care of by the `expo.py` tool.
+
+The expo should have a `scenes` node with a named scene as a subnode. Within the
+scene, add properties for the scene, then a subnode for each object in the
+scene.
+
+All object nodes require an `id` value and a `type` property. Other properties
+depend on the type. For example, a menu has a `title` and an `item-label` list
+proving the text for the menu items, as well as an `item-id` list providing the
+ID of each menu item, so it can be selected.
+
+Text properties may have two variants. For example `title` specifies the title
+of a menu, but you can instead use `title-id` to specify the string ID to use as
+the title. String are defined in a separate area, common to the whole expo,
+which contains a subnode for each string. Within that subnode are the ID and the
+`value` (i.e. the text). For now only English is supported, but in future it may
+be possible to append a language identifier to provide other values (e.g.
+'value-es' for Spanish).
+
+
+Create an ID header-file
+------------------------
+
+Expo needs to know the integer value to use for every ID referenced in your
+expo-format file. For example, if you have defined a `cpu-speed` node with an
+id of `ID_CPU_SPEED`, then Expo needs to know the value of `ID_CPU_SPEED`.
+
+When you write C code to use the expo, you may need to know the IDs. For
+example, to find which value the user selected in `cpu-speed` menu, you must
+use the `ID_CPU_SPEED` ID. The ID is the only way to refer to anything in Expo.
+
+Since we need a shared set of IDs, it is best to have a header file containing
+them. Expo supports doing this with an enum, where every ID is listed in the
+enum::
+
+ enum {
+ ZERO,
+
+ ID_PROMPT,
+
+ ID_SCENE1,
+ ID_SCENE1_TITLE,
+ ...
+ };
+
+The C compiler can parse this directly. The `expo.py` tool parses it for expo.
+
+Create a header file containing every ID mentioned in your expo. Try to group
+related things together.
+
+
+Build the expo layout
+---------------------
+
+Use the `expo.py` tool to build a .dtb for your expo::
+
+ ./tools/expo.py -e expo_ids.h -l expo_layout.dts -o expo.dtb
+
+This uses the enum in the provided header file to get the ID numbers, grabs
+the `.dts` file, inserts the ID numbers and then uses the devicetree compiler to
+build a `.dtb` file.
+
+If you get an error::
+
+ Devicetree compiler error:
+ Error: <stdin>:9.19-20 syntax error
+ FATAL ERROR: Unable to parse input tree
+
+that means that something is wrong with your syntax, or perhaps you have an ID
+in the `.dts` file that is not mentioned in your enum. Check both files and try
+again.
+
+
+Use the command interface
+-------------------------
+
+See the :doc:`../usage/cmd/cedit` command for information on available commands.
+Typically you will use `cedit load` to load the `.dtb` file and `cedit run` to
+let the user interact with it.
+
+
+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.
+
+
+Reading and writing settings
+----------------------------
+
+Cedit provides several options for persistent settings:
+
+- Writing an FDT file to a filesystem
+- Writing to U-Boot's environment variables, which are then typically stored in
+ a persistent manner
+- Writing to CMOS RAM registers (common on x86 machines)
+
+For now, reading and writing settings is not automatic. See the
+:doc:`../usage/cmd/cedit` for how to do this on the command line or in a
+script.
diff --git a/doc/develop/expo.rst b/doc/develop/expo.rst
index 2ac4af2..f137619 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:
@@ -358,6 +370,9 @@ The `expo_arrange()` function can be called to arrange the expo objects in a
suitable manner. For each scene it puts the title at the top, the prompt at the
bottom and the objects in order from top to bottom.
+
+.. _expo_example:
+
Expo format example
~~~~~~~~~~~~~~~~~~~
@@ -367,22 +382,27 @@ strings are provided inline in the nodes where they are used.
::
- #define ID_PROMPT 1
- #define ID_SCENE1 2
- #define ID_SCENE1_TITLE 3
+ /* this comment is parsed by the expo.py tool to insert the values below
+
+ enum {
+ ZERO,
+ ID_PROMPT,
+ ID_SCENE1,
+ ID_SCENE1_TITLE,
- #define ID_CPU_SPEED 4
- #define ID_CPU_SPEED_TITLE 5
- #define ID_CPU_SPEED_1 6
- #define ID_CPU_SPEED_2 7
- #define ID_CPU_SPEED_3 8
+ ID_CPU_SPEED,
+ ID_CPU_SPEED_TITLE,
+ ID_CPU_SPEED_1,
+ ID_CPU_SPEED_2,
+ ID_CPU_SPEED_3,
- #define ID_POWER_LOSS 9
- #define ID_AC_OFF 10
- #define ID_AC_ON 11
- #define ID_AC_MEMORY 12
+ ID_POWER_LOSS,
+ ID_AC_OFF,
+ ID_AC_ON,
+ ID_AC_MEMORY,
- #define ID_DYNAMIC_START 13
+ ID_DYNAMIC_START,
+ */
&cedit {
dynamic-start = <ID_DYNAMIC_START>;
@@ -465,7 +485,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
+- Update expo.py tool to check for overlapping names and CMOS locations
.. Simon Glass <sjg@chromium.org>
.. 7-Oct-22
diff --git a/doc/develop/index.rst b/doc/develop/index.rst
index 5b230d0..0d12484 100644
--- a/doc/develop/index.rst
+++ b/doc/develop/index.rst
@@ -38,6 +38,7 @@ Implementation
driver-model/index
environment
expo
+ cedit
event
global_data
logging
diff --git a/doc/usage/cmd/cedit.rst b/doc/usage/cmd/cedit.rst
index 8e1110c..f415b48 100644
--- a/doc/usage/cmd/cedit.rst
+++ b/doc/usage/cmd/cedit.rst
@@ -10,6 +10,11 @@ Synopis
cedit load <interface> <dev[:part]> <filename>
cedit run
+ cedit write_fdt <dev[:part]> <filename>
+ cedit read_fdt <dev[:part]> <filename>
+ cedit write_env [-v]
+ cedit read_env [-v]
+ cedit write_cmos [-v] [dev]
Description
-----------
@@ -22,6 +27,69 @@ It makes use of the expo subsystem.
The description is in the form of a devicetree file, as documented at
:ref:`expo_format`.
+See :doc:`../../develop/cedit` for information about the configuration editor.
+
+cedit load
+~~~~~~~~~~
+
+Loads a configuration-editor description from a file. It creates a new cedit
+structure ready for use. Initially no settings are read, so default values are
+used for each object.
+
+cedit run
+~~~~~~~~~
+
+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.
+
+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 `<name>`,
+cedit looks for a variable called `c.<name>` 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
+~~~~~~~~~~~~~~~
+
+Writes the settings to environment variables. For each menu item the selected
+ID and its text string are written, similar to:
+
+ setenv c.<name> <selected_id>
+ setenv c.<name>-str <selected_id's text string>
+
+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
-------
@@ -29,3 +97,52 @@ 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";
+ };
+ }
+
+ => cedit read_fdt hostfs - settings.dtb
+
+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
+ c.cpu-speed-str=2 GHz
+ c.power-loss=10
+ c.power-loss-str=Always Off
+ ...
+
+ => 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 -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/abuf.h b/include/abuf.h
index 9badda6..be98ec7 100644
--- a/include/abuf.h
+++ b/include/abuf.h
@@ -91,6 +91,15 @@ void abuf_map_sysmem(struct abuf *abuf, ulong addr, size_t size);
bool abuf_realloc(struct abuf *abuf, size_t new_size);
/**
+ * abuf_realloc_inc() - Increment abuf size by a given amount
+ *
+ * @abuf: abuf to adjust
+ * @inc: Size incrmement to use (the buffer size will be increased by this much)
+ * Return: true if OK, false if out of memory
+ */
+bool abuf_realloc_inc(struct abuf *abuf, size_t inc);
+
+/**
* abuf_uninit_move() - Return the allocated contents and uninit the abuf
*
* This returns the abuf data to the caller, allocating it if necessary, so that
diff --git a/include/cedit.h b/include/cedit.h
new file mode 100644
index 0000000..f43cafa
--- /dev/null
+++ b/include/cedit.h
@@ -0,0 +1,125 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright 2023 Google LLC
+ * Written by Simon Glass <sjg@chromium.org>
+ */
+
+#ifndef __CEDIT_H
+#define __CEDIT_H
+
+#include <dm/ofnode_decl.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
+ *
+ * @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);
+
+/**
+ * cedit_prepare() - Prepare to run a cedit
+ *
+ * Set up the video device, select the first scene and highlight the first item.
+ * This ensures that all menus have a selected item.
+ *
+ * @exp: Expo to use
+ * @vid_privp: Set to private data for the video device
+ * @scnp: Set to the first scene
+ * Return: scene ID of first scene if OK, -ve on error
+ */
+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);
+
+/**
+ * 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);
+
+/**
+ * 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);
+
+/*
+ * 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);
+
+/**
+ * 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);
+
+/**
+ * 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/include/expo.h b/include/expo.h
index 0b1d944..a2b3a71 100644
--- a/include/expo.h
+++ b/include/expo.h
@@ -4,14 +4,13 @@
* Written by Simon Glass <sjg@chromium.org>
*/
-#ifndef __SCENE_H
-#define __SCENE_H
+#ifndef __EXPO_H
+#define __EXPO_H
#include <dm/ofnode_decl.h>
#include <linux/list.h>
struct udevice;
-struct video_priv;
/**
* enum expoact_type - types of actions reported by the expo
@@ -188,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 {
@@ -196,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;
};
@@ -676,24 +679,4 @@ 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 */
+#endif /*__EXPO_H */
diff --git a/lib/abuf.c b/lib/abuf.c
index bd27046..ce2cff5 100644
--- a/lib/abuf.c
+++ b/lib/abuf.c
@@ -82,6 +82,11 @@ bool abuf_realloc(struct abuf *abuf, size_t new_size)
}
}
+bool abuf_realloc_inc(struct abuf *abuf, size_t inc)
+{
+ return abuf_realloc(abuf, abuf->size + inc);
+}
+
void *abuf_uninit_move(struct abuf *abuf, size_t *sizep)
{
void *ptr;
diff --git a/test/boot/Makefile b/test/boot/Makefile
index 22ed61c..5294758 100644
--- a/test/boot/Makefile
+++ b/test/boot/Makefile
@@ -6,6 +6,7 @@ obj-$(CONFIG_BOOTSTD) += bootdev.o bootstd_common.o bootflow.o bootmeth.o
obj-$(CONFIG_FIT) += image.o
obj-$(CONFIG_EXPO) += expo.o
+obj-$(CONFIG_CEDIT) += cedit.o
ifdef CONFIG_OF_LIVE
obj-$(CONFIG_BOOTMETH_VBE_SIMPLE) += vbe_simple.o
diff --git a/test/boot/cedit.c b/test/boot/cedit.c
new file mode 100644
index 0000000..ab2b8a1
--- /dev/null
+++ b/test/boot/cedit.c
@@ -0,0 +1,198 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2023 Google LLC
+ * Written by Simon Glass <sjg@chromium.org>
+ */
+
+#include <common.h>
+#include <cedit.h>
+#include <env.h>
+#include <expo.h>
+#include <mapmem.h>
+#include <dm/ofnode.h>
+#include <test/ut.h>
+#include "bootstd_common.h"
+#include <test/cedit-test.h>
+#include "../../boot/scene_internal.h"
+
+/* Check the cedit command */
+static int cedit_base(struct unit_test_state *uts)
+{
+ extern struct expo *cur_exp;
+ struct scene_obj_menu *menu;
+ struct scene_obj_txt *txt;
+ struct expo *exp;
+ struct scene *scn;
+
+ ut_assertok(run_command("cedit load hostfs - cedit.dtb", 0));
+
+ console_record_reset_enable();
+
+ /*
+ * ^N Move down to second menu
+ * ^M Open menu
+ * ^N Move down to second item
+ * ^M Select item
+ * \e Quit
+ */
+ console_in_puts("\x0e\x0d\x0e\x0d\e");
+ ut_assertok(run_command("cedit run", 0));
+
+ exp = cur_exp;
+ scn = expo_lookup_scene_id(exp, exp->scene_id);
+ ut_assertnonnull(scn);
+
+ menu = scene_obj_find(scn, scn->highlight_id, SCENEOBJT_NONE);
+ ut_assertnonnull(menu);
+
+ txt = scene_obj_find(scn, menu->title_id, SCENEOBJT_NONE);
+ ut_assertnonnull(txt);
+ ut_asserteq_str("AC Power", expo_get_str(exp, txt->str_id));
+
+ ut_asserteq(ID_AC_ON, menu->cur_item_id);
+
+ return 0;
+}
+BOOTSTD_TEST(cedit_base, 0);
+
+/* 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;
+ 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));
+
+ /* 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");
+
+ 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_2,
+ ofnode_read_u32_default(node, "cpu-speed", 0));
+ ut_asserteq_str("2.5 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();
+
+ /* 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);
+
+/* Check the cedit write_env and read_env commands */
+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"));
+
+ /* 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);
+
+/* 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();
+
+ /* 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);
diff --git a/test/boot/expo.c b/test/boot/expo.c
index 3898f85..9002740 100644
--- a/test/boot/expo.c
+++ b/test/boot/expo.c
@@ -289,6 +289,33 @@ static int expo_object_attr(struct unit_test_state *uts)
}
BOOTSTD_TEST(expo_object_attr, UT_TESTF_DM | UT_TESTF_SCAN_FDT);
+/**
+ * struct test_iter_priv - private data for expo-iterator test
+ *
+ * @count: number of scene objects
+ * @menu_count: number of menus
+ * @fail_at: item ID at which to return an error
+ */
+struct test_iter_priv {
+ int count;
+ int menu_count;
+ int fail_at;
+};
+
+int h_test_iter(struct scene_obj *obj, void *vpriv)
+{
+ struct test_iter_priv *priv = vpriv;
+
+ if (priv->fail_at == obj->id)
+ return -EINVAL;
+
+ priv->count++;
+ if (obj->type == SCENEOBJT_MENU)
+ priv->menu_count++;
+
+ return 0;
+}
+
/* Check creating a scene with a menu */
static int expo_object_menu(struct unit_test_state *uts)
{
@@ -296,6 +323,7 @@ static int expo_object_menu(struct unit_test_state *uts)
struct scene_menitem *item;
int id, label_id, desc_id, key_id, pointer_id, preview_id;
struct scene_obj_txt *ptr, *name1, *desc1, *key1, *tit, *prev1;
+ struct test_iter_priv priv;
struct scene *scn;
struct expo *exp;
ulong start_mem;
@@ -382,6 +410,23 @@ static int expo_object_menu(struct unit_test_state *uts)
ut_asserteq(menu->obj.dim.y + 32, prev1->obj.dim.y);
ut_asserteq(true, prev1->obj.flags & SCENEOF_HIDE);
+ /* check iterating through scene items */
+ memset(&priv, '\0', sizeof(priv));
+ ut_assertok(expo_iter_scene_objs(exp, h_test_iter, &priv));
+ ut_asserteq(7, priv.count);
+ ut_asserteq(1, priv.menu_count);
+
+ /* check the iterator failing part way through iteration */
+ memset(&priv, '\0', sizeof(priv));
+ priv.fail_at = key_id;
+ ut_asserteq(-EINVAL, expo_iter_scene_objs(exp, h_test_iter, &priv));
+
+ /* 2 items (preview_id and the menuitem) are after key_id, 7 - 2 = 5 */
+ ut_asserteq(5, priv.count);
+
+ /* menu is first, so is still processed */
+ ut_asserteq(1, priv.menu_count);
+
expo_destroy(exp);
ut_assertok(ut_check_delta(start_mem));
@@ -669,46 +714,3 @@ static int expo_test_build(struct unit_test_state *uts)
return 0;
}
BOOTSTD_TEST(expo_test_build, UT_TESTF_DM);
-
-/* Check the cedit command */
-static int expo_cedit(struct unit_test_state *uts)
-{
- extern struct expo *cur_exp;
- struct scene_obj_menu *menu;
- struct scene_obj_txt *txt;
- struct expo *exp;
- struct scene *scn;
-
- if (!IS_ENABLED(CONFIG_CMD_CEDIT))
- return -EAGAIN;
-
- ut_assertok(run_command("cedit load hostfs - cedit.dtb", 0));
-
- console_record_reset_enable();
-
- /*
- * ^N Move down to second menu
- * ^M Open menu
- * ^N Move down to second item
- * ^M Select item
- * \e Quit
- */
- console_in_puts("\x0e\x0d\x0e\x0d\e");
- ut_assertok(run_command("cedit run", 0));
-
- exp = cur_exp;
- scn = expo_lookup_scene_id(exp, exp->scene_id);
- ut_assertnonnull(scn);
-
- menu = scene_obj_find(scn, scn->highlight_id, SCENEOBJT_NONE);
- ut_assertnonnull(menu);
-
- txt = scene_obj_find(scn, menu->title_id, SCENEOBJT_NONE);
- ut_assertnonnull(txt);
- ut_asserteq_str("AC Power", expo_get_str(exp, txt->str_id));
-
- ut_asserteq(ID_AC_ON, menu->cur_item_id);
-
- return 0;
-}
-BOOTSTD_TEST(expo_cedit, UT_TESTF_DM | UT_TESTF_SCAN_FDT);
diff --git a/test/boot/files/expo_ids.h b/test/boot/files/expo_ids.h
new file mode 100644
index 0000000..027d44b
--- /dev/null
+++ b/test/boot/files/expo_ids.h
@@ -0,0 +1,25 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Sample expo screen layout (ID numbers)
+ */
+
+enum {
+ ZERO,
+ ID_PROMPT,
+
+ ID_SCENE1,
+ ID_SCENE1_TITLE,
+
+ ID_CPU_SPEED,
+ ID_CPU_SPEED_TITLE,
+ ID_CPU_SPEED_1,
+ ID_CPU_SPEED_2,
+ ID_CPU_SPEED_3,
+
+ ID_POWER_LOSS,
+ ID_AC_OFF,
+ ID_AC_ON,
+ ID_AC_MEMORY,
+
+ ID_DYNAMIC_START,
+};
diff --git a/test/boot/files/expo_layout.dts b/test/boot/files/expo_layout.dts
index 55d5c91..cb2a674 100644
--- a/test/boot/files/expo_layout.dts
+++ b/test/boot/files/expo_layout.dts
@@ -5,28 +5,7 @@
/dts-v1/;
-/*
-enum {
- ZERO,
- ID_PROMPT,
-
- ID_SCENE1,
- ID_SCENE1_TITLE,
-
- ID_CPU_SPEED,
- ID_CPU_SPEED_TITLE,
- ID_CPU_SPEED_1,
- ID_CPU_SPEED_2,
- ID_CPU_SPEED_3,
-
- ID_POWER_LOSS,
- ID_AC_OFF,
- ID_AC_ON,
- ID_AC_MEMORY,
-
- ID_DYNAMIC_START,
-};
-*/
+/* see expo_ids.h for the IDs */
/ {
dynamic-start = <ID_DYNAMIC_START>;
@@ -59,6 +38,9 @@ enum {
/* IDs for the menu items */
item-id = <ID_CPU_SPEED_1 ID_CPU_SPEED_2
ID_CPU_SPEED_3>;
+
+ start-bit = <0x400>;
+ bit-length = <2>;
};
power-loss {
@@ -70,6 +52,8 @@ enum {
"Memory";
item-id = <ID_AC_OFF ID_AC_ON ID_AC_MEMORY>;
+ start-bit = <0x422>;
+ bit-length = <2>;
};
};
};
diff --git a/test/lib/abuf.c b/test/lib/abuf.c
index 42ee4c1..42803b2 100644
--- a/test/lib/abuf.c
+++ b/test/lib/abuf.c
@@ -155,6 +155,31 @@ static int lib_test_abuf_realloc_size(struct unit_test_state *uts)
}
LIB_TEST(lib_test_abuf_realloc_size, 0);
+/* Test abuf_realloc_inc() */
+static int lib_test_abuf_realloc_inc(struct unit_test_state *uts)
+{
+ struct abuf buf;
+ ulong start;
+
+ start = ut_check_free();
+
+ abuf_init(&buf);
+ ut_asserteq(0, buf.size);
+ ut_asserteq(false, buf.alloced);
+
+ abuf_realloc_inc(&buf, 20);
+ ut_asserteq(20, buf.size);
+ ut_asserteq(true, buf.alloced);
+
+ abuf_uninit(&buf);
+
+ /* Check for memory leaks */
+ ut_assertok(ut_check_delta(start));
+
+ return 0;
+}
+LIB_TEST(lib_test_abuf_realloc_inc, 0);
+
/* Test handling of buffers that are too large */
static int lib_test_abuf_large(struct unit_test_state *uts)
{
diff --git a/test/py/tests/test_ut.py b/test/py/tests/test_ut.py
index aa1d477..6a59c30 100644
--- a/test/py/tests/test_ut.py
+++ b/test/py/tests/test_ut.py
@@ -285,10 +285,12 @@ label Fedora-Workstation-armhfp-31-1.9 (5.3.7-301.fc31.armv7hl)
def setup_cedit_file(cons):
infname = os.path.join(cons.config.source_dir,
'test/boot/files/expo_layout.dts')
+ inhname = os.path.join(cons.config.source_dir,
+ 'test/boot/files/expo_ids.h')
expo_tool = os.path.join(cons.config.source_dir, 'tools/expo.py')
outfname = 'cedit.dtb'
u_boot_utils.run_and_log(
- cons, f'{expo_tool} -e {infname} -l {infname} -o {outfname}')
+ cons, f'{expo_tool} -e {inhname} -l {infname} -o {outfname}')
@pytest.mark.buildconfigspec('ut_dm')
diff --git a/tools/expo.py b/tools/expo.py
index c6eb87a..ea80c70 100755
--- a/tools/expo.py
+++ b/tools/expo.py
@@ -69,7 +69,10 @@ def calc_ids(fname):
def run_expo(args):
"""Run the expo program"""
- ids = calc_ids(args.enum_fname)
+ fname = args.enum_fname or args.layout
+ ids = calc_ids(fname)
+ if not ids:
+ print(f"Warning: No enum ID values found in file '{fname}'")
indata = tools.read_file(args.layout)
@@ -88,10 +91,10 @@ def run_expo(args):
with open('/tmp/asc', 'wb') as outf:
outf.write(data)
- proc = subprocess.run('dtc', input=data, capture_output=True, check=True)
+ proc = subprocess.run('dtc', input=data, capture_output=True)
edtb = proc.stdout
if proc.stderr:
- print(proc.stderr)
+ print(f"Devicetree compiler error:\n{proc.stderr.decode('utf-8')}")
return 1
tools.write_file(args.outfile, edtb)
return 0
@@ -109,11 +112,13 @@ def parse_args(argv):
args is a list of string arguments
"""
parser = argparse.ArgumentParser()
+ parser.add_argument('-D', '--debug', action='store_true',
+ help='Enable full debug traceback')
parser.add_argument('-e', '--enum-fname', type=str,
- help='C file containing enum declaration for expo items')
- parser.add_argument('-l', '--layout', type=str,
- help='Devicetree file source .dts for expo layout')
- parser.add_argument('-o', '--outfile', type=str,
+ help='.dts or C file containing enum declaration for expo items')
+ parser.add_argument('-l', '--layout', type=str, required=True,
+ help='Devicetree file source .dts for expo layout (and perhaps enums)')
+ parser.add_argument('-o', '--outfile', type=str, required=True,
help='Filename to write expo layout dtb')
return parser.parse_args(argv)
@@ -122,6 +127,9 @@ def start_expo():
"""Start the expo program"""
args = parse_args(sys.argv[1:])
+ if not args.debug:
+ sys.tracebacklimit = 0
+
ret_code = run_expo(args)
sys.exit(ret_code)