blob: 9d48d3ed50b98aba846351de83e8b5cd3edd9be3 [file] [log] [blame]
Simon Glassc8925112023-06-01 10:23:02 -06001// SPDX-License-Identifier: GPL-2.0+
2/*
3 * Implementation of configuration editor
4 *
5 * Copyright 2023 Google LLC
6 * Written by Simon Glass <sjg@chromium.org>
7 */
8
Simon Glass6a5af5f2023-08-14 16:40:27 -06009#define LOG_CATEGORY LOGC_EXPO
10
Simon Glass28bf4352023-08-14 16:40:33 -060011#include <abuf.h>
Simon Glass8df4a4e2023-08-14 16:40:25 -060012#include <cedit.h>
Simon Glassc8925112023-06-01 10:23:02 -060013#include <cli.h>
14#include <dm.h>
Simon Glass237f3752023-08-14 16:40:35 -060015#include <env.h>
Simon Glassc8925112023-06-01 10:23:02 -060016#include <expo.h>
Simon Glass2b91ca62023-08-14 16:40:37 -060017#include <malloc.h>
Simon Glassc8925112023-06-01 10:23:02 -060018#include <menu.h>
Simon Glass2b91ca62023-08-14 16:40:37 -060019#include <rtc.h>
Simon Glassc8925112023-06-01 10:23:02 -060020#include <video.h>
21#include <linux/delay.h>
22#include "scene_internal.h"
23
Simon Glass2b91ca62023-08-14 16:40:37 -060024enum {
25 CMOS_MAX_BITS = 2048,
26 CMOS_MAX_BYTES = CMOS_MAX_BITS / 8,
27};
28
29#define CMOS_BYTE(bit) ((bit) / 8)
30#define CMOS_BIT(bit) ((bit) % 8)
31
Simon Glass28bf4352023-08-14 16:40:33 -060032/**
33 * struct cedit_iter_priv - private data for cedit operations
34 *
35 * @buf: Buffer to use when writing settings to the devicetree
Simon Glassb1cd32b2023-08-14 16:40:34 -060036 * @node: Node to read from when reading settings from devicetree
Simon Glass237f3752023-08-14 16:40:35 -060037 * @verbose: true to show writing to environment variables
Simon Glass2b91ca62023-08-14 16:40:37 -060038 * @mask: Mask bits for the CMOS RAM. If a bit is set the byte containing it
39 * will be written
40 * @value: Value bits for CMOS RAM. This is the actual value written
Simon Glass4462fa32023-08-14 16:40:38 -060041 * @dev: RTC device to write to
Simon Glass28bf4352023-08-14 16:40:33 -060042 */
43struct cedit_iter_priv {
44 struct abuf *buf;
Simon Glassb1cd32b2023-08-14 16:40:34 -060045 ofnode node;
Simon Glass237f3752023-08-14 16:40:35 -060046 bool verbose;
Simon Glass2b91ca62023-08-14 16:40:37 -060047 u8 *mask;
48 u8 *value;
Simon Glass4462fa32023-08-14 16:40:38 -060049 struct udevice *dev;
Simon Glass28bf4352023-08-14 16:40:33 -060050};
51
Simon Glassc8925112023-06-01 10:23:02 -060052int cedit_arange(struct expo *exp, struct video_priv *vpriv, uint scene_id)
53{
Simon Glass377f18e2024-10-14 16:31:55 -060054 struct expo_arrange_info arr;
Simon Glassc8925112023-06-01 10:23:02 -060055 struct scene_obj_txt *txt;
56 struct scene_obj *obj;
57 struct scene *scn;
Simon Glass377f18e2024-10-14 16:31:55 -060058 int y, ret;
Simon Glassc8925112023-06-01 10:23:02 -060059
60 scn = expo_lookup_scene_id(exp, scene_id);
61 if (!scn)
62 return log_msg_ret("scn", -ENOENT);
63
64 txt = scene_obj_find_by_name(scn, "prompt");
65 if (txt)
66 scene_obj_set_pos(scn, txt->obj.id, 0, vpriv->ysize - 50);
67
68 txt = scene_obj_find_by_name(scn, "title");
69 if (txt)
70 scene_obj_set_pos(scn, txt->obj.id, 200, 10);
71
Simon Glass377f18e2024-10-14 16:31:55 -060072 memset(&arr, '\0', sizeof(arr));
73 ret = scene_calc_arrange(scn, &arr);
74 if (ret < 0)
75 return log_msg_ret("arr", ret);
76
Simon Glassc8925112023-06-01 10:23:02 -060077 y = 100;
78 list_for_each_entry(obj, &scn->obj_head, sibling) {
Simon Glassb7a64532023-10-01 19:13:24 -060079 switch (obj->type) {
80 case SCENEOBJT_NONE:
81 case SCENEOBJT_IMAGE:
82 case SCENEOBJT_TEXT:
83 break;
84 case SCENEOBJT_MENU:
Simon Glassc8925112023-06-01 10:23:02 -060085 scene_obj_set_pos(scn, obj->id, 50, y);
Simon Glass377f18e2024-10-14 16:31:55 -060086 scene_menu_arrange(scn, &arr,
87 (struct scene_obj_menu *)obj);
Simon Glassc8925112023-06-01 10:23:02 -060088 y += 50;
Simon Glassb7a64532023-10-01 19:13:24 -060089 break;
Simon Glass23c3eb42023-10-01 19:13:37 -060090 case SCENEOBJT_TEXTLINE:
91 scene_obj_set_pos(scn, obj->id, 50, y);
Simon Glass377f18e2024-10-14 16:31:55 -060092 scene_textline_arrange(scn, &arr,
Simon Glass23c3eb42023-10-01 19:13:37 -060093 (struct scene_obj_textline *)obj);
94 y += 50;
95 break;
Simon Glassc8925112023-06-01 10:23:02 -060096 }
97 }
98
99 return 0;
100}
101
Simon Glass6a5af5f2023-08-14 16:40:27 -0600102int cedit_prepare(struct expo *exp, struct video_priv **vid_privp,
103 struct scene **scnp)
Simon Glassc8925112023-06-01 10:23:02 -0600104{
Simon Glassc8925112023-06-01 10:23:02 -0600105 struct video_priv *vid_priv;
Simon Glassc8925112023-06-01 10:23:02 -0600106 struct udevice *dev;
107 struct scene *scn;
Simon Glass6a5af5f2023-08-14 16:40:27 -0600108 uint scene_id;
Simon Glassc8925112023-06-01 10:23:02 -0600109 int ret;
110
Simon Glassc8925112023-06-01 10:23:02 -0600111 /* For now we only support a video console */
112 ret = uclass_first_device_err(UCLASS_VIDEO, &dev);
113 if (ret)
114 return log_msg_ret("vid", ret);
115 ret = expo_set_display(exp, dev);
116 if (ret)
117 return log_msg_ret("dis", ret);
118
119 ret = expo_first_scene_id(exp);
120 if (ret < 0)
121 return log_msg_ret("scn", ret);
122 scene_id = ret;
123
124 ret = expo_set_scene_id(exp, scene_id);
125 if (ret)
126 return log_msg_ret("sid", ret);
127
128 exp->popup = true;
129
130 /* This is not supported for now */
131 if (0)
132 expo_set_text_mode(exp, true);
133
134 vid_priv = dev_get_uclass_priv(dev);
135
136 scn = expo_lookup_scene_id(exp, scene_id);
137 scene_highlight_first(scn);
138
139 cedit_arange(exp, vid_priv, scene_id);
140
141 ret = expo_calc_dims(exp);
142 if (ret)
143 return log_msg_ret("dim", ret);
144
Simon Glass6a5af5f2023-08-14 16:40:27 -0600145 *vid_privp = vid_priv;
146 *scnp = scn;
147
148 return scene_id;
149}
150
151int cedit_run(struct expo *exp)
152{
153 struct cli_ch_state s_cch, *cch = &s_cch;
154 struct video_priv *vid_priv;
155 uint scene_id;
156 struct scene *scn;
Simon Glass53a0a2f2024-10-14 16:31:57 -0600157 bool done, save;
Simon Glass6a5af5f2023-08-14 16:40:27 -0600158 int ret;
159
160 cli_ch_init(cch);
161 ret = cedit_prepare(exp, &vid_priv, &scn);
162 if (ret < 0)
163 return log_msg_ret("prep", ret);
164 scene_id = ret;
165
Simon Glassc8925112023-06-01 10:23:02 -0600166 done = false;
Simon Glass53a0a2f2024-10-14 16:31:57 -0600167 save = false;
Simon Glassc8925112023-06-01 10:23:02 -0600168 do {
169 struct expo_action act;
170 int ichar, key;
171
172 ret = expo_render(exp);
173 if (ret)
174 break;
175
176 ichar = cli_ch_process(cch, 0);
177 if (!ichar) {
178 while (!ichar && !tstc()) {
179 schedule();
180 mdelay(2);
181 ichar = cli_ch_process(cch, -ETIMEDOUT);
182 }
183 if (!ichar) {
184 ichar = getchar();
185 ichar = cli_ch_process(cch, ichar);
186 }
187 }
188
189 key = 0;
190 if (ichar) {
191 key = bootmenu_conv_key(ichar);
Simon Glasseb8af2c2023-10-01 19:13:36 -0600192 if (key == BKEY_NONE || key >= BKEY_FIRST_EXTRA)
Simon Glassc8925112023-06-01 10:23:02 -0600193 key = ichar;
194 }
195 if (!key)
196 continue;
197
198 ret = expo_send_key(exp, key);
199 if (ret)
200 break;
201
202 ret = expo_action_get(exp, &act);
203 if (!ret) {
204 switch (act.type) {
205 case EXPOACT_POINT_OBJ:
206 scene_set_highlight_id(scn, act.select.id);
207 cedit_arange(exp, vid_priv, scene_id);
208 break;
209 case EXPOACT_OPEN:
210 scene_set_open(scn, act.select.id, true);
211 cedit_arange(exp, vid_priv, scene_id);
Simon Glass53a0a2f2024-10-14 16:31:57 -0600212 switch (scn->highlight_id) {
213 case EXPOID_SAVE:
214 done = true;
215 save = true;
216 break;
217 case EXPOID_DISCARD:
218 done = true;
219 break;
220 }
Simon Glassc8925112023-06-01 10:23:02 -0600221 break;
222 case EXPOACT_CLOSE:
223 scene_set_open(scn, act.select.id, false);
224 cedit_arange(exp, vid_priv, scene_id);
225 break;
226 case EXPOACT_SELECT:
227 scene_set_open(scn, scn->highlight_id, false);
228 cedit_arange(exp, vid_priv, scene_id);
229 break;
230 case EXPOACT_QUIT:
231 log_debug("quitting\n");
232 done = true;
233 break;
234 default:
235 break;
236 }
237 }
238 } while (!done);
239
240 if (ret)
241 return log_msg_ret("end", ret);
Simon Glass53a0a2f2024-10-14 16:31:57 -0600242 if (!save)
243 return -EACCES;
Simon Glassc8925112023-06-01 10:23:02 -0600244
245 return 0;
246}
Simon Glass28bf4352023-08-14 16:40:33 -0600247
248static int check_space(int ret, struct abuf *buf)
249{
250 if (ret == -FDT_ERR_NOSPACE) {
251 if (!abuf_realloc_inc(buf, CEDIT_SIZE_INC))
252 return log_msg_ret("spc", -ENOMEM);
253 ret = fdt_resize(abuf_data(buf), abuf_data(buf),
254 abuf_size(buf));
255 if (ret)
256 return log_msg_ret("res", -EFAULT);
257 }
258
259 return 0;
260}
261
Simon Glassdb6a0512023-10-01 19:13:23 -0600262/**
263 * get_cur_menuitem_text() - Get the text of the currently selected item
264 *
265 * Looks up the object for the current item, finds text object for it and looks
266 * up the string for that text
267 *
268 * @menu: Menu to look at
269 * @strp: Returns a pointer to the next
270 * Return: 0 if OK, -ENOENT if something was not found
271 */
Simon Glass237f3752023-08-14 16:40:35 -0600272static int get_cur_menuitem_text(const struct scene_obj_menu *menu,
273 const char **strp)
274{
275 struct scene *scn = menu->obj.scene;
276 const struct scene_menitem *mi;
277 const struct scene_obj_txt *txt;
278 const char *str;
279
280 mi = scene_menuitem_find(menu, menu->cur_item_id);
281 if (!mi)
282 return log_msg_ret("mi", -ENOENT);
283
284 txt = scene_obj_find(scn, mi->label_id, SCENEOBJT_TEXT);
285 if (!txt)
286 return log_msg_ret("txt", -ENOENT);
287
288 str = expo_get_str(scn->expo, txt->str_id);
289 if (!str)
290 return log_msg_ret("str", -ENOENT);
291 *strp = str;
292
293 return 0;
294}
295
Simon Glassf78388b2023-10-01 19:13:28 -0600296static int write_dt_string(struct abuf *buf, const char *name, const char *str)
297{
298 int ret, i;
299
300 /* write the text of the current item */
301 ret = -EAGAIN;
302 for (i = 0; ret && i < 2; i++) {
303 ret = fdt_property_string(abuf_data(buf), name, str);
304 if (!i) {
305 ret = check_space(ret, buf);
306 if (ret)
307 return log_msg_ret("rs2", -ENOMEM);
308 }
309 }
310
311 /* this should not happen */
312 if (ret)
313 return log_msg_ret("str", -EFAULT);
314
315 return 0;
316}
317
Simon Glass28bf4352023-08-14 16:40:33 -0600318static int h_write_settings(struct scene_obj *obj, void *vpriv)
319{
320 struct cedit_iter_priv *priv = vpriv;
321 struct abuf *buf = priv->buf;
Simon Glass23c3eb42023-10-01 19:13:37 -0600322 int ret;
Simon Glass28bf4352023-08-14 16:40:33 -0600323
324 switch (obj->type) {
325 case SCENEOBJT_NONE:
326 case SCENEOBJT_IMAGE:
327 case SCENEOBJT_TEXT:
328 break;
Simon Glass23c3eb42023-10-01 19:13:37 -0600329 case SCENEOBJT_TEXTLINE: {
330 const struct scene_obj_textline *tline;
331
332 tline = (struct scene_obj_textline *)obj;
333 ret = write_dt_string(buf, obj->name, abuf_data(&tline->buf));
334 if (ret)
335 return log_msg_ret("wr2", ret);
336 break;
337 }
Simon Glass28bf4352023-08-14 16:40:33 -0600338 case SCENEOBJT_MENU: {
339 const struct scene_obj_menu *menu;
Simon Glass28bf4352023-08-14 16:40:33 -0600340 const char *str;
341 char name[80];
Simon Glass23c3eb42023-10-01 19:13:37 -0600342 int i;
Simon Glass28bf4352023-08-14 16:40:33 -0600343
Simon Glassf78388b2023-10-01 19:13:28 -0600344 /* write the ID of the current item */
Simon Glass28bf4352023-08-14 16:40:33 -0600345 menu = (struct scene_obj_menu *)obj;
346 ret = -EAGAIN;
347 for (i = 0; ret && i < 2; i++) {
348 ret = fdt_property_u32(abuf_data(buf), obj->name,
349 menu->cur_item_id);
350 if (!i) {
351 ret = check_space(ret, buf);
352 if (ret)
353 return log_msg_ret("res", -ENOMEM);
354 }
355 }
356 /* this should not happen */
357 if (ret)
358 return log_msg_ret("wrt", -EFAULT);
359
Simon Glass237f3752023-08-14 16:40:35 -0600360 ret = get_cur_menuitem_text(menu, &str);
361 if (ret)
362 return log_msg_ret("mis", ret);
Simon Glass28bf4352023-08-14 16:40:33 -0600363
Simon Glassf78388b2023-10-01 19:13:28 -0600364 /* write the text of the current item */
Simon Glass28bf4352023-08-14 16:40:33 -0600365 snprintf(name, sizeof(name), "%s-str", obj->name);
Simon Glassf78388b2023-10-01 19:13:28 -0600366 ret = write_dt_string(buf, name, str);
Simon Glass28bf4352023-08-14 16:40:33 -0600367 if (ret)
Simon Glassf78388b2023-10-01 19:13:28 -0600368 return log_msg_ret("wr2", ret);
Simon Glass28bf4352023-08-14 16:40:33 -0600369
370 break;
371 }
372 }
373
374 return 0;
375}
376
377int cedit_write_settings(struct expo *exp, struct abuf *buf)
378{
379 struct cedit_iter_priv priv;
380 void *fdt;
381 int ret;
382
383 abuf_init(buf);
384 if (!abuf_realloc(buf, CEDIT_SIZE_INC))
385 return log_msg_ret("buf", -ENOMEM);
386
387 fdt = abuf_data(buf);
388 ret = fdt_create(fdt, abuf_size(buf));
389 if (!ret)
390 ret = fdt_finish_reservemap(fdt);
391 if (!ret)
392 ret = fdt_begin_node(fdt, "");
393 if (!ret)
394 ret = fdt_begin_node(fdt, CEDIT_NODE_NAME);
395 if (ret) {
396 log_debug("Failed to start FDT (err=%d)\n", ret);
397 return log_msg_ret("sta", -EINVAL);
398 }
399
400 /* write out the items */
401 priv.buf = buf;
402 ret = expo_iter_scene_objs(exp, h_write_settings, &priv);
403 if (ret) {
404 log_debug("Failed to write settings (err=%d)\n", ret);
405 return log_msg_ret("set", ret);
406 }
407
408 ret = fdt_end_node(fdt);
409 if (!ret)
410 ret = fdt_end_node(fdt);
411 if (!ret)
412 ret = fdt_finish(fdt);
413 if (ret) {
414 log_debug("Failed to finish FDT (err=%d)\n", ret);
415 return log_msg_ret("fin", -EINVAL);
416 }
417
418 return 0;
419}
Simon Glassb1cd32b2023-08-14 16:40:34 -0600420
421static int h_read_settings(struct scene_obj *obj, void *vpriv)
422{
423 struct cedit_iter_priv *priv = vpriv;
424 ofnode node = priv->node;
425
426 switch (obj->type) {
427 case SCENEOBJT_NONE:
428 case SCENEOBJT_IMAGE:
429 case SCENEOBJT_TEXT:
430 break;
Simon Glass23c3eb42023-10-01 19:13:37 -0600431 case SCENEOBJT_TEXTLINE: {
432 const struct scene_obj_textline *tline;
433 const char *val;
434 int len;
435
436 tline = (struct scene_obj_textline *)obj;
437
438 val = ofnode_read_prop(node, obj->name, &len);
439 if (len >= tline->max_chars)
440 return log_msg_ret("str", -ENOSPC);
441 strcpy(abuf_data(&tline->buf), val);
442 break;
443 }
Simon Glassb1cd32b2023-08-14 16:40:34 -0600444 case SCENEOBJT_MENU: {
445 struct scene_obj_menu *menu;
446 uint val;
447
448 if (ofnode_read_u32(node, obj->name, &val))
449 return log_msg_ret("rd", -ENOENT);
450 menu = (struct scene_obj_menu *)obj;
451 menu->cur_item_id = val;
452
453 break;
454 }
455 }
456
457 return 0;
458}
459
460int cedit_read_settings(struct expo *exp, oftree tree)
461{
462 struct cedit_iter_priv priv;
463 ofnode root, node;
464 int ret;
465
466 root = oftree_root(tree);
467 if (!ofnode_valid(root))
468 return log_msg_ret("roo", -ENOENT);
469 node = ofnode_find_subnode(root, CEDIT_NODE_NAME);
470 if (!ofnode_valid(node))
471 return log_msg_ret("pat", -ENOENT);
472
473 /* read in the items */
474 priv.node = node;
475 ret = expo_iter_scene_objs(exp, h_read_settings, &priv);
476 if (ret) {
477 log_debug("Failed to read settings (err=%d)\n", ret);
478 return log_msg_ret("set", ret);
479 }
480
481 return 0;
482}
Simon Glass237f3752023-08-14 16:40:35 -0600483
484static int h_write_settings_env(struct scene_obj *obj, void *vpriv)
485{
486 const struct scene_obj_menu *menu;
487 struct cedit_iter_priv *priv = vpriv;
488 char name[80], var[60];
489 const char *str;
490 int val, ret;
491
Simon Glass53a0a2f2024-10-14 16:31:57 -0600492 if (obj->id < EXPOID_BASE_ID)
493 return 0;
494
Simon Glass237f3752023-08-14 16:40:35 -0600495 snprintf(var, sizeof(var), "c.%s", obj->name);
496
Simon Glassb7a64532023-10-01 19:13:24 -0600497 switch (obj->type) {
498 case SCENEOBJT_NONE:
499 case SCENEOBJT_IMAGE:
500 case SCENEOBJT_TEXT:
501 break;
502 case SCENEOBJT_MENU:
503 menu = (struct scene_obj_menu *)obj;
504 val = menu->cur_item_id;
Simon Glass237f3752023-08-14 16:40:35 -0600505
Simon Glassb7a64532023-10-01 19:13:24 -0600506 if (priv->verbose)
507 printf("%s=%d\n", var, val);
Simon Glass237f3752023-08-14 16:40:35 -0600508
Simon Glassb7a64532023-10-01 19:13:24 -0600509 ret = env_set_ulong(var, val);
510 if (ret)
511 return log_msg_ret("set", ret);
Simon Glass237f3752023-08-14 16:40:35 -0600512
Simon Glassb7a64532023-10-01 19:13:24 -0600513 ret = get_cur_menuitem_text(menu, &str);
514 if (ret)
515 return log_msg_ret("mis", ret);
Simon Glass237f3752023-08-14 16:40:35 -0600516
Simon Glassb7a64532023-10-01 19:13:24 -0600517 snprintf(name, sizeof(name), "c.%s-str", obj->name);
518 if (priv->verbose)
519 printf("%s=%s\n", name, str);
520
521 ret = env_set(name, str);
522 if (ret)
523 return log_msg_ret("st2", ret);
524 break;
Simon Glass23c3eb42023-10-01 19:13:37 -0600525 case SCENEOBJT_TEXTLINE: {
526 const struct scene_obj_textline *tline;
527
528 tline = (struct scene_obj_textline *)obj;
529 str = abuf_data(&tline->buf);
530 ret = env_set(var, str);
531 if (ret)
532 return log_msg_ret("set", ret);
533
534 if (priv->verbose)
535 printf("%s=%s\n", var, str);
536
537 break;
538 }
Simon Glassb7a64532023-10-01 19:13:24 -0600539 }
Simon Glass237f3752023-08-14 16:40:35 -0600540
541 return 0;
542}
543
544int cedit_write_settings_env(struct expo *exp, bool verbose)
545{
546 struct cedit_iter_priv priv;
547 int ret;
548
549 /* write out the items */
550 priv.verbose = verbose;
551 ret = expo_iter_scene_objs(exp, h_write_settings_env, &priv);
552 if (ret) {
553 log_debug("Failed to write settings to env (err=%d)\n", ret);
554 return log_msg_ret("set", ret);
555 }
556
557 return 0;
558}
Simon Glass0f2e5a62023-08-14 16:40:36 -0600559
560static int h_read_settings_env(struct scene_obj *obj, void *vpriv)
561{
562 struct cedit_iter_priv *priv = vpriv;
563 struct scene_obj_menu *menu;
564 char var[60];
Simon Glass2b91ca62023-08-14 16:40:37 -0600565 int val;
Simon Glass0f2e5a62023-08-14 16:40:36 -0600566
Simon Glass53a0a2f2024-10-14 16:31:57 -0600567 if (obj->id < EXPOID_BASE_ID)
568 return 0;
569
Simon Glass0f2e5a62023-08-14 16:40:36 -0600570 snprintf(var, sizeof(var), "c.%s", obj->name);
571
Simon Glassb7a64532023-10-01 19:13:24 -0600572 switch (obj->type) {
573 case SCENEOBJT_NONE:
574 case SCENEOBJT_IMAGE:
575 case SCENEOBJT_TEXT:
576 break;
577 case SCENEOBJT_MENU:
578 menu = (struct scene_obj_menu *)obj;
579 val = env_get_ulong(var, 10, 0);
580 if (priv->verbose)
581 printf("%s=%d\n", var, val);
582 if (!val)
583 return log_msg_ret("get", -ENOENT);
Simon Glass0f2e5a62023-08-14 16:40:36 -0600584
Simon Glassb7a64532023-10-01 19:13:24 -0600585 /*
586 * note that no validation is done here, to make sure the ID is
Simon Glass330aa1e2024-10-14 16:31:59 -0600587 * valid and actually points to a menu item
Simon Glassb7a64532023-10-01 19:13:24 -0600588 */
589 menu->cur_item_id = val;
590 break;
Simon Glass23c3eb42023-10-01 19:13:37 -0600591 case SCENEOBJT_TEXTLINE: {
592 const struct scene_obj_textline *tline;
593 const char *value;
594
595 tline = (struct scene_obj_textline *)obj;
596 value = env_get(var);
597 if (value && strlen(value) >= tline->max_chars)
598 return log_msg_ret("str", -ENOSPC);
599 if (!value)
600 value = "";
601 if (priv->verbose)
602 printf("%s=%s\n", var, value);
603 strcpy(abuf_data(&tline->buf), value);
604 break;
605 }
Simon Glassb7a64532023-10-01 19:13:24 -0600606 }
Simon Glass0f2e5a62023-08-14 16:40:36 -0600607
608 return 0;
609}
610
611int cedit_read_settings_env(struct expo *exp, bool verbose)
612{
613 struct cedit_iter_priv priv;
614 int ret;
615
616 /* write out the items */
617 priv.verbose = verbose;
618 ret = expo_iter_scene_objs(exp, h_read_settings_env, &priv);
619 if (ret) {
620 log_debug("Failed to read settings from env (err=%d)\n", ret);
621 return log_msg_ret("set", ret);
622 }
623
624 return 0;
625}
Simon Glass2b91ca62023-08-14 16:40:37 -0600626
627/**
628 * get_cur_menuitem_seq() - Get the sequence number of a menu's current item
629 *
630 * Enumerates the items of a menu (0, 1, 2) and returns the sequence number of
631 * the currently selected item. If the first item is selected, this returns 0;
632 * if the second, 1; etc.
633 *
634 * @menu: Menu to check
635 * Return: Sequence number on success, else -ve error value
636 */
637static int get_cur_menuitem_seq(const struct scene_obj_menu *menu)
638{
639 const struct scene_menitem *mi;
640 int seq, found;
641
642 seq = 0;
643 found = -1;
644 list_for_each_entry(mi, &menu->item_head, sibling) {
645 if (mi->id == menu->cur_item_id) {
646 found = seq;
647 break;
648 }
649 seq++;
650 }
651
652 if (found == -1)
653 return log_msg_ret("nf", -ENOENT);
654
655 return found;
656}
657
658static int h_write_settings_cmos(struct scene_obj *obj, void *vpriv)
659{
660 const struct scene_obj_menu *menu;
661 struct cedit_iter_priv *priv = vpriv;
662 int val, ret;
663 uint i, seq;
664
Simon Glass53a0a2f2024-10-14 16:31:57 -0600665 if (obj->type != SCENEOBJT_MENU || obj->id < EXPOID_BASE_ID)
Simon Glass2b91ca62023-08-14 16:40:37 -0600666 return 0;
667
668 menu = (struct scene_obj_menu *)obj;
669 val = menu->cur_item_id;
670
671 ret = get_cur_menuitem_seq(menu);
672 if (ret < 0)
673 return log_msg_ret("cur", ret);
674 seq = ret;
675 log_debug("%s: seq=%d\n", menu->obj.name, seq);
676
677 /* figure out where to place this item */
678 if (!obj->bit_length)
679 return log_msg_ret("len", -EINVAL);
680 if (obj->start_bit + obj->bit_length > CMOS_MAX_BITS)
681 return log_msg_ret("bit", -E2BIG);
682
683 for (i = 0; i < obj->bit_length; i++, seq >>= 1) {
684 uint bitnum = obj->start_bit + i;
685
686 priv->mask[CMOS_BYTE(bitnum)] |= 1 << CMOS_BIT(bitnum);
687 if (seq & 1)
688 priv->value[CMOS_BYTE(bitnum)] |= BIT(CMOS_BIT(bitnum));
689 log_debug("bit %x %x %x\n", bitnum,
690 priv->mask[CMOS_BYTE(bitnum)],
691 priv->value[CMOS_BYTE(bitnum)]);
692 }
693
694 return 0;
695}
696
697int cedit_write_settings_cmos(struct expo *exp, struct udevice *dev,
698 bool verbose)
699{
700 struct cedit_iter_priv priv;
701 int ret, i, count, first, last;
702
703 /* write out the items */
704 priv.mask = calloc(1, CMOS_MAX_BYTES);
705 if (!priv.mask)
706 return log_msg_ret("mas", -ENOMEM);
707 priv.value = calloc(1, CMOS_MAX_BYTES);
708 if (!priv.value) {
709 free(priv.mask);
710 return log_msg_ret("val", -ENOMEM);
711 }
712
713 ret = expo_iter_scene_objs(exp, h_write_settings_cmos, &priv);
714 if (ret) {
715 log_debug("Failed to write CMOS (err=%d)\n", ret);
716 ret = log_msg_ret("set", ret);
717 goto done;
718 }
719
720 /* write the data to the RTC */
Simon Glass330aa1e2024-10-14 16:31:59 -0600721 log_debug("Writing CMOS\n");
Simon Glass2b91ca62023-08-14 16:40:37 -0600722 first = CMOS_MAX_BYTES;
723 last = -1;
724 for (i = 0, count = 0; i < CMOS_MAX_BYTES; i++) {
725 if (priv.mask[i]) {
726 log_debug("Write byte %x: %x\n", i, priv.value[i]);
727 ret = rtc_write8(dev, i, priv.value[i]);
728 if (ret) {
729 ret = log_msg_ret("wri", ret);
730 goto done;
731 }
732 count++;
733 first = min(first, i);
734 last = max(last, i);
735 }
736 }
737 if (verbose) {
738 printf("Write %d bytes from offset %x to %x\n", count, first,
739 last);
740 }
741
742done:
743 free(priv.mask);
744 free(priv.value);
745 return ret;
746}
Simon Glass4462fa32023-08-14 16:40:38 -0600747
748static int h_read_settings_cmos(struct scene_obj *obj, void *vpriv)
749{
750 struct cedit_iter_priv *priv = vpriv;
751 const struct scene_menitem *mi;
752 struct scene_obj_menu *menu;
753 int val, ret;
754 uint i;
755
Simon Glass53a0a2f2024-10-14 16:31:57 -0600756 if (obj->type != SCENEOBJT_MENU || obj->id < EXPOID_BASE_ID)
Simon Glass4462fa32023-08-14 16:40:38 -0600757 return 0;
758
759 menu = (struct scene_obj_menu *)obj;
760
761 /* figure out where to place this item */
762 if (!obj->bit_length)
763 return log_msg_ret("len", -EINVAL);
764 if (obj->start_bit + obj->bit_length > CMOS_MAX_BITS)
765 return log_msg_ret("bit", -E2BIG);
766
767 val = 0;
768 for (i = 0; i < obj->bit_length; i++) {
769 uint bitnum = obj->start_bit + i;
770 uint offset = CMOS_BYTE(bitnum);
771
772 /* read the byte if not already read */
773 if (!priv->mask[offset]) {
774 ret = rtc_read8(priv->dev, offset);
775 if (ret < 0)
776 return log_msg_ret("rea", ret);
777 priv->value[offset] = ret;
778
779 /* mark it as read */
780 priv->mask[offset] = 0xff;
781 }
782
783 if (priv->value[offset] & BIT(CMOS_BIT(bitnum)))
784 val |= BIT(i);
785 log_debug("bit %x %x\n", bitnum, val);
786 }
787
788 /* update the current item */
Simon Glass330aa1e2024-10-14 16:31:59 -0600789 log_debug("look for menuitem value %d in menu %d\n", val, menu->obj.id);
Simon Glass4462fa32023-08-14 16:40:38 -0600790 mi = scene_menuitem_find_seq(menu, val);
791 if (!mi)
792 return log_msg_ret("seq", -ENOENT);
793
794 menu->cur_item_id = mi->id;
795 log_debug("Update menu %d cur_item_id %d\n", menu->obj.id, mi->id);
796
797 return 0;
798}
799
800int cedit_read_settings_cmos(struct expo *exp, struct udevice *dev,
801 bool verbose)
802{
803 struct cedit_iter_priv priv;
804 int ret, i, count, first, last;
805
806 /* read in the items */
807 priv.mask = calloc(1, CMOS_MAX_BYTES);
808 if (!priv.mask)
809 return log_msg_ret("mas", -ENOMEM);
810 priv.value = calloc(1, CMOS_MAX_BYTES);
811 if (!priv.value) {
812 free(priv.mask);
813 return log_msg_ret("val", -ENOMEM);
814 }
815 priv.dev = dev;
816
817 ret = expo_iter_scene_objs(exp, h_read_settings_cmos, &priv);
818 if (ret) {
819 log_debug("Failed to read CMOS (err=%d)\n", ret);
820 ret = log_msg_ret("set", ret);
821 goto done;
822 }
823
Simon Glass330aa1e2024-10-14 16:31:59 -0600824 /* indicate what bytes were read from the RTC */
Simon Glass4462fa32023-08-14 16:40:38 -0600825 first = CMOS_MAX_BYTES;
826 last = -1;
827 for (i = 0, count = 0; i < CMOS_MAX_BYTES; i++) {
828 if (priv.mask[i]) {
829 log_debug("Read byte %x: %x\n", i, priv.value[i]);
830 count++;
831 first = min(first, i);
832 last = max(last, i);
833 }
834 }
835 if (verbose) {
836 printf("Read %d bytes from offset %x to %x\n", count, first,
837 last);
838 }
839
840done:
841 free(priv.mask);
842 free(priv.value);
843 return ret;
844}