1 // SPDX-License-Identifier: GPL-2.0+
3 * Implementation of configuration editor
5 * Copyright 2023 Google LLC
9 #define LOG_CATEGORY LOGC_EXPO
22 #include <linux/delay.h>
23 #include "scene_internal.h"
27 CMOS_MAX_BYTES = CMOS_MAX_BITS / 8,
30 #define CMOS_BYTE(bit) ((bit) / 8)
31 #define CMOS_BIT(bit) ((bit) % 8)
34 * struct cedit_iter_priv - private data for cedit operations
36 * @buf: Buffer to use when writing settings to the devicetree
37 * @node: Node to read from when reading settings from devicetree
38 * @verbose: true to show writing to environment variables
39 * @mask: Mask bits for the CMOS RAM. If a bit is set the byte containing it
41 * @value: Value bits for CMOS RAM. This is the actual value written
42 * @dev: RTC device to write to
44 struct cedit_iter_priv {
53 int cedit_arange(struct expo *exp, struct video_priv *vpriv, uint scene_id)
55 struct scene_obj_txt *txt;
56 struct scene_obj *obj;
60 scn = expo_lookup_scene_id(exp, scene_id);
62 return log_msg_ret("scn", -ENOENT);
64 txt = scene_obj_find_by_name(scn, "prompt");
66 scene_obj_set_pos(scn, txt->obj.id, 0, vpriv->ysize - 50);
68 txt = scene_obj_find_by_name(scn, "title");
70 scene_obj_set_pos(scn, txt->obj.id, 200, 10);
73 list_for_each_entry(obj, &scn->obj_head, sibling) {
80 scene_obj_set_pos(scn, obj->id, 50, y);
81 scene_menu_arrange(scn, (struct scene_obj_menu *)obj);
84 case SCENEOBJT_TEXTLINE:
85 scene_obj_set_pos(scn, obj->id, 50, y);
86 scene_textline_arrange(scn,
87 (struct scene_obj_textline *)obj);
96 int cedit_prepare(struct expo *exp, struct video_priv **vid_privp,
99 struct video_priv *vid_priv;
105 /* For now we only support a video console */
106 ret = uclass_first_device_err(UCLASS_VIDEO, &dev);
108 return log_msg_ret("vid", ret);
109 ret = expo_set_display(exp, dev);
111 return log_msg_ret("dis", ret);
113 ret = expo_first_scene_id(exp);
115 return log_msg_ret("scn", ret);
118 ret = expo_set_scene_id(exp, scene_id);
120 return log_msg_ret("sid", ret);
124 /* This is not supported for now */
126 expo_set_text_mode(exp, true);
128 vid_priv = dev_get_uclass_priv(dev);
130 scn = expo_lookup_scene_id(exp, scene_id);
131 scene_highlight_first(scn);
133 cedit_arange(exp, vid_priv, scene_id);
135 ret = expo_calc_dims(exp);
137 return log_msg_ret("dim", ret);
139 *vid_privp = vid_priv;
145 int cedit_run(struct expo *exp)
147 struct cli_ch_state s_cch, *cch = &s_cch;
148 struct video_priv *vid_priv;
155 ret = cedit_prepare(exp, &vid_priv, &scn);
157 return log_msg_ret("prep", ret);
162 struct expo_action act;
165 ret = expo_render(exp);
169 ichar = cli_ch_process(cch, 0);
171 while (!ichar && !tstc()) {
174 ichar = cli_ch_process(cch, -ETIMEDOUT);
178 ichar = cli_ch_process(cch, ichar);
184 key = bootmenu_conv_key(ichar);
185 if (key == BKEY_NONE || key >= BKEY_FIRST_EXTRA)
191 ret = expo_send_key(exp, key);
195 ret = expo_action_get(exp, &act);
198 case EXPOACT_POINT_OBJ:
199 scene_set_highlight_id(scn, act.select.id);
200 cedit_arange(exp, vid_priv, scene_id);
203 scene_set_open(scn, act.select.id, true);
204 cedit_arange(exp, vid_priv, scene_id);
207 scene_set_open(scn, act.select.id, false);
208 cedit_arange(exp, vid_priv, scene_id);
211 scene_set_open(scn, scn->highlight_id, false);
212 cedit_arange(exp, vid_priv, scene_id);
215 log_debug("quitting\n");
225 return log_msg_ret("end", ret);
230 static int check_space(int ret, struct abuf *buf)
232 if (ret == -FDT_ERR_NOSPACE) {
233 if (!abuf_realloc_inc(buf, CEDIT_SIZE_INC))
234 return log_msg_ret("spc", -ENOMEM);
235 ret = fdt_resize(abuf_data(buf), abuf_data(buf),
238 return log_msg_ret("res", -EFAULT);
245 * get_cur_menuitem_text() - Get the text of the currently selected item
247 * Looks up the object for the current item, finds text object for it and looks
248 * up the string for that text
250 * @menu: Menu to look at
251 * @strp: Returns a pointer to the next
252 * Return: 0 if OK, -ENOENT if something was not found
254 static int get_cur_menuitem_text(const struct scene_obj_menu *menu,
257 struct scene *scn = menu->obj.scene;
258 const struct scene_menitem *mi;
259 const struct scene_obj_txt *txt;
262 mi = scene_menuitem_find(menu, menu->cur_item_id);
264 return log_msg_ret("mi", -ENOENT);
266 txt = scene_obj_find(scn, mi->label_id, SCENEOBJT_TEXT);
268 return log_msg_ret("txt", -ENOENT);
270 str = expo_get_str(scn->expo, txt->str_id);
272 return log_msg_ret("str", -ENOENT);
278 static int write_dt_string(struct abuf *buf, const char *name, const char *str)
282 /* write the text of the current item */
284 for (i = 0; ret && i < 2; i++) {
285 ret = fdt_property_string(abuf_data(buf), name, str);
287 ret = check_space(ret, buf);
289 return log_msg_ret("rs2", -ENOMEM);
293 /* this should not happen */
295 return log_msg_ret("str", -EFAULT);
300 static int h_write_settings(struct scene_obj *obj, void *vpriv)
302 struct cedit_iter_priv *priv = vpriv;
303 struct abuf *buf = priv->buf;
308 case SCENEOBJT_IMAGE:
311 case SCENEOBJT_TEXTLINE: {
312 const struct scene_obj_textline *tline;
314 tline = (struct scene_obj_textline *)obj;
315 ret = write_dt_string(buf, obj->name, abuf_data(&tline->buf));
317 return log_msg_ret("wr2", ret);
320 case SCENEOBJT_MENU: {
321 const struct scene_obj_menu *menu;
326 /* write the ID of the current item */
327 menu = (struct scene_obj_menu *)obj;
329 for (i = 0; ret && i < 2; i++) {
330 ret = fdt_property_u32(abuf_data(buf), obj->name,
333 ret = check_space(ret, buf);
335 return log_msg_ret("res", -ENOMEM);
338 /* this should not happen */
340 return log_msg_ret("wrt", -EFAULT);
342 ret = get_cur_menuitem_text(menu, &str);
344 return log_msg_ret("mis", ret);
346 /* write the text of the current item */
347 snprintf(name, sizeof(name), "%s-str", obj->name);
348 ret = write_dt_string(buf, name, str);
350 return log_msg_ret("wr2", ret);
359 int cedit_write_settings(struct expo *exp, struct abuf *buf)
361 struct cedit_iter_priv priv;
366 if (!abuf_realloc(buf, CEDIT_SIZE_INC))
367 return log_msg_ret("buf", -ENOMEM);
369 fdt = abuf_data(buf);
370 ret = fdt_create(fdt, abuf_size(buf));
372 ret = fdt_finish_reservemap(fdt);
374 ret = fdt_begin_node(fdt, "");
376 ret = fdt_begin_node(fdt, CEDIT_NODE_NAME);
378 log_debug("Failed to start FDT (err=%d)\n", ret);
379 return log_msg_ret("sta", -EINVAL);
382 /* write out the items */
384 ret = expo_iter_scene_objs(exp, h_write_settings, &priv);
386 log_debug("Failed to write settings (err=%d)\n", ret);
387 return log_msg_ret("set", ret);
390 ret = fdt_end_node(fdt);
392 ret = fdt_end_node(fdt);
394 ret = fdt_finish(fdt);
396 log_debug("Failed to finish FDT (err=%d)\n", ret);
397 return log_msg_ret("fin", -EINVAL);
403 static int h_read_settings(struct scene_obj *obj, void *vpriv)
405 struct cedit_iter_priv *priv = vpriv;
406 ofnode node = priv->node;
410 case SCENEOBJT_IMAGE:
413 case SCENEOBJT_TEXTLINE: {
414 const struct scene_obj_textline *tline;
418 tline = (struct scene_obj_textline *)obj;
420 val = ofnode_read_prop(node, obj->name, &len);
421 if (len >= tline->max_chars)
422 return log_msg_ret("str", -ENOSPC);
423 strcpy(abuf_data(&tline->buf), val);
426 case SCENEOBJT_MENU: {
427 struct scene_obj_menu *menu;
430 if (ofnode_read_u32(node, obj->name, &val))
431 return log_msg_ret("rd", -ENOENT);
432 menu = (struct scene_obj_menu *)obj;
433 menu->cur_item_id = val;
442 int cedit_read_settings(struct expo *exp, oftree tree)
444 struct cedit_iter_priv priv;
448 root = oftree_root(tree);
449 if (!ofnode_valid(root))
450 return log_msg_ret("roo", -ENOENT);
451 node = ofnode_find_subnode(root, CEDIT_NODE_NAME);
452 if (!ofnode_valid(node))
453 return log_msg_ret("pat", -ENOENT);
455 /* read in the items */
457 ret = expo_iter_scene_objs(exp, h_read_settings, &priv);
459 log_debug("Failed to read settings (err=%d)\n", ret);
460 return log_msg_ret("set", ret);
466 static int h_write_settings_env(struct scene_obj *obj, void *vpriv)
468 const struct scene_obj_menu *menu;
469 struct cedit_iter_priv *priv = vpriv;
470 char name[80], var[60];
474 snprintf(var, sizeof(var), "c.%s", obj->name);
478 case SCENEOBJT_IMAGE:
482 menu = (struct scene_obj_menu *)obj;
483 val = menu->cur_item_id;
486 printf("%s=%d\n", var, val);
488 ret = env_set_ulong(var, val);
490 return log_msg_ret("set", ret);
492 ret = get_cur_menuitem_text(menu, &str);
494 return log_msg_ret("mis", ret);
496 snprintf(name, sizeof(name), "c.%s-str", obj->name);
498 printf("%s=%s\n", name, str);
500 ret = env_set(name, str);
502 return log_msg_ret("st2", ret);
504 case SCENEOBJT_TEXTLINE: {
505 const struct scene_obj_textline *tline;
507 tline = (struct scene_obj_textline *)obj;
508 str = abuf_data(&tline->buf);
509 ret = env_set(var, str);
511 return log_msg_ret("set", ret);
514 printf("%s=%s\n", var, str);
523 int cedit_write_settings_env(struct expo *exp, bool verbose)
525 struct cedit_iter_priv priv;
528 /* write out the items */
529 priv.verbose = verbose;
530 ret = expo_iter_scene_objs(exp, h_write_settings_env, &priv);
532 log_debug("Failed to write settings to env (err=%d)\n", ret);
533 return log_msg_ret("set", ret);
539 static int h_read_settings_env(struct scene_obj *obj, void *vpriv)
541 struct cedit_iter_priv *priv = vpriv;
542 struct scene_obj_menu *menu;
546 snprintf(var, sizeof(var), "c.%s", obj->name);
550 case SCENEOBJT_IMAGE:
554 menu = (struct scene_obj_menu *)obj;
555 val = env_get_ulong(var, 10, 0);
557 printf("%s=%d\n", var, val);
559 return log_msg_ret("get", -ENOENT);
562 * note that no validation is done here, to make sure the ID is
563 * valid * and actually points to a menu item
565 menu->cur_item_id = val;
567 case SCENEOBJT_TEXTLINE: {
568 const struct scene_obj_textline *tline;
571 tline = (struct scene_obj_textline *)obj;
572 value = env_get(var);
573 if (value && strlen(value) >= tline->max_chars)
574 return log_msg_ret("str", -ENOSPC);
578 printf("%s=%s\n", var, value);
579 strcpy(abuf_data(&tline->buf), value);
587 int cedit_read_settings_env(struct expo *exp, bool verbose)
589 struct cedit_iter_priv priv;
592 /* write out the items */
593 priv.verbose = verbose;
594 ret = expo_iter_scene_objs(exp, h_read_settings_env, &priv);
596 log_debug("Failed to read settings from env (err=%d)\n", ret);
597 return log_msg_ret("set", ret);
604 * get_cur_menuitem_seq() - Get the sequence number of a menu's current item
606 * Enumerates the items of a menu (0, 1, 2) and returns the sequence number of
607 * the currently selected item. If the first item is selected, this returns 0;
608 * if the second, 1; etc.
610 * @menu: Menu to check
611 * Return: Sequence number on success, else -ve error value
613 static int get_cur_menuitem_seq(const struct scene_obj_menu *menu)
615 const struct scene_menitem *mi;
620 list_for_each_entry(mi, &menu->item_head, sibling) {
621 if (mi->id == menu->cur_item_id) {
629 return log_msg_ret("nf", -ENOENT);
634 static int h_write_settings_cmos(struct scene_obj *obj, void *vpriv)
636 const struct scene_obj_menu *menu;
637 struct cedit_iter_priv *priv = vpriv;
641 if (obj->type != SCENEOBJT_MENU)
644 menu = (struct scene_obj_menu *)obj;
645 val = menu->cur_item_id;
647 ret = get_cur_menuitem_seq(menu);
649 return log_msg_ret("cur", ret);
651 log_debug("%s: seq=%d\n", menu->obj.name, seq);
653 /* figure out where to place this item */
654 if (!obj->bit_length)
655 return log_msg_ret("len", -EINVAL);
656 if (obj->start_bit + obj->bit_length > CMOS_MAX_BITS)
657 return log_msg_ret("bit", -E2BIG);
659 for (i = 0; i < obj->bit_length; i++, seq >>= 1) {
660 uint bitnum = obj->start_bit + i;
662 priv->mask[CMOS_BYTE(bitnum)] |= 1 << CMOS_BIT(bitnum);
664 priv->value[CMOS_BYTE(bitnum)] |= BIT(CMOS_BIT(bitnum));
665 log_debug("bit %x %x %x\n", bitnum,
666 priv->mask[CMOS_BYTE(bitnum)],
667 priv->value[CMOS_BYTE(bitnum)]);
673 int cedit_write_settings_cmos(struct expo *exp, struct udevice *dev,
676 struct cedit_iter_priv priv;
677 int ret, i, count, first, last;
679 /* write out the items */
680 priv.mask = calloc(1, CMOS_MAX_BYTES);
682 return log_msg_ret("mas", -ENOMEM);
683 priv.value = calloc(1, CMOS_MAX_BYTES);
686 return log_msg_ret("val", -ENOMEM);
689 ret = expo_iter_scene_objs(exp, h_write_settings_cmos, &priv);
691 log_debug("Failed to write CMOS (err=%d)\n", ret);
692 ret = log_msg_ret("set", ret);
696 /* write the data to the RTC */
697 first = CMOS_MAX_BYTES;
699 for (i = 0, count = 0; i < CMOS_MAX_BYTES; i++) {
701 log_debug("Write byte %x: %x\n", i, priv.value[i]);
702 ret = rtc_write8(dev, i, priv.value[i]);
704 ret = log_msg_ret("wri", ret);
708 first = min(first, i);
713 printf("Write %d bytes from offset %x to %x\n", count, first,
723 static int h_read_settings_cmos(struct scene_obj *obj, void *vpriv)
725 struct cedit_iter_priv *priv = vpriv;
726 const struct scene_menitem *mi;
727 struct scene_obj_menu *menu;
731 if (obj->type != SCENEOBJT_MENU)
734 menu = (struct scene_obj_menu *)obj;
736 /* figure out where to place this item */
737 if (!obj->bit_length)
738 return log_msg_ret("len", -EINVAL);
739 if (obj->start_bit + obj->bit_length > CMOS_MAX_BITS)
740 return log_msg_ret("bit", -E2BIG);
743 for (i = 0; i < obj->bit_length; i++) {
744 uint bitnum = obj->start_bit + i;
745 uint offset = CMOS_BYTE(bitnum);
747 /* read the byte if not already read */
748 if (!priv->mask[offset]) {
749 ret = rtc_read8(priv->dev, offset);
751 return log_msg_ret("rea", ret);
752 priv->value[offset] = ret;
754 /* mark it as read */
755 priv->mask[offset] = 0xff;
758 if (priv->value[offset] & BIT(CMOS_BIT(bitnum)))
760 log_debug("bit %x %x\n", bitnum, val);
763 /* update the current item */
764 mi = scene_menuitem_find_seq(menu, val);
766 return log_msg_ret("seq", -ENOENT);
768 menu->cur_item_id = mi->id;
769 log_debug("Update menu %d cur_item_id %d\n", menu->obj.id, mi->id);
774 int cedit_read_settings_cmos(struct expo *exp, struct udevice *dev,
777 struct cedit_iter_priv priv;
778 int ret, i, count, first, last;
780 /* read in the items */
781 priv.mask = calloc(1, CMOS_MAX_BYTES);
783 return log_msg_ret("mas", -ENOMEM);
784 priv.value = calloc(1, CMOS_MAX_BYTES);
787 return log_msg_ret("val", -ENOMEM);
791 ret = expo_iter_scene_objs(exp, h_read_settings_cmos, &priv);
793 log_debug("Failed to read CMOS (err=%d)\n", ret);
794 ret = log_msg_ret("set", ret);
798 /* read the data to the RTC */
799 first = CMOS_MAX_BYTES;
801 for (i = 0, count = 0; i < CMOS_MAX_BYTES; i++) {
803 log_debug("Read byte %x: %x\n", i, priv.value[i]);
805 first = min(first, i);
810 printf("Read %d bytes from offset %x to %x\n", count, first,