Enable network on VF2 board
[platform/kernel/u-boot.git] / boot / expo_build.c
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Building an expo from an FDT description
4  *
5  * Copyright 2022 Google LLC
6  * Written by Simon Glass <sjg@chromium.org>
7  */
8
9 #define LOG_CATEGORY    LOGC_EXPO
10
11 #include <common.h>
12 #include <expo.h>
13 #include <fdtdec.h>
14 #include <log.h>
15 #include <malloc.h>
16 #include <dm/ofnode.h>
17 #include <linux/libfdt.h>
18
19 /**
20  * struct build_info - Information to use when building
21  *
22  * @str_for_id: String for each ID in use, NULL if empty. The string is NULL
23  *      if there is nothing for this ID. Since ID 0 is never used, the first
24  *      element of this array is always NULL
25  * @str_count: Number of entries in @str_for_id
26  */
27 struct build_info {
28         const char **str_for_id;
29         int str_count;
30 };
31
32 /**
33  * add_txt_str - Add a string or lookup its ID, then add to expo
34  *
35  * @info: Build information
36  * @node: Node describing scene
37  * @scn: Scene to add to
38  * @find_name: Name to look for (e.g. "title"). This will find a property called
39  * "title" if it exists, else will look up the string for "title-id"
40  * Return: ID of added string, or -ve on error
41  */
42 int add_txt_str(struct build_info *info, ofnode node, struct scene *scn,
43                 const char *find_name, uint obj_id)
44 {
45         const char *text;
46         uint str_id;
47         int ret;
48
49         text = ofnode_read_string(node, find_name);
50         if (!text) {
51                 char name[40];
52                 u32 id;
53
54                 snprintf(name, sizeof(name), "%s-id", find_name);
55                 ret = ofnode_read_u32(node, name, &id);
56                 if (ret)
57                         return log_msg_ret("id", -EINVAL);
58
59                 if (id >= info->str_count)
60                         return log_msg_ret("id", -E2BIG);
61                 text = info->str_for_id[id];
62                 if (!text)
63                         return log_msg_ret("id", -EINVAL);
64         }
65
66         ret = expo_str(scn->expo, find_name, 0, text);
67         if (ret < 0)
68                 return log_msg_ret("add", ret);
69         str_id = ret;
70
71         ret = scene_txt_str(scn, find_name, obj_id, str_id, text, NULL);
72         if (ret < 0)
73                 return log_msg_ret("add", ret);
74
75         return ret;
76 }
77
78 /**
79  * add_txt_str_list - Add a list string or lookup its ID, then add to expo
80  *
81  * @info: Build information
82  * @node: Node describing scene
83  * @scn: Scene to add to
84  * @find_name: Name to look for (e.g. "title"). This will find a string-list
85  * property called "title" if it exists, else will look up the string in the
86  * "title-id" string list.
87  * Return: ID of added string, or -ve on error
88  */
89 int add_txt_str_list(struct build_info *info, ofnode node, struct scene *scn,
90                      const char *find_name, int index, uint obj_id)
91 {
92         const char *text;
93         uint str_id;
94         int ret;
95
96         ret = ofnode_read_string_index(node, find_name, index, &text);
97         if (ret) {
98                 char name[40];
99                 u32 id;
100
101                 snprintf(name, sizeof(name), "%s-id", find_name);
102                 ret = ofnode_read_u32_index(node, name, index, &id);
103                 if (ret)
104                         return log_msg_ret("id", -ENOENT);
105
106                 if (id >= info->str_count)
107                         return log_msg_ret("id", -E2BIG);
108                 text = info->str_for_id[id];
109                 if (!text)
110                         return log_msg_ret("id", -EINVAL);
111         }
112
113         ret = expo_str(scn->expo, find_name, 0, text);
114         if (ret < 0)
115                 return log_msg_ret("add", ret);
116         str_id = ret;
117
118         ret = scene_txt_str(scn, find_name, obj_id, str_id, text, NULL);
119         if (ret < 0)
120                 return log_msg_ret("add", ret);
121
122         return ret;
123 }
124
125 /*
126  * build_element() - Handle creating a text object from a label
127  *
128  * Look up a property called @label or @label-id and create a string for it
129  */
130 int build_element(void *ldtb, int node, const char *label)
131 {
132         return 0;
133 }
134
135 /**
136  * read_strings() - Read in the list of strings
137  *
138  * Read the strings into an ID-indexed list, so they can be used for building
139  * an expo. The strings are in a /strings node and each has its own subnode
140  * containing the ID and the string itself:
141  *
142  * example {
143  *    id = <123>;
144  *    value = "This is a test";
145  * };
146  *
147  * Future work may add support for unicode and multiple languages
148  *
149  * @info: Build information
150  * @root: Root node to read from
151  * Returns: 0 if OK, -ENOMEM if out of memory, -EINVAL if there is a format
152  * error
153  */
154 static int read_strings(struct build_info *info, ofnode root)
155 {
156         ofnode strings, node;
157
158         strings = ofnode_find_subnode(root, "strings");
159         if (!ofnode_valid(strings))
160                 return log_msg_ret("str", -EINVAL);
161
162         ofnode_for_each_subnode(node, strings) {
163                 const char *val;
164                 int ret;
165                 u32 id;
166
167                 ret = ofnode_read_u32(node, "id", &id);
168                 if (ret)
169                         return log_msg_ret("id", -EINVAL);
170                 val = ofnode_read_string(node, "value");
171                 if (!val)
172                         return log_msg_ret("val", -EINVAL);
173
174                 if (id >= info->str_count) {
175                         int new_count = info->str_count + 20;
176                         void *new_arr;
177
178                         new_arr = realloc(info->str_for_id,
179                                           new_count * sizeof(char *));
180                         if (!new_arr)
181                                 return log_msg_ret("id", -ENOMEM);
182                         memset(new_arr + info->str_count, '\0',
183                                (new_count - info->str_count) * sizeof(char *));
184                         info->str_for_id = new_arr;
185                         info->str_count = new_count;
186                 }
187
188                 info->str_for_id[id] = val;
189         }
190
191         return 0;
192 }
193
194 /**
195  * list_strings() - List the available strings with their IDs
196  *
197  * @info: Build information
198  */
199 static void list_strings(struct build_info *info)
200 {
201         int i;
202
203         for (i = 0; i < info->str_count; i++) {
204                 if (info->str_for_id[i])
205                         printf("%3d %s\n", i, info->str_for_id[i]);
206         }
207 }
208
209 /**
210  * menu_build() - Build a menu and add it to a scene
211  *
212  * See doc/develop/expo.rst for a description of the format
213  *
214  * @info: Build information
215  * @node: Node containing the menu description
216  * @scn: Scene to add the menu to
217  * Returns: 0 if OK, -ENOMEM if out of memory, -EINVAL if there is a format
218  * error, -ENOENT if there is a references to a non-existent string
219  */
220 static int menu_build(struct build_info *info, ofnode node, struct scene *scn)
221 {
222         struct scene_obj_menu *menu;
223         uint title_id, menu_id;
224         const u32 *item_ids;
225         int ret, size, i;
226         const char *name;
227         u32 id;
228
229         name = ofnode_get_name(node);
230         ret = ofnode_read_u32(node, "id", &id);
231         if (ret)
232                 return log_msg_ret("id", -EINVAL);
233
234         ret = scene_menu(scn, name, id, &menu);
235         if (ret < 0)
236                 return log_msg_ret("men", ret);
237         menu_id = ret;
238
239         /* Set the title */
240         ret = add_txt_str(info, node, scn, "title", 0);
241         if (ret < 0)
242                 return log_msg_ret("tit", ret);
243         title_id = ret;
244         ret = scene_menu_set_title(scn, menu_id, title_id);
245
246         item_ids = ofnode_read_prop(node, "item-id", &size);
247         if (!item_ids)
248                 return log_msg_ret("itm", -EINVAL);
249         if (!size || size % sizeof(u32))
250                 return log_msg_ret("isz", -EINVAL);
251         size /= sizeof(u32);
252
253         for (i = 0; i < size; i++) {
254                 struct scene_menitem *item;
255                 uint label, key, desc;
256
257                 ret = add_txt_str_list(info, node, scn, "item-label", i, 0);
258                 if (ret < 0 && ret != -ENOENT)
259                         return log_msg_ret("lab", ret);
260                 label = max(0, ret);
261
262                 ret = add_txt_str_list(info, node, scn, "key-label", i, 0);
263                 if (ret < 0 && ret != -ENOENT)
264                         return log_msg_ret("key", ret);
265                 key = max(0, ret);
266
267                 ret = add_txt_str_list(info, node, scn, "desc-label", i, 0);
268                 if (ret < 0  && ret != -ENOENT)
269                         return log_msg_ret("lab", ret);
270                 desc = max(0, ret);
271
272                 ret = scene_menuitem(scn, menu_id, simple_xtoa(i),
273                                      fdt32_to_cpu(item_ids[i]), key, label,
274                                      desc, 0, 0, &item);
275                 if (ret < 0)
276                         return log_msg_ret("mi", ret);
277         }
278
279         return 0;
280 }
281
282 /**
283  * menu_build() - Build an expo object and add it to a scene
284  *
285  * See doc/develop/expo.rst for a description of the format
286  *
287  * @info: Build information
288  * @node: Node containing the object description
289  * @scn: Scene to add the object to
290  * Returns: 0 if OK, -ENOMEM if out of memory, -EINVAL if there is a format
291  * error, -ENOENT if there is a references to a non-existent string
292  */
293 static int obj_build(struct build_info *info, ofnode node, struct scene *scn)
294 {
295         const char *type;
296         u32 id;
297         int ret;
298
299         log_debug("- object %s\n", ofnode_get_name(node));
300         ret = ofnode_read_u32(node, "id", &id);
301         if (ret)
302                 return log_msg_ret("id", -EINVAL);
303
304         type = ofnode_read_string(node, "type");
305         if (!type)
306                 return log_msg_ret("typ", -EINVAL);
307
308         if (!strcmp("menu", type))
309                 ret = menu_build(info, node, scn);
310          else
311                 ret = -EINVAL;
312         if (ret)
313                 return log_msg_ret("bld", ret);
314
315         return 0;
316 }
317
318 /**
319  * scene_build() - Build a scene and all its objects
320  *
321  * See doc/develop/expo.rst for a description of the format
322  *
323  * @info: Build information
324  * @node: Node containing the scene description
325  * @scn: Scene to add the object to
326  * Returns: 0 if OK, -ENOMEM if out of memory, -EINVAL if there is a format
327  * error, -ENOENT if there is a references to a non-existent string
328  */
329 static int scene_build(struct build_info *info, ofnode scn_node,
330                        struct expo *exp)
331 {
332         const char *name;
333         struct scene *scn;
334         uint id, title_id;
335         ofnode node;
336         int ret;
337
338         name = ofnode_get_name(scn_node);
339         log_debug("Building scene %s\n", name);
340         ret = ofnode_read_u32(scn_node, "id", &id);
341         if (ret)
342                 return log_msg_ret("id", -EINVAL);
343
344         ret = scene_new(exp, name, id, &scn);
345         if (ret < 0)
346                 return log_msg_ret("scn", ret);
347
348         ret = add_txt_str(info, scn_node, scn, "title", 0);
349         if (ret < 0)
350                 return log_msg_ret("tit", ret);
351         title_id = ret;
352         scene_title_set(scn, title_id);
353
354         ret = add_txt_str(info, scn_node, scn, "prompt", 0);
355         if (ret < 0)
356                 return log_msg_ret("pr", ret);
357
358         ofnode_for_each_subnode(node, scn_node) {
359                 ret = obj_build(info, node, scn);
360                 if (ret < 0)
361                         return log_msg_ret("mit", ret);
362         }
363
364         return 0;
365 }
366
367 int expo_build(ofnode root, struct expo **expp)
368 {
369         struct build_info info;
370         ofnode scenes, node;
371         struct expo *exp;
372         u32 dyn_start;
373         int ret;
374
375         memset(&info, '\0', sizeof(info));
376         ret = read_strings(&info, root);
377         if (ret)
378                 return log_msg_ret("str", ret);
379         if (_DEBUG)
380                 list_strings(&info);
381
382         ret = expo_new("name", NULL, &exp);
383         if (ret)
384                 return log_msg_ret("exp", ret);
385
386         if (!ofnode_read_u32(root, "dynamic-start", &dyn_start))
387                 expo_set_dynamic_start(exp, dyn_start);
388
389         scenes = ofnode_find_subnode(root, "scenes");
390         if (!ofnode_valid(scenes))
391                 return log_msg_ret("sno", -EINVAL);
392
393         ofnode_for_each_subnode(node, scenes) {
394                 ret = scene_build(&info, node, exp);
395                 if (ret < 0)
396                         return log_msg_ret("scn", ret);
397         }
398         *expp = exp;
399
400         return 0;
401 }