[BUILD] conditionally compile libfdt/*.c in libfdt/Makefile
[platform/kernel/u-boot.git] / libfdt / fdt_ro.c
1 /*
2  * libfdt - Flat Device Tree manipulation
3  * Copyright (C) 2006 David Gibson, IBM Corporation.
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public License
7  * as published by the Free Software Foundation; either version 2.1 of
8  * the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful, but
11  * WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18  */
19 #include "libfdt_env.h"
20
21 #include <fdt.h>
22 #include <libfdt.h>
23
24 #include "libfdt_internal.h"
25
26 #define CHECK_HEADER(fdt)       { \
27         int err; \
28         if ((err = fdt_check_header(fdt)) != 0) \
29                 return err; \
30 }
31
32 static int offset_streq(const void *fdt, int offset,
33                         const char *s, int len)
34 {
35         const char *p = fdt_offset_ptr(fdt, offset, len+1);
36
37         if (! p)
38                 /* short match */
39                 return 0;
40
41         if (memcmp(p, s, len) != 0)
42                 return 0;
43
44         if (p[len] != '\0')
45                 return 0;
46
47         return 1;
48 }
49
50 /*
51  * Checks if the property name matches.
52  */
53 static int prop_name_eq(const void *fdt, int offset, const char *name,
54                         struct fdt_property **prop, int *lenp)
55 {
56         int namestroff, len;
57
58         *prop = fdt_offset_ptr_typed(fdt, offset, *prop);
59         if (! *prop)
60                 return -FDT_ERR_BADSTRUCTURE;
61
62         namestroff = fdt32_to_cpu((*prop)->nameoff);
63         if (streq(fdt_string(fdt, namestroff), name)) {
64                 len = fdt32_to_cpu((*prop)->len);
65                 *prop = fdt_offset_ptr(fdt, offset,
66                                        sizeof(**prop) + len);
67                 if (*prop) {
68                         if (lenp)
69                                 *lenp = len;
70                         return 1;
71                 } else
72                         return -FDT_ERR_BADSTRUCTURE;
73         }
74         return 0;
75 }
76
77 /*
78  * Return a pointer to the string at the given string offset.
79  */
80 char *fdt_string(const void *fdt, int stroffset)
81 {
82         return (char *)fdt + fdt_off_dt_strings(fdt) + stroffset;
83 }
84
85 /*
86  * Check if the specified node is compatible by comparing the tokens
87  * in its "compatible" property with the specified string:
88  *
89  *   nodeoffset - starting place of the node
90  *   compat     - the string to match to one of the tokens in the
91  *                "compatible" list.
92  */
93 int fdt_node_is_compatible(const void *fdt, int nodeoffset,
94                            const char *compat)
95 {
96         const char* cp;
97         int cplen, len;
98
99         cp = fdt_getprop(fdt, nodeoffset, "compatible", &cplen);
100         if (cp == NULL)
101                 return 0;
102         while (cplen > 0) {
103                 if (strncmp(cp, compat, strlen(compat)) == 0)
104                         return 1;
105                 len = strlen(cp) + 1;
106                 cp += len;
107                 cplen -= len;
108         }
109
110         return 0;
111 }
112
113 /*
114  * Find a node by its device type property. On success, the offset of that
115  * node is returned or an error code otherwise:
116  *
117  *   nodeoffset - the node to start searching from or 0, the node you pass
118  *                will not be searched, only the next one will; typically,
119  *                you pass 0 to start the search and then what the previous
120  *                call returned.
121  *   type       - the device type string to match against.
122  */
123 int fdt_find_node_by_type(const void *fdt, int nodeoffset, const char *type)
124 {
125         int offset, nextoffset;
126         struct fdt_property *prop;
127         uint32_t tag;
128         int len, ret;
129
130         CHECK_HEADER(fdt);
131
132         tag = fdt_next_tag(fdt, nodeoffset, &nextoffset, NULL);
133         if (tag != FDT_BEGIN_NODE)
134                 return -FDT_ERR_BADOFFSET;
135         if (nodeoffset)
136                 nodeoffset = 0; /* start searching with next node */
137
138         while (1) {
139                 offset = nextoffset;
140                 tag = fdt_next_tag(fdt, offset, &nextoffset, NULL);
141
142                 switch (tag) {
143                 case FDT_BEGIN_NODE:
144                         nodeoffset = offset;
145                         break;
146
147                 case FDT_PROP:
148                         if (nodeoffset == 0)
149                                 break;
150                         ret = prop_name_eq(fdt, offset, "device_type",
151                                            &prop, &len);
152                         if (ret < 0)
153                                 return ret;
154                         else if (ret > 0 &&
155                                  strncmp(prop->data, type, len - 1) == 0)
156                             return nodeoffset;
157                         break;
158
159                 case FDT_END_NODE:
160                 case FDT_NOP:
161                         break;
162
163                 case FDT_END:
164                         return -FDT_ERR_NOTFOUND;
165
166                 default:
167                         return -FDT_ERR_BADSTRUCTURE;
168                 }
169         }
170 }
171
172 /*
173  * Find a node based on its device type and one of the tokens in its its
174  * "compatible" property. On success, the offset of that node is returned
175  * or an error code otherwise:
176  *
177  *   nodeoffset - the node to start searching from or 0, the node you pass
178  *                will not be searched, only the next one will; typically,
179  *                you pass 0 to start the search and then what the previous
180  *                call returned.
181  *   type       - the device type string to match against.
182  *   compat     - the string to match to one of the tokens in the
183  *                "compatible" list.
184  */
185 int fdt_find_compatible_node(const void *fdt, int nodeoffset,
186                              const char *type, const char *compat)
187 {
188         int offset;
189
190         offset = fdt_find_node_by_type(fdt, nodeoffset, type);
191         if (offset < 0 || fdt_node_is_compatible(fdt, offset, compat))
192                 return offset;
193
194         return -FDT_ERR_NOTFOUND;
195 }
196
197 /*
198  * Return the node offset of the node specified by:
199  *   parentoffset - starting place (0 to start at the root)
200  *   name         - name being searched for
201  *   namelen      - length of the name: typically strlen(name)
202  *
203  * Notes:
204  *   If the start node has subnodes, the subnodes are _not_ searched for the
205  *     requested name.
206  */
207 int fdt_subnode_offset_namelen(const void *fdt, int parentoffset,
208                                const char *name, int namelen)
209 {
210         int level = 0;
211         uint32_t tag;
212         int offset, nextoffset;
213
214         CHECK_HEADER(fdt);
215
216         tag = fdt_next_tag(fdt, parentoffset, &nextoffset, NULL);
217         if (tag != FDT_BEGIN_NODE)
218                 return -FDT_ERR_BADOFFSET;
219
220         do {
221                 offset = nextoffset;
222                 tag = fdt_next_tag(fdt, offset, &nextoffset, NULL);
223
224                 switch (tag) {
225                 case FDT_END:
226                         return -FDT_ERR_TRUNCATED;
227
228                 case FDT_BEGIN_NODE:
229                         level++;
230                         /*
231                          * If we are nested down levels, ignore the strings
232                          * until we get back to the proper level.
233                          */
234                         if (level != 1)
235                                 continue;
236
237                         /* Return the offset if this is "our" string. */
238                         if (offset_streq(fdt, offset+FDT_TAGSIZE, name, namelen))
239                                 return offset;
240                         break;
241
242                 case FDT_END_NODE:
243                         level--;
244                         break;
245
246                 case FDT_PROP:
247                 case FDT_NOP:
248                         break;
249
250                 default:
251                         return -FDT_ERR_BADSTRUCTURE;
252                 }
253         } while (level >= 0);
254
255         return -FDT_ERR_NOTFOUND;
256 }
257
258 /*
259  * See fdt_subnode_offset_namelen()
260  */
261 int fdt_subnode_offset(const void *fdt, int parentoffset,
262                        const char *name)
263 {
264         return fdt_subnode_offset_namelen(fdt, parentoffset, name, strlen(name));
265 }
266
267 /*
268  * Searches for the node corresponding to the given path and returns the
269  * offset of that node.
270  */
271 int fdt_find_node_by_path(const void *fdt, const char *path)
272 {
273         const char *end = path + strlen(path);
274         const char *p = path;
275         int offset = 0;
276
277         CHECK_HEADER(fdt);
278
279         /* Paths must be absolute */
280         if (*path != '/')
281                 return -FDT_ERR_BADPATH;
282
283         /* Handle the root path: root offset is 0 */
284         if (strcmp(path, "/") == 0)
285                 return 0;
286
287         while (*p) {
288                 const char *q;
289
290                 /* Skip path separator(s) */
291                 while (*p == '/')
292                         p++;
293                 if (! *p)
294                         return -FDT_ERR_BADPATH;
295
296                 /*
297                  * Find the next path separator.  The characters between
298                  * p and q are the next segment of the the path to find.
299                  */
300                 q = strchr(p, '/');
301                 if (! q)
302                         q = end;
303
304                 /*
305                  * Find the offset corresponding to the this path segment.
306                  */
307                 offset = fdt_subnode_offset_namelen(fdt, offset, p, q-p);
308
309                 /* Oops, error, abort abort abort */
310                 if (offset < 0)
311                         return offset;
312
313                 p = q;
314         }
315
316         return offset;
317 }
318
319 /*
320  * Given the offset of a node and a name of a property in that node, return
321  * a pointer to the property struct.
322  */
323 struct fdt_property *fdt_get_property(const void *fdt,
324                                       int nodeoffset,
325                                       const char *name, int *lenp)
326 {
327         int level = 0;
328         uint32_t tag;
329         struct fdt_property *prop;
330         int offset, nextoffset;
331         int err;
332
333         if ((err = fdt_check_header(fdt)) != 0)
334                 goto fail;
335
336         err = -FDT_ERR_BADOFFSET;
337         if (nodeoffset % FDT_TAGSIZE)
338                 goto fail;
339
340         tag = fdt_next_tag(fdt, nodeoffset, &nextoffset, NULL);
341         if (tag != FDT_BEGIN_NODE)
342                 goto fail;
343
344         do {
345                 offset = nextoffset;
346
347                 tag = fdt_next_tag(fdt, offset, &nextoffset, NULL);
348                 switch (tag) {
349                 case FDT_END:
350                         err = -FDT_ERR_TRUNCATED;
351                         goto fail;
352
353                 case FDT_BEGIN_NODE:
354                         level++;
355                         break;
356
357                 case FDT_END_NODE:
358                         level--;
359                         break;
360
361                 case FDT_PROP:
362                         /*
363                          * If we are nested down levels, ignore the strings
364                          * until we get back to the proper level.
365                          */
366                         if (level != 0)
367                                 continue;
368
369                         err = prop_name_eq(fdt, offset, name, &prop, lenp);
370                         if (err > 0)
371                                 return prop;
372                         else if (err < 0)
373                                 goto fail;
374                         break;
375
376                 case FDT_NOP:
377                         break;
378
379                 default:
380                         err = -FDT_ERR_BADSTRUCTURE;
381                         goto fail;
382                 }
383         } while (level >= 0);
384
385         err = -FDT_ERR_NOTFOUND;
386 fail:
387         if (lenp)
388                 *lenp = err;
389         return NULL;
390 }
391
392 /*
393  * Given the offset of a node and a name of a property in that node, return
394  * a pointer to the property data (ONLY).
395  */
396 void *fdt_getprop(const void *fdt, int nodeoffset,
397                   const char *name, int *lenp)
398 {
399         const struct fdt_property *prop;
400
401         prop = fdt_get_property(fdt, nodeoffset, name, lenp);
402         if (! prop)
403                 return NULL;
404
405         return (void *)prop->data;
406 }
407
408
409 uint32_t fdt_next_tag(const void *fdt, int offset, int *nextoffset, char **namep)
410 {
411         const uint32_t *tagp, *lenp;
412         uint32_t tag;
413         const char *p;
414
415         if (offset % FDT_TAGSIZE)
416                 return -1;
417
418         tagp = fdt_offset_ptr(fdt, offset, FDT_TAGSIZE);
419         if (! tagp)
420                 return FDT_END; /* premature end */
421         tag = fdt32_to_cpu(*tagp);
422         offset += FDT_TAGSIZE;
423
424         switch (tag) {
425         case FDT_BEGIN_NODE:
426                 if(namep)
427                         *namep = fdt_offset_ptr(fdt, offset, 1);
428
429                 /* skip name */
430                 do {
431                         p = fdt_offset_ptr(fdt, offset++, 1);
432                 } while (p && (*p != '\0'));
433                 if (! p)
434                         return FDT_END;
435                 break;
436         case FDT_PROP:
437                 lenp = fdt_offset_ptr(fdt, offset, sizeof(*lenp));
438                 if (! lenp)
439                         return FDT_END;
440                 /*
441                  * Get the property and set the namep to the name.
442                  */
443                 if(namep) {
444                         struct fdt_property *prop;
445
446                         prop = fdt_offset_ptr_typed(fdt, offset - FDT_TAGSIZE, prop);
447                         if (! prop)
448                                 return -FDT_ERR_BADSTRUCTURE;
449                         *namep = fdt_string(fdt, fdt32_to_cpu(prop->nameoff));
450                 }
451                 /* skip name offset, length and value */
452                 offset += 2*FDT_TAGSIZE + fdt32_to_cpu(*lenp);
453                 break;
454         }
455
456         if (nextoffset)
457                 *nextoffset = ALIGN(offset, FDT_TAGSIZE);
458
459         return tag;
460 }
461
462 /*
463  * Return the number of used reserve map entries and total slots available.
464  */
465 int fdt_num_reservemap(void *fdt, int *used, int *total)
466 {
467         struct fdt_reserve_entry *re;
468         int  start;
469         int  end;
470         int  err = fdt_check_header(fdt);
471
472         if (err != 0)
473                 return err;
474
475         start = fdt_off_mem_rsvmap(fdt);
476
477         /*
478          * Convention is that the reserve map is before the dt_struct,
479          * but it does not have to be.
480          */
481         end = fdt_totalsize(fdt);
482         if (end > fdt_off_dt_struct(fdt))
483                 end = fdt_off_dt_struct(fdt);
484         if (end > fdt_off_dt_strings(fdt))
485                 end = fdt_off_dt_strings(fdt);
486
487         /*
488          * Since the reserved area list is zero terminated, you get one fewer.
489          */
490         if (total)
491                 *total = ((end - start) / sizeof(struct fdt_reserve_entry)) - 1;
492
493         if (used) {
494                 *used = 0;
495                 while (start < end) {
496                         re = (struct fdt_reserve_entry *)(fdt + start);
497                         if (re->size == 0)
498                                 return 0;       /* zero size terminates the list */
499
500                         *used += 1;
501                         start += sizeof(struct fdt_reserve_entry);
502                 }
503                 /*
504                  * If we get here, there was no zero size termination.
505                  */
506                 return -FDT_ERR_BADLAYOUT;
507         }
508         return 0;
509 }
510
511 /*
512  * Return the nth reserve map entry.
513  */
514 int fdt_get_reservemap(void *fdt, int n, struct fdt_reserve_entry *re)
515 {
516         int  used;
517         int  total;
518         int  err;
519
520         err = fdt_num_reservemap(fdt, &used, &total);
521         if (err != 0)
522                 return err;
523
524         if (n >= total)
525                 return -FDT_ERR_NOSPACE;
526         if (re) {
527                 *re = *(struct fdt_reserve_entry *)
528                         _fdt_offset_ptr(fdt, n * sizeof(struct fdt_reserve_entry));
529         }
530         return 0;
531 }