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