1 // SPDX-License-Identifier: GPL-2.0+
3 * Building an expo from an FDT description
5 * Copyright 2022 Google LLC
6 * Written by Simon Glass <sjg@chromium.org>
9 #define LOG_CATEGORY LOGC_EXPO
16 #include <dm/ofnode.h>
17 #include <linux/libfdt.h>
20 * struct build_info - Information to use when building
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
28 const char **str_for_id;
33 * add_txt_str - Add a string or lookup its ID, then add to expo
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
42 int add_txt_str(struct build_info *info, ofnode node, struct scene *scn,
43 const char *find_name, uint obj_id)
49 text = ofnode_read_string(node, find_name);
54 snprintf(name, sizeof(name), "%s-id", find_name);
55 ret = ofnode_read_u32(node, name, &id);
57 return log_msg_ret("id", -EINVAL);
59 if (id >= info->str_count)
60 return log_msg_ret("id", -E2BIG);
61 text = info->str_for_id[id];
63 return log_msg_ret("id", -EINVAL);
66 ret = expo_str(scn->expo, find_name, 0, text);
68 return log_msg_ret("add", ret);
71 ret = scene_txt_str(scn, find_name, obj_id, str_id, text, NULL);
73 return log_msg_ret("add", ret);
79 * add_txt_str_list - Add a list string or lookup its ID, then add to expo
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
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)
96 ret = ofnode_read_string_index(node, find_name, index, &text);
101 snprintf(name, sizeof(name), "%s-id", find_name);
102 ret = ofnode_read_u32_index(node, name, index, &id);
104 return log_msg_ret("id", -ENOENT);
106 if (id >= info->str_count)
107 return log_msg_ret("id", -E2BIG);
108 text = info->str_for_id[id];
110 return log_msg_ret("id", -EINVAL);
113 ret = expo_str(scn->expo, find_name, 0, text);
115 return log_msg_ret("add", ret);
118 ret = scene_txt_str(scn, find_name, obj_id, str_id, text, NULL);
120 return log_msg_ret("add", ret);
126 * build_element() - Handle creating a text object from a label
128 * Look up a property called @label or @label-id and create a string for it
130 int build_element(void *ldtb, int node, const char *label)
136 * read_strings() - Read in the list of strings
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:
144 * value = "This is a test";
147 * Future work may add support for unicode and multiple languages
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
154 static int read_strings(struct build_info *info, ofnode root)
156 ofnode strings, node;
158 strings = ofnode_find_subnode(root, "strings");
159 if (!ofnode_valid(strings))
160 return log_msg_ret("str", -EINVAL);
162 ofnode_for_each_subnode(node, strings) {
167 ret = ofnode_read_u32(node, "id", &id);
169 return log_msg_ret("id", -EINVAL);
170 val = ofnode_read_string(node, "value");
172 return log_msg_ret("val", -EINVAL);
174 if (id >= info->str_count) {
175 int new_count = info->str_count + 20;
178 new_arr = realloc(info->str_for_id,
179 new_count * sizeof(char *));
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;
188 info->str_for_id[id] = val;
195 * list_strings() - List the available strings with their IDs
197 * @info: Build information
199 static void list_strings(struct build_info *info)
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]);
210 * menu_build() - Build a menu and add it to a scene
212 * See doc/develop/expo.rst for a description of the format
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
220 static int menu_build(struct build_info *info, ofnode node, struct scene *scn)
222 struct scene_obj_menu *menu;
223 uint title_id, menu_id;
229 name = ofnode_get_name(node);
230 ret = ofnode_read_u32(node, "id", &id);
232 return log_msg_ret("id", -EINVAL);
234 ret = scene_menu(scn, name, id, &menu);
236 return log_msg_ret("men", ret);
240 ret = add_txt_str(info, node, scn, "title", 0);
242 return log_msg_ret("tit", ret);
244 ret = scene_menu_set_title(scn, menu_id, title_id);
246 item_ids = ofnode_read_prop(node, "item-id", &size);
248 return log_msg_ret("itm", -EINVAL);
249 if (!size || size % sizeof(u32))
250 return log_msg_ret("isz", -EINVAL);
253 for (i = 0; i < size; i++) {
254 struct scene_menitem *item;
255 uint label, key, desc;
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);
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);
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);
272 ret = scene_menuitem(scn, menu_id, simple_xtoa(i),
273 fdt32_to_cpu(item_ids[i]), key, label,
276 return log_msg_ret("mi", ret);
283 * menu_build() - Build an expo object and add it to a scene
285 * See doc/develop/expo.rst for a description of the format
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
293 static int obj_build(struct build_info *info, ofnode node, struct scene *scn)
299 log_debug("- object %s\n", ofnode_get_name(node));
300 ret = ofnode_read_u32(node, "id", &id);
302 return log_msg_ret("id", -EINVAL);
304 type = ofnode_read_string(node, "type");
306 return log_msg_ret("typ", -EINVAL);
308 if (!strcmp("menu", type))
309 ret = menu_build(info, node, scn);
313 return log_msg_ret("bld", ret);
319 * scene_build() - Build a scene and all its objects
321 * See doc/develop/expo.rst for a description of the format
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
329 static int scene_build(struct build_info *info, ofnode scn_node,
338 name = ofnode_get_name(scn_node);
339 log_debug("Building scene %s\n", name);
340 ret = ofnode_read_u32(scn_node, "id", &id);
342 return log_msg_ret("id", -EINVAL);
344 ret = scene_new(exp, name, id, &scn);
346 return log_msg_ret("scn", ret);
348 ret = add_txt_str(info, scn_node, scn, "title", 0);
350 return log_msg_ret("tit", ret);
352 scene_title_set(scn, title_id);
354 ret = add_txt_str(info, scn_node, scn, "prompt", 0);
356 return log_msg_ret("pr", ret);
358 ofnode_for_each_subnode(node, scn_node) {
359 ret = obj_build(info, node, scn);
361 return log_msg_ret("mit", ret);
367 int expo_build(ofnode root, struct expo **expp)
369 struct build_info info;
375 memset(&info, '\0', sizeof(info));
376 ret = read_strings(&info, root);
378 return log_msg_ret("str", ret);
382 ret = expo_new("name", NULL, &exp);
384 return log_msg_ret("exp", ret);
386 if (!ofnode_read_u32(root, "dynamic-start", &dyn_start))
387 expo_set_dynamic_start(exp, dyn_start);
389 scenes = ofnode_find_subnode(root, "scenes");
390 if (!ofnode_valid(scenes))
391 return log_msg_ret("sno", -EINVAL);
393 ofnode_for_each_subnode(node, scenes) {
394 ret = scene_build(&info, node, exp);
396 return log_msg_ret("scn", ret);