]> Git Repo - u-boot.git/blob - boot/cedit.c
expo: Fix up comments for get_cur_menuitem_text() et al
[u-boot.git] / boot / cedit.c
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Implementation of configuration editor
4  *
5  * Copyright 2023 Google LLC
6  * Written by Simon Glass <[email protected]>
7  */
8
9 #define LOG_CATEGORY LOGC_EXPO
10
11 #include <common.h>
12 #include <abuf.h>
13 #include <cedit.h>
14 #include <cli.h>
15 #include <dm.h>
16 #include <env.h>
17 #include <expo.h>
18 #include <malloc.h>
19 #include <menu.h>
20 #include <rtc.h>
21 #include <video.h>
22 #include <linux/delay.h>
23 #include "scene_internal.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
53 int cedit_arange(struct expo *exp, struct video_priv *vpriv, uint scene_id)
54 {
55         struct scene_obj_txt *txt;
56         struct scene_obj *obj;
57         struct scene *scn;
58         int y;
59
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
72         y = 100;
73         list_for_each_entry(obj, &scn->obj_head, sibling) {
74                 if (obj->type == SCENEOBJT_MENU) {
75                         scene_obj_set_pos(scn, obj->id, 50, y);
76                         scene_menu_arrange(scn, (struct scene_obj_menu *)obj);
77                         y += 50;
78                 }
79         }
80
81         return 0;
82 }
83
84 int cedit_prepare(struct expo *exp, struct video_priv **vid_privp,
85                   struct scene **scnp)
86 {
87         struct video_priv *vid_priv;
88         struct udevice *dev;
89         struct scene *scn;
90         uint scene_id;
91         int ret;
92
93         /* For now we only support a video console */
94         ret = uclass_first_device_err(UCLASS_VIDEO, &dev);
95         if (ret)
96                 return log_msg_ret("vid", ret);
97         ret = expo_set_display(exp, dev);
98         if (ret)
99                 return log_msg_ret("dis", ret);
100
101         ret = expo_first_scene_id(exp);
102         if (ret < 0)
103                 return log_msg_ret("scn", ret);
104         scene_id = ret;
105
106         ret = expo_set_scene_id(exp, scene_id);
107         if (ret)
108                 return log_msg_ret("sid", ret);
109
110         exp->popup = true;
111
112         /* This is not supported for now */
113         if (0)
114                 expo_set_text_mode(exp, true);
115
116         vid_priv = dev_get_uclass_priv(dev);
117
118         scn = expo_lookup_scene_id(exp, scene_id);
119         scene_highlight_first(scn);
120
121         cedit_arange(exp, vid_priv, scene_id);
122
123         ret = expo_calc_dims(exp);
124         if (ret)
125                 return log_msg_ret("dim", ret);
126
127         *vid_privp = vid_priv;
128         *scnp = scn;
129
130         return scene_id;
131 }
132
133 int cedit_run(struct expo *exp)
134 {
135         struct cli_ch_state s_cch, *cch = &s_cch;
136         struct video_priv *vid_priv;
137         uint scene_id;
138         struct scene *scn;
139         bool done;
140         int ret;
141
142         cli_ch_init(cch);
143         ret = cedit_prepare(exp, &vid_priv, &scn);
144         if (ret < 0)
145                 return log_msg_ret("prep", ret);
146         scene_id = ret;
147
148         done = false;
149         do {
150                 struct expo_action act;
151                 int ichar, key;
152
153                 ret = expo_render(exp);
154                 if (ret)
155                         break;
156
157                 ichar = cli_ch_process(cch, 0);
158                 if (!ichar) {
159                         while (!ichar && !tstc()) {
160                                 schedule();
161                                 mdelay(2);
162                                 ichar = cli_ch_process(cch, -ETIMEDOUT);
163                         }
164                         if (!ichar) {
165                                 ichar = getchar();
166                                 ichar = cli_ch_process(cch, ichar);
167                         }
168                 }
169
170                 key = 0;
171                 if (ichar) {
172                         key = bootmenu_conv_key(ichar);
173                         if (key == BKEY_NONE)
174                                 key = ichar;
175                 }
176                 if (!key)
177                         continue;
178
179                 ret = expo_send_key(exp, key);
180                 if (ret)
181                         break;
182
183                 ret = expo_action_get(exp, &act);
184                 if (!ret) {
185                         switch (act.type) {
186                         case EXPOACT_POINT_OBJ:
187                                 scene_set_highlight_id(scn, act.select.id);
188                                 cedit_arange(exp, vid_priv, scene_id);
189                                 break;
190                         case EXPOACT_OPEN:
191                                 scene_set_open(scn, act.select.id, true);
192                                 cedit_arange(exp, vid_priv, scene_id);
193                                 break;
194                         case EXPOACT_CLOSE:
195                                 scene_set_open(scn, act.select.id, false);
196                                 cedit_arange(exp, vid_priv, scene_id);
197                                 break;
198                         case EXPOACT_SELECT:
199                                 scene_set_open(scn, scn->highlight_id, false);
200                                 cedit_arange(exp, vid_priv, scene_id);
201                                 break;
202                         case EXPOACT_QUIT:
203                                 log_debug("quitting\n");
204                                 done = true;
205                                 break;
206                         default:
207                                 break;
208                         }
209                 }
210         } while (!done);
211
212         if (ret)
213                 return log_msg_ret("end", ret);
214
215         return 0;
216 }
217
218 static int check_space(int ret, struct abuf *buf)
219 {
220         if (ret == -FDT_ERR_NOSPACE) {
221                 if (!abuf_realloc_inc(buf, CEDIT_SIZE_INC))
222                         return log_msg_ret("spc", -ENOMEM);
223                 ret = fdt_resize(abuf_data(buf), abuf_data(buf),
224                                  abuf_size(buf));
225                 if (ret)
226                         return log_msg_ret("res", -EFAULT);
227         }
228
229         return 0;
230 }
231
232 /**
233  * get_cur_menuitem_text() - Get the text of the currently selected item
234  *
235  * Looks up the object for the current item, finds text object for it and looks
236  * up the string for that text
237  *
238  * @menu: Menu to look at
239  * @strp: Returns a pointer to the next
240  * Return: 0 if OK, -ENOENT if something was not found
241  */
242 static int get_cur_menuitem_text(const struct scene_obj_menu *menu,
243                                  const char **strp)
244 {
245         struct scene *scn = menu->obj.scene;
246         const struct scene_menitem *mi;
247         const struct scene_obj_txt *txt;
248         const char *str;
249
250         mi = scene_menuitem_find(menu, menu->cur_item_id);
251         if (!mi)
252                 return log_msg_ret("mi", -ENOENT);
253
254         txt = scene_obj_find(scn, mi->label_id, SCENEOBJT_TEXT);
255         if (!txt)
256                 return log_msg_ret("txt", -ENOENT);
257
258         str = expo_get_str(scn->expo, txt->str_id);
259         if (!str)
260                 return log_msg_ret("str", -ENOENT);
261         *strp = str;
262
263         return 0;
264 }
265
266 static int h_write_settings(struct scene_obj *obj, void *vpriv)
267 {
268         struct cedit_iter_priv *priv = vpriv;
269         struct abuf *buf = priv->buf;
270
271         switch (obj->type) {
272         case SCENEOBJT_NONE:
273         case SCENEOBJT_IMAGE:
274         case SCENEOBJT_TEXT:
275                 break;
276         case SCENEOBJT_MENU: {
277                 const struct scene_obj_menu *menu;
278                 const char *str;
279                 char name[80];
280                 int ret, i;
281
282                 menu = (struct scene_obj_menu *)obj;
283                 ret = -EAGAIN;
284                 for (i = 0; ret && i < 2; i++) {
285                         ret = fdt_property_u32(abuf_data(buf), obj->name,
286                                                menu->cur_item_id);
287                         if (!i) {
288                                 ret = check_space(ret, buf);
289                                 if (ret)
290                                         return log_msg_ret("res", -ENOMEM);
291                         }
292                 }
293                 /* this should not happen */
294                 if (ret)
295                         return log_msg_ret("wrt", -EFAULT);
296
297                 ret = get_cur_menuitem_text(menu, &str);
298                 if (ret)
299                         return log_msg_ret("mis", ret);
300
301                 snprintf(name, sizeof(name), "%s-str", obj->name);
302                 ret = -EAGAIN;
303                 for (i = 0; ret && i < 2; i++) {
304                         ret = fdt_property_string(abuf_data(buf), name, str);
305                         if (!i) {
306                                 ret = check_space(ret, buf);
307                                 if (ret)
308                                         return log_msg_ret("rs2", -ENOMEM);
309                         }
310                 }
311
312                 /* this should not happen */
313                 if (ret)
314                         return log_msg_ret("wr2", -EFAULT);
315
316                 break;
317         }
318         }
319
320         return 0;
321 }
322
323 int cedit_write_settings(struct expo *exp, struct abuf *buf)
324 {
325         struct cedit_iter_priv priv;
326         void *fdt;
327         int ret;
328
329         abuf_init(buf);
330         if (!abuf_realloc(buf, CEDIT_SIZE_INC))
331                 return log_msg_ret("buf", -ENOMEM);
332
333         fdt = abuf_data(buf);
334         ret = fdt_create(fdt, abuf_size(buf));
335         if (!ret)
336                 ret = fdt_finish_reservemap(fdt);
337         if (!ret)
338                 ret = fdt_begin_node(fdt, "");
339         if (!ret)
340                 ret = fdt_begin_node(fdt, CEDIT_NODE_NAME);
341         if (ret) {
342                 log_debug("Failed to start FDT (err=%d)\n", ret);
343                 return log_msg_ret("sta", -EINVAL);
344         }
345
346         /* write out the items */
347         priv.buf = buf;
348         ret = expo_iter_scene_objs(exp, h_write_settings, &priv);
349         if (ret) {
350                 log_debug("Failed to write settings (err=%d)\n", ret);
351                 return log_msg_ret("set", ret);
352         }
353
354         ret = fdt_end_node(fdt);
355         if (!ret)
356                 ret = fdt_end_node(fdt);
357         if (!ret)
358                 ret = fdt_finish(fdt);
359         if (ret) {
360                 log_debug("Failed to finish FDT (err=%d)\n", ret);
361                 return log_msg_ret("fin", -EINVAL);
362         }
363
364         return 0;
365 }
366
367 static int h_read_settings(struct scene_obj *obj, void *vpriv)
368 {
369         struct cedit_iter_priv *priv = vpriv;
370         ofnode node = priv->node;
371
372         switch (obj->type) {
373         case SCENEOBJT_NONE:
374         case SCENEOBJT_IMAGE:
375         case SCENEOBJT_TEXT:
376                 break;
377         case SCENEOBJT_MENU: {
378                 struct scene_obj_menu *menu;
379                 uint val;
380
381                 if (ofnode_read_u32(node, obj->name, &val))
382                         return log_msg_ret("rd", -ENOENT);
383                 menu = (struct scene_obj_menu *)obj;
384                 menu->cur_item_id = val;
385
386                 break;
387         }
388         }
389
390         return 0;
391 }
392
393 int cedit_read_settings(struct expo *exp, oftree tree)
394 {
395         struct cedit_iter_priv priv;
396         ofnode root, node;
397         int ret;
398
399         root = oftree_root(tree);
400         if (!ofnode_valid(root))
401                 return log_msg_ret("roo", -ENOENT);
402         node = ofnode_find_subnode(root, CEDIT_NODE_NAME);
403         if (!ofnode_valid(node))
404                 return log_msg_ret("pat", -ENOENT);
405
406         /* read in the items */
407         priv.node = node;
408         ret = expo_iter_scene_objs(exp, h_read_settings, &priv);
409         if (ret) {
410                 log_debug("Failed to read settings (err=%d)\n", ret);
411                 return log_msg_ret("set", ret);
412         }
413
414         return 0;
415 }
416
417 static int h_write_settings_env(struct scene_obj *obj, void *vpriv)
418 {
419         const struct scene_obj_menu *menu;
420         struct cedit_iter_priv *priv = vpriv;
421         char name[80], var[60];
422         const char *str;
423         int val, ret;
424
425         if (obj->type != SCENEOBJT_MENU)
426                 return 0;
427
428         menu = (struct scene_obj_menu *)obj;
429         val = menu->cur_item_id;
430         snprintf(var, sizeof(var), "c.%s", obj->name);
431
432         if (priv->verbose)
433                 printf("%s=%d\n", var, val);
434
435         ret = env_set_ulong(var, val);
436         if (ret)
437                 return log_msg_ret("set", ret);
438
439         ret = get_cur_menuitem_text(menu, &str);
440         if (ret)
441                 return log_msg_ret("mis", ret);
442
443         snprintf(name, sizeof(name), "c.%s-str", obj->name);
444         if (priv->verbose)
445                 printf("%s=%s\n", name, str);
446
447         ret = env_set(name, str);
448         if (ret)
449                 return log_msg_ret("st2", ret);
450
451         return 0;
452 }
453
454 int cedit_write_settings_env(struct expo *exp, bool verbose)
455 {
456         struct cedit_iter_priv priv;
457         int ret;
458
459         /* write out the items */
460         priv.verbose = verbose;
461         ret = expo_iter_scene_objs(exp, h_write_settings_env, &priv);
462         if (ret) {
463                 log_debug("Failed to write settings to env (err=%d)\n", ret);
464                 return log_msg_ret("set", ret);
465         }
466
467         return 0;
468 }
469
470 static int h_read_settings_env(struct scene_obj *obj, void *vpriv)
471 {
472         struct cedit_iter_priv *priv = vpriv;
473         struct scene_obj_menu *menu;
474         char var[60];
475         int val;
476
477         if (obj->type != SCENEOBJT_MENU)
478                 return 0;
479
480         menu = (struct scene_obj_menu *)obj;
481         val = menu->cur_item_id;
482         snprintf(var, sizeof(var), "c.%s", obj->name);
483
484         val = env_get_ulong(var, 10, 0);
485         if (priv->verbose)
486                 printf("%s=%d\n", var, val);
487         if (!val)
488                 return log_msg_ret("get", -ENOENT);
489
490         /*
491          * note that no validation is done here, to make sure the ID is valid
492          * and actually points to a menu item
493          */
494         menu->cur_item_id = val;
495
496         return 0;
497 }
498
499 int cedit_read_settings_env(struct expo *exp, bool verbose)
500 {
501         struct cedit_iter_priv priv;
502         int ret;
503
504         /* write out the items */
505         priv.verbose = verbose;
506         ret = expo_iter_scene_objs(exp, h_read_settings_env, &priv);
507         if (ret) {
508                 log_debug("Failed to read settings from env (err=%d)\n", ret);
509                 return log_msg_ret("set", ret);
510         }
511
512         return 0;
513 }
514
515 /**
516  * get_cur_menuitem_seq() - Get the sequence number of a menu's current item
517  *
518  * Enumerates the items of a menu (0, 1, 2) and returns the sequence number of
519  * the currently selected item. If the first item is selected, this returns 0;
520  * if the second, 1; etc.
521  *
522  * @menu: Menu to check
523  * Return: Sequence number on success, else -ve error value
524  */
525 static int get_cur_menuitem_seq(const struct scene_obj_menu *menu)
526 {
527         const struct scene_menitem *mi;
528         int seq, found;
529
530         seq = 0;
531         found = -1;
532         list_for_each_entry(mi, &menu->item_head, sibling) {
533                 if (mi->id == menu->cur_item_id) {
534                         found = seq;
535                         break;
536                 }
537                 seq++;
538         }
539
540         if (found == -1)
541                 return log_msg_ret("nf", -ENOENT);
542
543         return found;
544 }
545
546 static int h_write_settings_cmos(struct scene_obj *obj, void *vpriv)
547 {
548         const struct scene_obj_menu *menu;
549         struct cedit_iter_priv *priv = vpriv;
550         int val, ret;
551         uint i, seq;
552
553         if (obj->type != SCENEOBJT_MENU)
554                 return 0;
555
556         menu = (struct scene_obj_menu *)obj;
557         val = menu->cur_item_id;
558
559         ret = get_cur_menuitem_seq(menu);
560         if (ret < 0)
561                 return log_msg_ret("cur", ret);
562         seq = ret;
563         log_debug("%s: seq=%d\n", menu->obj.name, seq);
564
565         /* figure out where to place this item */
566         if (!obj->bit_length)
567                 return log_msg_ret("len", -EINVAL);
568         if (obj->start_bit + obj->bit_length > CMOS_MAX_BITS)
569                 return log_msg_ret("bit", -E2BIG);
570
571         for (i = 0; i < obj->bit_length; i++, seq >>= 1) {
572                 uint bitnum = obj->start_bit + i;
573
574                 priv->mask[CMOS_BYTE(bitnum)] |= 1 << CMOS_BIT(bitnum);
575                 if (seq & 1)
576                         priv->value[CMOS_BYTE(bitnum)] |= BIT(CMOS_BIT(bitnum));
577                 log_debug("bit %x %x %x\n", bitnum,
578                           priv->mask[CMOS_BYTE(bitnum)],
579                           priv->value[CMOS_BYTE(bitnum)]);
580         }
581
582         return 0;
583 }
584
585 int cedit_write_settings_cmos(struct expo *exp, struct udevice *dev,
586                               bool verbose)
587 {
588         struct cedit_iter_priv priv;
589         int ret, i, count, first, last;
590
591         /* write out the items */
592         priv.mask = calloc(1, CMOS_MAX_BYTES);
593         if (!priv.mask)
594                 return log_msg_ret("mas", -ENOMEM);
595         priv.value = calloc(1, CMOS_MAX_BYTES);
596         if (!priv.value) {
597                 free(priv.mask);
598                 return log_msg_ret("val", -ENOMEM);
599         }
600
601         ret = expo_iter_scene_objs(exp, h_write_settings_cmos, &priv);
602         if (ret) {
603                 log_debug("Failed to write CMOS (err=%d)\n", ret);
604                 ret = log_msg_ret("set", ret);
605                 goto done;
606         }
607
608         /* write the data to the RTC */
609         first = CMOS_MAX_BYTES;
610         last = -1;
611         for (i = 0, count = 0; i < CMOS_MAX_BYTES; i++) {
612                 if (priv.mask[i]) {
613                         log_debug("Write byte %x: %x\n", i, priv.value[i]);
614                         ret = rtc_write8(dev, i, priv.value[i]);
615                         if (ret) {
616                                 ret = log_msg_ret("wri", ret);
617                                 goto done;
618                         }
619                         count++;
620                         first = min(first, i);
621                         last = max(last, i);
622                 }
623         }
624         if (verbose) {
625                 printf("Write %d bytes from offset %x to %x\n", count, first,
626                        last);
627         }
628
629 done:
630         free(priv.mask);
631         free(priv.value);
632         return ret;
633 }
634
635 static int h_read_settings_cmos(struct scene_obj *obj, void *vpriv)
636 {
637         struct cedit_iter_priv *priv = vpriv;
638         const struct scene_menitem *mi;
639         struct scene_obj_menu *menu;
640         int val, ret;
641         uint i;
642
643         if (obj->type != SCENEOBJT_MENU)
644                 return 0;
645
646         menu = (struct scene_obj_menu *)obj;
647
648         /* figure out where to place this item */
649         if (!obj->bit_length)
650                 return log_msg_ret("len", -EINVAL);
651         if (obj->start_bit + obj->bit_length > CMOS_MAX_BITS)
652                 return log_msg_ret("bit", -E2BIG);
653
654         val = 0;
655         for (i = 0; i < obj->bit_length; i++) {
656                 uint bitnum = obj->start_bit + i;
657                 uint offset = CMOS_BYTE(bitnum);
658
659                 /* read the byte if not already read */
660                 if (!priv->mask[offset]) {
661                         ret = rtc_read8(priv->dev, offset);
662                         if (ret < 0)
663                                 return  log_msg_ret("rea", ret);
664                         priv->value[offset] = ret;
665
666                         /* mark it as read */
667                         priv->mask[offset] = 0xff;
668                 }
669
670                 if (priv->value[offset] & BIT(CMOS_BIT(bitnum)))
671                         val |= BIT(i);
672                 log_debug("bit %x %x\n", bitnum, val);
673         }
674
675         /* update the current item */
676         mi = scene_menuitem_find_seq(menu, val);
677         if (!mi)
678                 return log_msg_ret("seq", -ENOENT);
679
680         menu->cur_item_id = mi->id;
681         log_debug("Update menu %d cur_item_id %d\n", menu->obj.id, mi->id);
682
683         return 0;
684 }
685
686 int cedit_read_settings_cmos(struct expo *exp, struct udevice *dev,
687                              bool verbose)
688 {
689         struct cedit_iter_priv priv;
690         int ret, i, count, first, last;
691
692         /* read in the items */
693         priv.mask = calloc(1, CMOS_MAX_BYTES);
694         if (!priv.mask)
695                 return log_msg_ret("mas", -ENOMEM);
696         priv.value = calloc(1, CMOS_MAX_BYTES);
697         if (!priv.value) {
698                 free(priv.mask);
699                 return log_msg_ret("val", -ENOMEM);
700         }
701         priv.dev = dev;
702
703         ret = expo_iter_scene_objs(exp, h_read_settings_cmos, &priv);
704         if (ret) {
705                 log_debug("Failed to read CMOS (err=%d)\n", ret);
706                 ret = log_msg_ret("set", ret);
707                 goto done;
708         }
709
710         /* read the data to the RTC */
711         first = CMOS_MAX_BYTES;
712         last = -1;
713         for (i = 0, count = 0; i < CMOS_MAX_BYTES; i++) {
714                 if (priv.mask[i]) {
715                         log_debug("Read byte %x: %x\n", i, priv.value[i]);
716                         count++;
717                         first = min(first, i);
718                         last = max(last, i);
719                 }
720         }
721         if (verbose) {
722                 printf("Read %d bytes from offset %x to %x\n", count, first,
723                        last);
724         }
725
726 done:
727         free(priv.mask);
728         free(priv.value);
729         return ret;
730 }
This page took 0.072038 seconds and 4 git commands to generate.