1 /*
2  * Copyright (c) 2006-2023, RT-Thread Development Team
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 #include "dtb_node.h"
7 #include "libfdt.h"
8 #include "libfdt_env.h"
9 #include <ctype.h>
10 
_parse_integer_fixup_radix(const char * s,unsigned int * base)11 static const char *_parse_integer_fixup_radix(const char *s, unsigned int *base)
12 {
13     if (*base == 0)
14     {
15         if (s[0] == '0')
16         {
17             if (tolower(s[1]) == 'x' && isxdigit((int)s[2]))
18                 *base = 16;
19             else
20                 *base = 8;
21         }
22         else
23             *base = 10;
24     }
25     if (*base == 16 && s[0] == '0' && tolower(s[1]) == 'x')
26         s += 2;
27     return s;
28 }
29 
simple_strtoul(const char * cp,char ** endp,unsigned int base)30 unsigned long simple_strtoul(const char *cp, char **endp,
31                 unsigned int base)
32 {
33     unsigned long result = 0;
34     unsigned long value;
35 
36     cp = _parse_integer_fixup_radix(cp, &base);
37 
38     while (isxdigit((int)*cp) && (value = isdigit((int)*cp) ? *cp-'0' : (islower((int)*cp)
39         ? toupper(*cp) : *cp)-'A'+10) < base)
40     {
41         result = result*base + value;
42         cp++;
43     }
44 
45     if (endp)
46         *endp = (char *)cp;
47 
48     return result;
49 }
50 
strict_strtoul(const char * cp,unsigned int base,unsigned long * res)51 int strict_strtoul(const char *cp, unsigned int base, unsigned long *res)
52 {
53     char *tail;
54     unsigned long val;
55     size_t len;
56 
57     *res = 0;
58     len = strlen(cp);
59     if (len == 0)
60         return -EINVAL;
61 
62     val = simple_strtoul(cp, &tail, base);
63     if (tail == cp)
64         return -EINVAL;
65 
66     if ((*tail == '\0') ||
67         ((len == (size_t)(tail - cp) + 1) && (*tail == '\n')))
68     {
69         *res = val;
70         return 0;
71     }
72 
73     return -EINVAL;
74 }
75 
simple_strtol(const char * cp,char ** endp,unsigned int base)76 long simple_strtol(const char *cp, char **endp, unsigned int base)
77 {
78     if (*cp == '-')
79         return -simple_strtoul(cp + 1, endp, base);
80 
81     return simple_strtoul(cp, endp, base);
82 }
83 
dtb_node_read_bool(const struct dtb_node * node,const char * propname)84 rt_bool_t dtb_node_read_bool(const struct dtb_node *node, const char *propname)
85 {
86     const void *prop;
87 
88     RT_ASSERT(dtb_node_valid(node));
89     debug("%s: %s: ", __func__, propname);
90 
91     prop = dtb_node_get_property(node, propname, NULL);
92 
93     debug("%s\n", prop ? "true" : "false");
94 
95     return prop ? RT_TRUE : RT_FALSE;
96 }
97 
dtb_node_read_prop(const struct dtb_node * node,const char * propname,int * sizep)98 const void *dtb_node_read_prop(const struct dtb_node *node, const char *propname, int *sizep)
99 {
100     const char *val = NULL;
101     int len;
102 
103     RT_ASSERT(dtb_node_valid(node));
104     debug("%s: %s: ", __func__, propname);
105 
106     struct dtb_property *prop = dtb_node_get_dtb_node_property(node, propname, &len);
107 
108     if (prop)
109     {
110         val = prop->value;
111         len = prop->size;
112     }
113 
114     if (!val)
115     {
116         debug("<not found>\n");
117         if (sizep)
118             *sizep = -FDT_ERR_NOTFOUND;
119         return NULL;
120     }
121     if (sizep)
122         *sizep = len;
123 
124     return val;
125 }
126 
dtb_node_read_string(const struct dtb_node * node,const char * propname)127 const char *dtb_node_read_string(const struct dtb_node *node, const char *propname)
128 {
129     const char *str;
130     int len;
131 
132     str = dtb_node_read_prop(node, propname, &len);
133     if (!str)
134         return NULL;
135 
136     if (strnlen(str, len) >= len)
137     {
138         debug("<invalid>\n");
139         return NULL;
140     }
141     debug("%s\n", str);
142 
143     return str;
144 }
145 
dtb_node_find_subnode(const struct dtb_node * node,const char * subnode_name)146 const struct dtb_node *dtb_node_find_subnode(const struct dtb_node *node, const char *subnode_name)
147 {
148     const struct dtb_node *subnode;
149 
150     RT_ASSERT(dtb_node_valid(node));
151     debug("%s: %s: ", __func__, subnode_name);
152 
153     for (node = node->child; node; node = node->sibling)
154     {
155         if (!strcmp(subnode_name, node->name))
156             break;
157     }
158     subnode = node;
159 
160     debug("%s\n", dtb_node_valid(subnode) ?\
161           dtb_node_get_name(subnode) : "<none>");
162 
163     return subnode;
164 }
165 
dtb_node_first_subnode(const struct dtb_node * node)166 struct dtb_node *dtb_node_first_subnode(const struct dtb_node *node)
167 {
168     RT_ASSERT(dtb_node_valid(node));
169 
170     return node->child;
171 }
172 
dtb_node_next_subnode(const struct dtb_node * node)173 struct dtb_node *dtb_node_next_subnode(const struct dtb_node *node)
174 {
175     RT_ASSERT(dtb_node_valid(node));
176 
177     return node->sibling;
178 }
179 
dtb_node_get_name(const struct dtb_node * node)180 const char *dtb_node_get_name(const struct dtb_node *node)
181 {
182     if (!dtb_node_valid(node))
183     {
184         debug("%s node not valid\n", __func__);
185         return NULL;
186     }
187 
188     return strrchr(node->path, '/') + 1;
189 
190 }
191 
dtb_node_get_by_phandle(uint32_t phandle)192 struct dtb_node *dtb_node_get_by_phandle(uint32_t phandle)
193 {
194     if (dtb_node_active())
195         return dtb_node_find_node_by_phandle(phandle);
196     return NULL;
197 }
198 
dtb_node_read_size(const struct dtb_node * node,const char * propname)199 int dtb_node_read_size(const struct dtb_node *node, const char *propname)
200 {
201     struct dtb_property *prop = dtb_node_get_dtb_node_property( node, propname, NULL);
202 
203     if (prop)
204         return prop->size;
205 
206     return -EINVAL;
207 }
208 
dtb_node_get_addr_and_size_by_index(const struct dtb_node * node,int index,size_t * addr,size_t * size)209 int dtb_node_get_addr_and_size_by_index(const struct dtb_node *node, int index, size_t *addr, size_t *size)
210 {
211     const uint32_t *prop;
212     int psize;
213     int onesize, na, ns;
214 
215     na = dtb_node_n_addr_cells(node);
216     ns = dtb_node_n_size_cells(node);
217 
218     prop = dtb_node_get_dtb_node_property_value(node, "reg", &psize);
219     if (prop == NULL)
220     {
221         return -1;
222     }
223 
224     psize /= 4;
225     onesize = na + ns;
226 
227     if (psize >= (index + 1) * onesize)
228     {
229         prop += index * onesize;
230 
231         if (addr)
232         {
233             *addr = dtb_node_read_number(prop, na);
234         }
235         if (size)
236         {
237             *size = dtb_node_read_number(prop + na, ns);
238         }
239 
240         return 0;
241     }
242 
243     return -1;
244 }
245 
dtb_node_get_addr_index(const struct dtb_node * node,int index)246 size_t dtb_node_get_addr_index(const struct dtb_node *node, int index)
247 {
248     int na;
249     size_t size;
250 
251     const uint32_t *prop_val;
252     uint flags;
253 
254     prop_val = dtb_node_get_address(node, index,
255                     (uint64_t *)&size, &flags);
256     if (!prop_val)
257         return -1;
258 
259     na = dtb_node_n_addr_cells(node);
260 
261     return dtb_node_read_number(prop_val, na);
262 }
263 
dtb_node_get_addr(const struct dtb_node * node)264 size_t dtb_node_get_addr(const struct dtb_node *node)
265 {
266     return dtb_node_get_addr_index(node, 0);
267 }
268 
dtb_node_stringlist_search(const struct dtb_node * node,const char * property,const char * string)269 int dtb_node_stringlist_search(const struct dtb_node *node, const char *property,
270                  const char *string)
271 {
272     return dtb_node_property_match_string(node, property, string);
273 }
274 
dtb_node_read_string_index(const struct dtb_node * node,const char * property,int index,const char ** outp)275 int dtb_node_read_string_index(const struct dtb_node *node, const char *property, int index,
276                  const char **outp)
277 {
278     return dtb_node_property_read_string_index(node, property, index, outp);
279 }
280 
dtb_node_read_string_count(const struct dtb_node * node,const char * property)281 int dtb_node_read_string_count(const struct dtb_node *node, const char *property)
282 {
283     return dtb_node_property_count_strings(node, property);
284 }
285 
dtb_node_path(const char * path)286 struct dtb_node *dtb_node_path(const char *path)
287 {
288     if (dtb_node_active())
289         return dtb_node_find_node_by_path(path);
290     return NULL;
291 }
292 
dtb_node_get_chosen_prop(const char * name)293 const char *dtb_node_get_chosen_prop(const char *name)
294 {
295     const struct dtb_node *chosen_node;
296 
297     chosen_node = (const struct dtb_node *)dtb_node_path("/chosen");
298 
299     return dtb_node_read_string(chosen_node, name);
300 }
301 
dtb_node_get_chosen_node(const char * name)302 struct dtb_node *dtb_node_get_chosen_node(const char *name)
303 {
304     const char *prop;
305 
306     prop = dtb_node_get_chosen_prop(name);
307     if (!prop)
308         return NULL;
309 
310     return dtb_node_path(prop);
311 }
312 
dtb_node_get_property(const struct dtb_node * node,const char * propname,int * lenp)313 const void *dtb_node_get_property(const struct dtb_node *node, const char *propname, int *lenp)
314 {
315     return dtb_node_get_dtb_node_property_value(node, propname, lenp);
316 }
317 
dtb_node_is_available(const struct dtb_node * node)318 rt_bool_t dtb_node_is_available(const struct dtb_node *node)
319 {
320     return dtb_node_device_is_available(node);
321 }
322 
dtb_node_get_addr_size(const struct dtb_node * node,const char * property,size_t * sizep)323 size_t dtb_node_get_addr_size(const struct dtb_node *node, const char *property,
324                 size_t *sizep)
325 {
326     int na, ns;
327     int psize;
328     const uint32_t *prop = dtb_node_get_dtb_node_property_value(node, property, &psize);
329 
330     if (!prop)
331         return -1;
332     na = dtb_node_n_addr_cells(node);
333     ns = dtb_node_n_size_cells(node);
334     *sizep = dtb_node_read_number(prop + na, ns);
335 
336     return dtb_node_read_number(prop, na);
337 }
338 
dtb_node_read_u8_array_ptr(const struct dtb_node * node,const char * propname,size_t sz)339 const uint8_t *dtb_node_read_u8_array_ptr(const struct dtb_node *node, const char *propname,
340                     size_t sz)
341 {
342     int psize;
343     const uint32_t *prop = dtb_node_get_dtb_node_property_value(node, propname, &psize);
344 
345     if (!prop || sz != psize)
346         return NULL;
347     return (uint8_t *)prop;
348 }
349 
dtb_node_find_all_compatible_node(const struct dtb_node * from,const char * compatible,struct dtb_node ** node_table,int max_num,int * node_num)350 int dtb_node_find_all_compatible_node(const struct dtb_node *from, const char *compatible, struct dtb_node **node_table, int max_num, int *node_num)
351 {
352     const struct dtb_node *dn;
353     int num = 0;
354     for_each_of_allnodes_from(from, dn)
355     {
356         if (dtb_node_get_dtb_node_compatible_match(dn, compatible) &&
357             dtb_node_get(dn))
358         {
359 
360             num++;
361             *node_table = (struct dtb_node *)dn;
362             node_table++;
363             if (num >= max_num)
364             {
365                 break;
366             }
367         }
368     }
369     *node_num = num;
370     dtb_node_put(from);
371 
372     return 0;
373 }
374 
dtb_node_write_prop(const struct dtb_node * node,const char * propname,int len,const void * value)375 int dtb_node_write_prop(const struct dtb_node *node, const char *propname, int len,
376               const void *value)
377 {
378     struct dtb_property *pp;
379     struct dtb_property *pp_last = NULL;
380     struct dtb_property *new;
381 
382     if (!dtb_node_active())
383         return -ENOSYS;
384 
385     if (!node)
386         return -EINVAL;
387 
388     for (pp = node->properties; pp; pp = pp->next)
389     {
390         if (strcmp(pp->name, propname) == 0)
391         {
392             /* Property exists -> change value */
393             pp->value = (void *)value;
394             pp->size = len;
395             return 0;
396         }
397         pp_last = pp;
398     }
399 
400     if (!pp_last)
401         return -ENOENT;
402 
403     /* Property does not exist -> append new property */
404     new = (struct dtb_property *)malloc(sizeof(struct dtb_property));
405     if (!new)
406         return -ENOMEM;
407 
408     new->name = strdup(propname);
409     if (!new->name)
410     {
411         free(new);
412         return -ENOMEM;
413     }
414 
415     new->value = (void *)value;
416     new->size = len;
417     new->next = NULL;
418 
419     pp_last->next = new;
420 
421     return 0;
422 }
423 
dtb_node_write_string(const struct dtb_node * node,const char * propname,const char * value)424 int dtb_node_write_string(const struct dtb_node *node, const char *propname, const char *value)
425 {
426     if (!dtb_node_active())
427         return -ENOSYS;
428 
429     RT_ASSERT(dtb_node_valid(node));
430 
431     debug("%s: %s = %s", __func__, propname, value);
432 
433     return dtb_node_write_prop(node, propname, strlen(value) + 1, value);
434 }
435 
dtb_node_set_enabled(const struct dtb_node * node,rt_bool_t value)436 int dtb_node_set_enabled(const struct dtb_node *node, rt_bool_t value)
437 {
438     if (!dtb_node_active())
439         return -ENOSYS;
440 
441     RT_ASSERT(dtb_node_valid(node));
442 
443     if (value)
444         return dtb_node_write_string(node, "status", "okay");
445     else
446         return dtb_node_write_string(node, "status", "disable");
447 }
448 
449 /**
450  * dtb_node_irq_find_parent - Given a device node, find its interrupt parent node
451  * @child: pointer to device node
452  *
453  * Returns a pointer to the interrupt parent node, or NULL if the interrupt
454  * parent could not be determined.
455  */
dtb_node_irq_find_parent(struct dtb_node * child)456 static struct dtb_node *dtb_node_irq_find_parent(struct dtb_node *child)
457 {
458     struct dtb_node *p;
459     phandle parent;
460 
461     if (!dtb_node_get(child))
462         return NULL;
463     do
464     {
465         if (dtb_node_read_u32_array(child, "interrupt-parent", &parent, 1))
466         {
467             p = dtb_node_get_parent(child);
468         }
469         else
470         {
471             p = dtb_node_get_by_phandle(parent);
472         }
473         dtb_node_put(child);
474         child = p;
475     } while (p && dtb_node_get_property(p, "#interrupt-cells", NULL) == NULL);
476 
477     return p;
478 }
479 
dtb_node_irq_get(struct dtb_node * dev,int index)480 int dtb_node_irq_get(struct dtb_node *dev, int index)
481 {
482     int rc = 0;
483     struct fdt_phandle_args out_irq;
484     struct dtb_node *p;
485     uint32_t intsize;
486     int res, i;
487 
488     p = dtb_node_irq_find_parent(dev);
489     if (p == NULL)
490         return -EINVAL;
491     /* Get size of interrupt specifier */
492     if (dtb_node_read_u32_array(p, "#interrupt-cells", &intsize, 1))
493     {
494         res = -EINVAL;
495         goto out;
496     }
497 
498     debug(" path:%s, parent=%pOF, intsize=%d\n", p->path,p, intsize);
499 
500     /* Copy intspec into irq structure */
501     out_irq.np = p;
502     out_irq.args_count = intsize;
503     for (i = 0; i < intsize; i++)
504     {
505         res = dtb_node_read_u32_index(dev, "interrupts",
506                          (index * 3 + i),
507                          out_irq.args + i);
508         if (res)
509             goto out;
510     }
511     rc = out_irq.args[1];
512  out:
513     dtb_node_put(p);
514     return rc;
515 
516 }
517 
518 /**
519  * dtb_node_irq_get_byname - Decode a node's IRQ and return it as a Linux IRQ number
520  * @dev: pointer to device tree node
521  * @name: IRQ name
522  *
523  * Returns Linux IRQ number on success, or 0 on the IRQ mapping failure, or
524  * -EPROBE_DEFER if the IRQ domain is not yet created, or error code in case
525  * of any other failure.
526  */
dtb_node_irq_get_byname(struct dtb_node * dev,const char * name)527 int dtb_node_irq_get_byname(struct dtb_node *dev, const char *name)
528 {
529     int index;
530 
531     if (!name)
532         return -EINVAL;
533 
534     index = dtb_node_stringlist_search(dev, "interrupt-names", name);
535     if (index < 0)
536         return index;
537 
538     return dtb_node_irq_get(dev, index);
539 }
540 
541 /**
542  * dtb_node_irq_count - Count the number of IRQs a node uses
543  * @dev: pointer to device tree node
544  */
dtb_node_irq_count(struct dtb_node * device)545 int dtb_node_irq_count(struct dtb_node *device)
546 {
547     struct dtb_node *p;
548     uint32_t intsize;
549     int nr = 0, res = 0;
550 
551     p = dtb_node_irq_find_parent(device);
552     if (p == NULL)
553         return -EINVAL;
554 
555     /* Get size of interrupt specifier */
556     if (dtb_node_read_u32_array(p, "#interrupt-cells", &intsize, 1))
557     {
558         res = -EINVAL;
559         goto out;
560     }
561 
562     debug(" path:%s, parent=%pOF, intsize=%d\n", p->path,p, intsize);
563 
564     res = dtb_node_read_size(device, "interrupts");
565     if (res < 0)
566     {
567         goto out;
568     }
569     nr = res / (intsize * 4);
570  out:
571     dtb_node_put(p);
572 
573     return nr;
574 }
575