1 // 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
9 #define LOG_CATEGORY LOGC_EXPO
10
11 #include <abuf.h>
12 #include <cedit.h>
13 #include <cli.h>
14 #include <dm.h>
15 #include <env.h>
16 #include <expo.h>
17 #include <malloc.h>
18 #include <menu.h>
19 #include <rtc.h>
20 #include <video.h>
21 #include <linux/delay.h>
22 #include "scene_internal.h"
23 #include <u-boot/schedule.h>
24
25 enum {
26 CMOS_MAX_BITS = 2048,
27 CMOS_MAX_BYTES = CMOS_MAX_BITS / 8,
28 };
29
30 #define CMOS_BYTE(bit) ((bit) / 8)
31 #define CMOS_BIT(bit) ((bit) % 8)
32
33 /**
34 * struct cedit_iter_priv - private data for cedit operations
35 *
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
40 * will be written
41 * @value: Value bits for CMOS RAM. This is the actual value written
42 * @dev: RTC device to write to
43 */
44 struct cedit_iter_priv {
45 struct abuf *buf;
46 ofnode node;
47 bool verbose;
48 u8 *mask;
49 u8 *value;
50 struct udevice *dev;
51 };
52
cedit_arange(struct expo * exp,struct video_priv * vpriv,uint scene_id)53 int cedit_arange(struct expo *exp, struct video_priv *vpriv, uint scene_id)
54 {
55 struct expo_arrange_info arr;
56 struct scene_obj_txt *txt;
57 struct scene_obj *obj;
58 struct scene *scn;
59 int y, ret;
60
61 scn = expo_lookup_scene_id(exp, scene_id);
62 if (!scn)
63 return log_msg_ret("scn", -ENOENT);
64
65 txt = scene_obj_find_by_name(scn, "prompt");
66 if (txt)
67 scene_obj_set_pos(scn, txt->obj.id, 0, vpriv->ysize - 50);
68
69 txt = scene_obj_find_by_name(scn, "title");
70 if (txt)
71 scene_obj_set_pos(scn, txt->obj.id, 200, 10);
72
73 memset(&arr, '\0', sizeof(arr));
74 ret = scene_calc_arrange(scn, &arr);
75 if (ret < 0)
76 return log_msg_ret("arr", ret);
77
78 y = 100;
79 list_for_each_entry(obj, &scn->obj_head, sibling) {
80 switch (obj->type) {
81 case SCENEOBJT_NONE:
82 case SCENEOBJT_IMAGE:
83 case SCENEOBJT_TEXT:
84 case SCENEOBJT_BOX:
85 case SCENEOBJT_TEXTEDIT:
86 break;
87 case SCENEOBJT_MENU:
88 scene_obj_set_pos(scn, obj->id, 50, y);
89 scene_menu_arrange(scn, &arr,
90 (struct scene_obj_menu *)obj);
91 y += 50;
92 break;
93 case SCENEOBJT_TEXTLINE:
94 scene_obj_set_pos(scn, obj->id, 50, y);
95 scene_textline_arrange(scn, &arr,
96 (struct scene_obj_textline *)obj);
97 y += 50;
98 break;
99 }
100 }
101
102 return 0;
103 }
104
cedit_prepare(struct expo * exp,struct udevice * vid_dev,struct scene ** scnp)105 int cedit_prepare(struct expo *exp, struct udevice *vid_dev,
106 struct scene **scnp)
107 {
108 struct udevice *dev = vid_dev;
109 struct video_priv *vid_priv;
110 struct scene *scn;
111 uint scene_id;
112 int ret;
113
114 /* For now we only support a video console */
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 exp->show_highlight = true;
130
131 /* This is not supported for now */
132 if (0)
133 expo_set_text_mode(exp, true);
134
135 vid_priv = dev_get_uclass_priv(dev);
136
137 scn = expo_lookup_scene_id(exp, scene_id);
138 scene_highlight_first(scn);
139
140 cedit_arange(exp, vid_priv, scene_id);
141
142 ret = expo_calc_dims(exp);
143 if (ret)
144 return log_msg_ret("dim", ret);
145
146 *scnp = scn;
147
148 return scene_id;
149 }
150
cedit_do_action(struct expo * exp,struct scene * scn,struct video_priv * vid_priv,struct expo_action * act)151 int cedit_do_action(struct expo *exp, struct scene *scn,
152 struct video_priv *vid_priv, struct expo_action *act)
153 {
154 int ret;
155
156 switch (act->type) {
157 case EXPOACT_NONE:
158 return -EAGAIN;
159 case EXPOACT_POINT_ITEM:
160 ret = scene_menu_select_item(scn, scn->highlight_id,
161 act->select.id);
162 if (ret)
163 return log_msg_ret("cdp", ret);
164 break;
165 case EXPOACT_POINT_OBJ:
166 scene_set_highlight_id(scn, act->select.id);
167 cedit_arange(exp, vid_priv, scn->id);
168 break;
169 case EXPOACT_OPEN:
170 scene_set_open(scn, act->select.id, true);
171 cedit_arange(exp, vid_priv, scn->id);
172 switch (scn->highlight_id) {
173 case EXPOID_SAVE:
174 exp->done = true;
175 exp->save = true;
176 break;
177 case EXPOID_DISCARD:
178 exp->done = true;
179 break;
180 }
181 break;
182 case EXPOACT_CLOSE:
183 scene_set_open(scn, act->select.id, false);
184 cedit_arange(exp, vid_priv, scn->id);
185 break;
186 case EXPOACT_SELECT:
187 scene_set_open(scn, scn->highlight_id, false);
188 cedit_arange(exp, vid_priv, scn->id);
189 break;
190 case EXPOACT_QUIT:
191 log_debug("quitting\n");
192 exp->done = true;
193 break;
194 }
195
196 return 0;
197 }
198
cedit_run(struct expo * exp)199 int cedit_run(struct expo *exp)
200 {
201 struct video_priv *vid_priv;
202 struct udevice *dev;
203 struct scene *scn;
204 uint scene_id;
205 int ret;
206
207 ret = uclass_first_device_err(UCLASS_VIDEO, &dev);
208 if (ret)
209 return log_msg_ret("vid", ret);
210 vid_priv = dev_get_uclass_priv(dev);
211
212 ret = cedit_prepare(exp, dev, &scn);
213 if (ret < 0)
214 return log_msg_ret("prep", ret);
215 scene_id = ret;
216
217 exp->done = false;
218 exp->save = false;
219 do {
220 struct expo_action act;
221
222 ret = expo_render(exp);
223 if (ret)
224 return log_msg_ret("cer", ret);
225
226 ret = expo_poll(exp, &act);
227 if (!ret)
228 cedit_do_action(exp, scn, vid_priv, &act);
229 else if (ret != -EAGAIN)
230 return log_msg_ret("cep", ret);
231 } while (!exp->done);
232
233 if (ret)
234 return log_msg_ret("end", ret);
235 if (!exp->save)
236 return -EACCES;
237
238 return 0;
239 }
240
check_space(int ret,struct abuf * buf)241 static int check_space(int ret, struct abuf *buf)
242 {
243 if (ret == -FDT_ERR_NOSPACE) {
244 if (!abuf_realloc_inc(buf, CEDIT_SIZE_INC))
245 return log_msg_ret("spc", -ENOMEM);
246 ret = fdt_resize(abuf_data(buf), abuf_data(buf),
247 abuf_size(buf));
248 if (ret)
249 return log_msg_ret("res", -EFAULT);
250 }
251
252 return 0;
253 }
254
255 /**
256 * get_cur_menuitem_text() - Get the text of the currently selected item
257 *
258 * Looks up the object for the current item, finds text object for it and looks
259 * up the string for that text
260 *
261 * @menu: Menu to look at
262 * @strp: Returns a pointer to the next
263 * Return: 0 if OK, -ENOENT if something was not found
264 */
get_cur_menuitem_text(const struct scene_obj_menu * menu,const char ** strp)265 static int get_cur_menuitem_text(const struct scene_obj_menu *menu,
266 const char **strp)
267 {
268 struct scene *scn = menu->obj.scene;
269 const struct scene_menitem *mi;
270 const struct scene_obj_txt *txt;
271 const char *str;
272
273 mi = scene_menuitem_find(menu, menu->cur_item_id);
274 if (!mi)
275 return log_msg_ret("mi", -ENOENT);
276
277 txt = scene_obj_find(scn, mi->label_id, SCENEOBJT_TEXT);
278 if (!txt)
279 return log_msg_ret("txt", -ENOENT);
280
281 str = expo_get_str(scn->expo, txt->gen.str_id);
282 if (!str)
283 return log_msg_ret("str", -ENOENT);
284 *strp = str;
285
286 return 0;
287 }
288
289 /**
290 * get_cur_menuitem_val() - Get the value of a menu's current item
291 *
292 * Obtains the value of the current item in the menu. If no value, then
293 * enumerates the items of a menu (0, 1, 2) and returns the sequence number of
294 * the currently selected item. If the first item is selected, this returns 0;
295 * if the second, 1; etc.
296 *
297 * @menu: Menu to check
298 * @valp: Returns current-item value / sequence number
299 * Return: 0 on success, else -ve error value
300 */
get_cur_menuitem_val(const struct scene_obj_menu * menu,int * valp)301 static int get_cur_menuitem_val(const struct scene_obj_menu *menu, int *valp)
302 {
303 const struct scene_menitem *mi;
304 int seq;
305
306 seq = 0;
307 list_for_each_entry(mi, &menu->item_head, sibling) {
308 if (mi->id == menu->cur_item_id) {
309 *valp = mi->value == INT_MAX ? seq : mi->value;
310 return 0;
311 }
312 seq++;
313 }
314
315 return log_msg_ret("nf", -ENOENT);
316 }
317
318 /**
319 * write_dt_string() - Write a string to the devicetree, expanding if needed
320 *
321 * If this fails, it tries again after expanding the devicetree a little
322 *
323 * @buf: Buffer containing the devicetree
324 * @name: Property name to use
325 * @str: String value
326 * Return: 0 if OK, -EFAULT if something went horribly wrong
327 */
write_dt_string(struct abuf * buf,const char * name,const char * str)328 static int write_dt_string(struct abuf *buf, const char *name, const char *str)
329 {
330 int ret, i;
331
332 ret = -EAGAIN;
333 for (i = 0; ret && i < 2; i++) {
334 ret = fdt_property_string(abuf_data(buf), name, str);
335 if (!i) {
336 ret = check_space(ret, buf);
337 if (ret)
338 return log_msg_ret("rs2", -ENOMEM);
339 }
340 }
341
342 /* this should not happen */
343 if (ret)
344 return log_msg_ret("str", -EFAULT);
345
346 return 0;
347 }
348
349 /**
350 * write_dt_u32() - Write an int to the devicetree, expanding if needed
351 *
352 * If this fails, it tries again after expanding the devicetree a little
353 *
354 * @buf: Buffer containing the devicetree
355 * @name: Property name to use
356 * @lva: Integer value
357 * Return: 0 if OK, -EFAULT if something went horribly wrong
358 */
write_dt_u32(struct abuf * buf,const char * name,uint val)359 static int write_dt_u32(struct abuf *buf, const char *name, uint val)
360 {
361 int ret, i;
362
363 /* write the text of the current item */
364 ret = -EAGAIN;
365 for (i = 0; ret && i < 2; i++) {
366 ret = fdt_property_u32(abuf_data(buf), name, val);
367 if (!i) {
368 ret = check_space(ret, buf);
369 if (ret)
370 return log_msg_ret("rs2", -ENOMEM);
371 }
372 }
373
374 /* this should not happen */
375 if (ret)
376 return log_msg_ret("str", -EFAULT);
377
378 return 0;
379 }
380
h_write_settings(struct scene_obj * obj,void * vpriv)381 static int h_write_settings(struct scene_obj *obj, void *vpriv)
382 {
383 struct cedit_iter_priv *priv = vpriv;
384 struct abuf *buf = priv->buf;
385 int ret;
386
387 switch (obj->type) {
388 case SCENEOBJT_NONE:
389 case SCENEOBJT_IMAGE:
390 case SCENEOBJT_TEXT:
391 case SCENEOBJT_BOX:
392 case SCENEOBJT_TEXTEDIT:
393 break;
394 case SCENEOBJT_TEXTLINE: {
395 const struct scene_obj_textline *tline;
396
397 tline = (struct scene_obj_textline *)obj;
398 ret = write_dt_string(buf, obj->name, abuf_data(&tline->buf));
399 if (ret)
400 return log_msg_ret("wr2", ret);
401 break;
402 }
403 case SCENEOBJT_MENU: {
404 const struct scene_obj_menu *menu;
405 const char *str;
406 char name[80];
407 int val;
408
409 /* write the ID of the current item */
410 menu = (struct scene_obj_menu *)obj;
411 ret = write_dt_u32(buf, obj->name, menu->cur_item_id);
412 if (ret)
413 return log_msg_ret("wrt", ret);
414
415 snprintf(name, sizeof(name), "%s-value", obj->name);
416 ret = get_cur_menuitem_val(menu, &val);
417 if (ret < 0)
418 return log_msg_ret("cur", ret);
419 ret = write_dt_u32(buf, name, val);
420 if (ret)
421 return log_msg_ret("wr2", ret);
422
423 ret = get_cur_menuitem_text(menu, &str);
424 if (ret)
425 return log_msg_ret("mis", ret);
426
427 /* write the text of the current item */
428 snprintf(name, sizeof(name), "%s-str", obj->name);
429 ret = write_dt_string(buf, name, str);
430 if (ret)
431 return log_msg_ret("wr2", ret);
432
433 break;
434 }
435 }
436
437 return 0;
438 }
439
cedit_write_settings(struct expo * exp,struct abuf * buf)440 int cedit_write_settings(struct expo *exp, struct abuf *buf)
441 {
442 struct cedit_iter_priv priv;
443 void *fdt;
444 int ret;
445
446 if (!abuf_init_size(buf, CEDIT_SIZE_INC))
447 return log_msg_ret("buf", -ENOMEM);
448
449 fdt = abuf_data(buf);
450 ret = fdt_create(fdt, abuf_size(buf));
451 if (!ret)
452 ret = fdt_finish_reservemap(fdt);
453 if (!ret)
454 ret = fdt_begin_node(fdt, "");
455 if (!ret)
456 ret = fdt_begin_node(fdt, CEDIT_NODE_NAME);
457 if (ret) {
458 log_debug("Failed to start FDT (err=%d)\n", ret);
459 return log_msg_ret("sta", -EINVAL);
460 }
461
462 /* write out the items */
463 priv.buf = buf;
464 ret = expo_iter_scene_objs(exp, h_write_settings, &priv);
465 if (ret) {
466 log_debug("Failed to write settings (err=%d)\n", ret);
467 return log_msg_ret("set", ret);
468 }
469
470 ret = fdt_end_node(fdt);
471 if (!ret)
472 ret = fdt_end_node(fdt);
473 if (!ret)
474 ret = fdt_finish(fdt);
475 if (ret) {
476 log_debug("Failed to finish FDT (err=%d)\n", ret);
477 return log_msg_ret("fin", -EINVAL);
478 }
479
480 return 0;
481 }
482
h_read_settings(struct scene_obj * obj,void * vpriv)483 static int h_read_settings(struct scene_obj *obj, void *vpriv)
484 {
485 struct cedit_iter_priv *priv = vpriv;
486 ofnode node = priv->node;
487
488 switch (obj->type) {
489 case SCENEOBJT_NONE:
490 case SCENEOBJT_IMAGE:
491 case SCENEOBJT_TEXT:
492 case SCENEOBJT_BOX:
493 case SCENEOBJT_TEXTEDIT:
494 break;
495 case SCENEOBJT_TEXTLINE: {
496 const struct scene_obj_textline *tline;
497 const char *val;
498 int len;
499
500 tline = (struct scene_obj_textline *)obj;
501
502 val = ofnode_read_prop(node, obj->name, &len);
503 if (len >= tline->max_chars)
504 return log_msg_ret("str", -ENOSPC);
505 strcpy(abuf_data(&tline->buf), val);
506 break;
507 }
508 case SCENEOBJT_MENU: {
509 struct scene_obj_menu *menu;
510 uint val;
511
512 if (ofnode_read_u32(node, obj->name, &val))
513 return log_msg_ret("rd", -ENOENT);
514 menu = (struct scene_obj_menu *)obj;
515 menu->cur_item_id = val;
516
517 break;
518 }
519 }
520
521 return 0;
522 }
523
cedit_read_settings(struct expo * exp,oftree tree)524 int cedit_read_settings(struct expo *exp, oftree tree)
525 {
526 struct cedit_iter_priv priv;
527 ofnode root, node;
528 int ret;
529
530 root = oftree_root(tree);
531 if (!ofnode_valid(root))
532 return log_msg_ret("roo", -ENOENT);
533 node = ofnode_find_subnode(root, CEDIT_NODE_NAME);
534 if (!ofnode_valid(node))
535 return log_msg_ret("pat", -ENOENT);
536
537 /* read in the items */
538 priv.node = node;
539 ret = expo_iter_scene_objs(exp, h_read_settings, &priv);
540 if (ret) {
541 log_debug("Failed to read settings (err=%d)\n", ret);
542 return log_msg_ret("set", ret);
543 }
544
545 return 0;
546 }
547
h_write_settings_env(struct scene_obj * obj,void * vpriv)548 static int h_write_settings_env(struct scene_obj *obj, void *vpriv)
549 {
550 const struct scene_obj_menu *menu;
551 struct cedit_iter_priv *priv = vpriv;
552 char name[80], var[60];
553 const char *str;
554 int val, ret;
555
556 if (obj->id < EXPOID_BASE_ID)
557 return 0;
558
559 snprintf(var, sizeof(var), "c.%s", obj->name);
560
561 switch (obj->type) {
562 case SCENEOBJT_NONE:
563 case SCENEOBJT_IMAGE:
564 case SCENEOBJT_TEXT:
565 case SCENEOBJT_BOX:
566 case SCENEOBJT_TEXTEDIT:
567 break;
568 case SCENEOBJT_MENU:
569 menu = (struct scene_obj_menu *)obj;
570 val = menu->cur_item_id;
571
572 if (priv->verbose)
573 printf("%s=%d\n", var, val);
574
575 ret = env_set_ulong(var, val);
576 if (ret)
577 return log_msg_ret("set", ret);
578
579 ret = get_cur_menuitem_text(menu, &str);
580 if (ret)
581 return log_msg_ret("mis", ret);
582
583 snprintf(name, sizeof(name), "c.%s-str", obj->name);
584 if (priv->verbose)
585 printf("%s=%s\n", name, str);
586
587 ret = env_set(name, str);
588 if (ret)
589 return log_msg_ret("st2", ret);
590
591 ret = get_cur_menuitem_val(menu, &val);
592 if (ret < 0)
593 return log_msg_ret("cur", ret);
594 snprintf(name, sizeof(name), "c.%s-value", obj->name);
595 if (priv->verbose)
596 printf("%s=%d\n", name, val);
597
598 break;
599 case SCENEOBJT_TEXTLINE: {
600 const struct scene_obj_textline *tline;
601
602 tline = (struct scene_obj_textline *)obj;
603 str = abuf_data(&tline->buf);
604 ret = env_set(var, str);
605 if (ret)
606 return log_msg_ret("set", ret);
607
608 if (priv->verbose)
609 printf("%s=%s\n", var, str);
610
611 break;
612 }
613 }
614
615 return 0;
616 }
617
cedit_write_settings_env(struct expo * exp,bool verbose)618 int cedit_write_settings_env(struct expo *exp, bool verbose)
619 {
620 struct cedit_iter_priv priv;
621 int ret;
622
623 /* write out the items */
624 priv.verbose = verbose;
625 ret = expo_iter_scene_objs(exp, h_write_settings_env, &priv);
626 if (ret) {
627 log_debug("Failed to write settings to env (err=%d)\n", ret);
628 return log_msg_ret("set", ret);
629 }
630
631 return 0;
632 }
633
h_read_settings_env(struct scene_obj * obj,void * vpriv)634 static int h_read_settings_env(struct scene_obj *obj, void *vpriv)
635 {
636 struct cedit_iter_priv *priv = vpriv;
637 struct scene_obj_menu *menu;
638 char var[60];
639 int val;
640
641 if (obj->id < EXPOID_BASE_ID)
642 return 0;
643
644 snprintf(var, sizeof(var), "c.%s", obj->name);
645
646 switch (obj->type) {
647 case SCENEOBJT_NONE:
648 case SCENEOBJT_IMAGE:
649 case SCENEOBJT_TEXT:
650 case SCENEOBJT_BOX:
651 case SCENEOBJT_TEXTEDIT:
652 break;
653 case SCENEOBJT_MENU:
654 menu = (struct scene_obj_menu *)obj;
655 val = env_get_ulong(var, 10, 0);
656 if (priv->verbose)
657 printf("%s=%d\n", var, val);
658 if (!val)
659 return log_msg_ret("get", -ENOENT);
660
661 /*
662 * note that no validation is done here, to make sure the ID is
663 * valid and actually points to a menu item
664 */
665 menu->cur_item_id = val;
666 break;
667 case SCENEOBJT_TEXTLINE: {
668 const struct scene_obj_textline *tline;
669 const char *value;
670
671 tline = (struct scene_obj_textline *)obj;
672 value = env_get(var);
673 if (value && strlen(value) >= tline->max_chars)
674 return log_msg_ret("str", -ENOSPC);
675 if (!value)
676 value = "";
677 if (priv->verbose)
678 printf("%s=%s\n", var, value);
679 strcpy(abuf_data(&tline->buf), value);
680 break;
681 }
682 }
683
684 return 0;
685 }
686
cedit_read_settings_env(struct expo * exp,bool verbose)687 int cedit_read_settings_env(struct expo *exp, bool verbose)
688 {
689 struct cedit_iter_priv priv;
690 int ret;
691
692 /* write out the items */
693 priv.verbose = verbose;
694 ret = expo_iter_scene_objs(exp, h_read_settings_env, &priv);
695 if (ret) {
696 log_debug("Failed to read settings from env (err=%d)\n", ret);
697 return log_msg_ret("set", ret);
698 }
699
700 return 0;
701 }
702
h_write_settings_cmos(struct scene_obj * obj,void * vpriv)703 static int h_write_settings_cmos(struct scene_obj *obj, void *vpriv)
704 {
705 const struct scene_obj_menu *menu;
706 struct cedit_iter_priv *priv = vpriv;
707 int val, ret;
708 uint i;
709
710 if (obj->type != SCENEOBJT_MENU || obj->id < EXPOID_BASE_ID)
711 return 0;
712
713 menu = (struct scene_obj_menu *)obj;
714 val = menu->cur_item_id;
715
716 ret = get_cur_menuitem_val(menu, &val);
717 if (ret < 0)
718 return log_msg_ret("cur", ret);
719 log_debug("%s: val=%d\n", menu->obj.name, val);
720
721 /* figure out where to place this item */
722 if (!obj->bit_length)
723 return log_msg_ret("len", -EINVAL);
724 if (obj->start_bit + obj->bit_length > CMOS_MAX_BITS)
725 return log_msg_ret("bit", -E2BIG);
726
727 for (i = 0; i < obj->bit_length; i++, val >>= 1) {
728 uint bitnum = obj->start_bit + i;
729
730 priv->mask[CMOS_BYTE(bitnum)] |= 1 << CMOS_BIT(bitnum);
731 if (val & 1)
732 priv->value[CMOS_BYTE(bitnum)] |= BIT(CMOS_BIT(bitnum));
733 log_debug("bit %x %x %x\n", bitnum,
734 priv->mask[CMOS_BYTE(bitnum)],
735 priv->value[CMOS_BYTE(bitnum)]);
736 }
737
738 return 0;
739 }
740
cedit_write_settings_cmos(struct expo * exp,struct udevice * dev,bool verbose)741 int cedit_write_settings_cmos(struct expo *exp, struct udevice *dev,
742 bool verbose)
743 {
744 struct cedit_iter_priv priv;
745 int ret, i, count, first, last;
746
747 /* write out the items */
748 priv.mask = calloc(1, CMOS_MAX_BYTES);
749 if (!priv.mask)
750 return log_msg_ret("mas", -ENOMEM);
751 priv.value = calloc(1, CMOS_MAX_BYTES);
752 if (!priv.value) {
753 free(priv.mask);
754 return log_msg_ret("val", -ENOMEM);
755 }
756
757 ret = expo_iter_scene_objs(exp, h_write_settings_cmos, &priv);
758 if (ret) {
759 log_debug("Failed to write CMOS (err=%d)\n", ret);
760 ret = log_msg_ret("set", ret);
761 goto done;
762 }
763
764 /* write the data to the RTC */
765 log_debug("Writing CMOS\n");
766 first = CMOS_MAX_BYTES;
767 last = -1;
768 for (i = 0, count = 0; i < CMOS_MAX_BYTES; i++) {
769 if (priv.mask[i]) {
770 log_debug("Write byte %x: %x\n", i, priv.value[i]);
771 ret = rtc_write8(dev, i, priv.value[i]);
772 if (ret) {
773 ret = log_msg_ret("wri", ret);
774 goto done;
775 }
776 count++;
777 first = min(first, i);
778 last = max(last, i);
779 }
780 }
781 if (verbose) {
782 printf("Write %d bytes from offset %x to %x\n", count, first,
783 last);
784 }
785
786 done:
787 free(priv.mask);
788 free(priv.value);
789 return ret;
790 }
791
h_read_settings_cmos(struct scene_obj * obj,void * vpriv)792 static int h_read_settings_cmos(struct scene_obj *obj, void *vpriv)
793 {
794 struct cedit_iter_priv *priv = vpriv;
795 const struct scene_menitem *mi;
796 struct scene_obj_menu *menu;
797 int val, ret;
798 uint i;
799
800 if (obj->type != SCENEOBJT_MENU || obj->id < EXPOID_BASE_ID)
801 return 0;
802
803 menu = (struct scene_obj_menu *)obj;
804
805 /* figure out where to place this item */
806 if (!obj->bit_length)
807 return log_msg_ret("len", -EINVAL);
808 if (obj->start_bit + obj->bit_length > CMOS_MAX_BITS)
809 return log_msg_ret("bit", -E2BIG);
810
811 val = 0;
812 for (i = 0; i < obj->bit_length; i++) {
813 uint bitnum = obj->start_bit + i;
814 uint offset = CMOS_BYTE(bitnum);
815
816 /* read the byte if not already read */
817 if (!priv->mask[offset]) {
818 ret = rtc_read8(priv->dev, offset);
819 if (ret < 0)
820 return log_msg_ret("rea", ret);
821 priv->value[offset] = ret;
822
823 /* mark it as read */
824 priv->mask[offset] = 0xff;
825 }
826
827 if (priv->value[offset] & BIT(CMOS_BIT(bitnum)))
828 val |= BIT(i);
829 log_debug("bit %x %x\n", bitnum, val);
830 }
831
832 /* update the current item */
833 log_debug("look for menuitem value %d in menu %d\n", val, menu->obj.id);
834 mi = scene_menuitem_find_val(menu, val);
835 if (!mi)
836 return log_msg_ret("seq", -ENOENT);
837
838 menu->cur_item_id = mi->id;
839 log_debug("Update menu %d cur_item_id %d\n", menu->obj.id, mi->id);
840
841 return 0;
842 }
843
cedit_read_settings_cmos(struct expo * exp,struct udevice * dev,bool verbose)844 int cedit_read_settings_cmos(struct expo *exp, struct udevice *dev,
845 bool verbose)
846 {
847 struct cedit_iter_priv priv;
848 int ret, i, count, first, last;
849
850 /* read in the items */
851 priv.mask = calloc(1, CMOS_MAX_BYTES);
852 if (!priv.mask)
853 return log_msg_ret("mas", -ENOMEM);
854 priv.value = calloc(1, CMOS_MAX_BYTES);
855 if (!priv.value) {
856 free(priv.mask);
857 return log_msg_ret("val", -ENOMEM);
858 }
859 priv.dev = dev;
860
861 ret = expo_iter_scene_objs(exp, h_read_settings_cmos, &priv);
862 if (ret) {
863 log_debug("Failed to read CMOS (err=%d)\n", ret);
864 ret = log_msg_ret("set", ret);
865 goto done;
866 }
867
868 /* indicate what bytes were read from the RTC */
869 first = CMOS_MAX_BYTES;
870 last = -1;
871 for (i = 0, count = 0; i < CMOS_MAX_BYTES; i++) {
872 if (priv.mask[i]) {
873 log_debug("Read byte %x: %x\n", i, priv.value[i]);
874 count++;
875 first = min(first, i);
876 last = max(last, i);
877 }
878 }
879 if (verbose) {
880 printf("Read %d bytes from offset %x to %x\n", count, first,
881 last);
882 }
883
884 done:
885 free(priv.mask);
886 free(priv.value);
887 return ret;
888 }
889