blob: 923c389ece9e3861902eb905ef60c14f567b17ab [file] [log] [blame]
Gerald Van Baren35748172007-03-31 12:00:56 -04001/*
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 */
Gerald Van Baren8096b3b2007-04-20 22:46:53 -040019#include "config.h"
20#if CONFIG_OF_LIBFDT
21
Gerald Van Baren35748172007-03-31 12:00:56 -040022#include "libfdt_env.h"
23
24#include <fdt.h>
25#include <libfdt.h>
26
27#include "libfdt_internal.h"
28
Wolfgang Denk94abd7c2007-04-04 01:49:15 +020029#define CHECK_HEADER(fdt) { \
30 int err; \
Gerald Van Baren6679f922007-04-06 14:17:14 -040031 if ((err = fdt_check_header(fdt)) != 0) \
Wolfgang Denk94abd7c2007-04-04 01:49:15 +020032 return err; \
33}
Gerald Van Baren35748172007-03-31 12:00:56 -040034
35static int offset_streq(const void *fdt, int offset,
36 const char *s, int len)
37{
38 const char *p = fdt_offset_ptr(fdt, offset, len+1);
39
40 if (! p)
41 /* short match */
42 return 0;
43
44 if (memcmp(p, s, len) != 0)
45 return 0;
46
47 if (p[len] != '\0')
48 return 0;
49
50 return 1;
51}
52
Gerald Van Baren3af0d582007-03-31 12:13:43 -040053/*
Gerald Van Baren9675ee72007-05-17 23:54:36 -040054 * Checks if the property name matches.
55 */
56static int prop_name_eq(const void *fdt, int offset, const char *name,
57 struct fdt_property **prop, int *lenp)
58{
59 int namestroff, len;
60
61 *prop = fdt_offset_ptr_typed(fdt, offset, *prop);
62 if (! *prop)
63 return -FDT_ERR_BADSTRUCTURE;
64
65 namestroff = fdt32_to_cpu((*prop)->nameoff);
66 if (streq(fdt_string(fdt, namestroff), name)) {
67 len = fdt32_to_cpu((*prop)->len);
68 *prop = fdt_offset_ptr(fdt, offset,
69 sizeof(**prop) + len);
70 if (*prop) {
71 if (lenp)
72 *lenp = len;
73 return 1;
74 } else
75 return -FDT_ERR_BADSTRUCTURE;
76 }
77 return 0;
78}
79
80/*
Gerald Van Baren3af0d582007-03-31 12:13:43 -040081 * Return a pointer to the string at the given string offset.
82 */
Gerald Van Baren35748172007-03-31 12:00:56 -040083char *fdt_string(const void *fdt, int stroffset)
84{
85 return (char *)fdt + fdt_off_dt_strings(fdt) + stroffset;
86}
87
Gerald Van Baren3af0d582007-03-31 12:13:43 -040088/*
Gerald Van Baren9675ee72007-05-17 23:54:36 -040089 * Check if the specified node is compatible by comparing the tokens
90 * in its "compatible" property with the specified string:
91 *
92 * nodeoffset - starting place of the node
93 * compat - the string to match to one of the tokens in the
94 * "compatible" list.
95 */
96int fdt_node_is_compatible(const void *fdt, int nodeoffset,
97 const char *compat)
98{
99 const char* cp;
100 int cplen, len;
101
102 cp = fdt_getprop(fdt, nodeoffset, "compatible", &cplen);
103 if (cp == NULL)
104 return 0;
105 while (cplen > 0) {
106 if (strncmp(cp, compat, strlen(compat)) == 0)
107 return 1;
108 len = strlen(cp) + 1;
109 cp += len;
110 cplen -= len;
111 }
112
113 return 0;
114}
115
116/*
117 * Find a node by its device type property. On success, the offset of that
118 * node is returned or an error code otherwise:
119 *
120 * nodeoffset - the node to start searching from or 0, the node you pass
121 * will not be searched, only the next one will; typically,
122 * you pass 0 to start the search and then what the previous
123 * call returned.
124 * type - the device type string to match against.
125 */
126int fdt_find_node_by_type(const void *fdt, int nodeoffset, const char *type)
127{
128 int offset, nextoffset;
129 struct fdt_property *prop;
130 uint32_t tag;
131 int len, ret;
132
133 CHECK_HEADER(fdt);
134
135 tag = fdt_next_tag(fdt, nodeoffset, &nextoffset, NULL);
136 if (tag != FDT_BEGIN_NODE)
137 return -FDT_ERR_BADOFFSET;
138 if (nodeoffset)
139 nodeoffset = 0; /* start searching with next node */
140
141 while (1) {
142 offset = nextoffset;
143 tag = fdt_next_tag(fdt, offset, &nextoffset, NULL);
144
145 switch (tag) {
146 case FDT_BEGIN_NODE:
147 nodeoffset = offset;
148 break;
149
150 case FDT_PROP:
151 if (nodeoffset == 0)
152 break;
153 ret = prop_name_eq(fdt, offset, "device_type",
154 &prop, &len);
155 if (ret < 0)
156 return ret;
157 else if (ret > 0 &&
158 strncmp(prop->data, type, len - 1) == 0)
159 return nodeoffset;
160 break;
161
162 case FDT_END_NODE:
163 case FDT_NOP:
164 break;
165
166 case FDT_END:
167 return -FDT_ERR_NOTFOUND;
168
169 default:
170 return -FDT_ERR_BADSTRUCTURE;
171 }
172 }
173}
174
175/*
176 * Find a node based on its device type and one of the tokens in its its
177 * "compatible" property. On success, the offset of that node is returned
178 * or an error code otherwise:
179 *
180 * nodeoffset - the node to start searching from or 0, the node you pass
181 * will not be searched, only the next one will; typically,
182 * you pass 0 to start the search and then what the previous
183 * call returned.
184 * type - the device type string to match against.
185 * compat - the string to match to one of the tokens in the
186 * "compatible" list.
187 */
188int fdt_find_compatible_node(const void *fdt, int nodeoffset,
189 const char *type, const char *compat)
190{
191 int offset;
192
193 offset = fdt_find_node_by_type(fdt, nodeoffset, type);
194 if (offset < 0 || fdt_node_is_compatible(fdt, offset, compat))
195 return offset;
196
197 return -FDT_ERR_NOTFOUND;
198}
199
200/*
Gerald Van Baren3af0d582007-03-31 12:13:43 -0400201 * Return the node offset of the node specified by:
202 * parentoffset - starting place (0 to start at the root)
203 * name - name being searched for
204 * namelen - length of the name: typically strlen(name)
205 *
206 * Notes:
207 * If the start node has subnodes, the subnodes are _not_ searched for the
208 * requested name.
209 */
Gerald Van Baren35748172007-03-31 12:00:56 -0400210int fdt_subnode_offset_namelen(const void *fdt, int parentoffset,
211 const char *name, int namelen)
212{
213 int level = 0;
214 uint32_t tag;
215 int offset, nextoffset;
216
217 CHECK_HEADER(fdt);
218
Gerald Van Baren3af0d582007-03-31 12:13:43 -0400219 tag = fdt_next_tag(fdt, parentoffset, &nextoffset, NULL);
Gerald Van Baren35748172007-03-31 12:00:56 -0400220 if (tag != FDT_BEGIN_NODE)
221 return -FDT_ERR_BADOFFSET;
222
223 do {
224 offset = nextoffset;
Gerald Van Baren3af0d582007-03-31 12:13:43 -0400225 tag = fdt_next_tag(fdt, offset, &nextoffset, NULL);
Gerald Van Baren35748172007-03-31 12:00:56 -0400226
227 switch (tag) {
228 case FDT_END:
229 return -FDT_ERR_TRUNCATED;
230
231 case FDT_BEGIN_NODE:
232 level++;
Gerald Van Baren3af0d582007-03-31 12:13:43 -0400233 /*
234 * If we are nested down levels, ignore the strings
235 * until we get back to the proper level.
236 */
Gerald Van Baren35748172007-03-31 12:00:56 -0400237 if (level != 1)
238 continue;
Gerald Van Baren3af0d582007-03-31 12:13:43 -0400239
240 /* Return the offset if this is "our" string. */
Gerald Van Baren35748172007-03-31 12:00:56 -0400241 if (offset_streq(fdt, offset+FDT_TAGSIZE, name, namelen))
Gerald Van Baren35748172007-03-31 12:00:56 -0400242 return offset;
243 break;
244
245 case FDT_END_NODE:
246 level--;
247 break;
248
249 case FDT_PROP:
250 case FDT_NOP:
251 break;
252
253 default:
254 return -FDT_ERR_BADSTRUCTURE;
255 }
256 } while (level >= 0);
257
258 return -FDT_ERR_NOTFOUND;
259}
260
Gerald Van Baren3af0d582007-03-31 12:13:43 -0400261/*
262 * See fdt_subnode_offset_namelen()
263 */
Gerald Van Baren35748172007-03-31 12:00:56 -0400264int fdt_subnode_offset(const void *fdt, int parentoffset,
265 const char *name)
266{
267 return fdt_subnode_offset_namelen(fdt, parentoffset, name, strlen(name));
268}
269
Gerald Van Baren3af0d582007-03-31 12:13:43 -0400270/*
271 * Searches for the node corresponding to the given path and returns the
272 * offset of that node.
273 */
Gerald Van Baren1a861162007-06-06 22:47:58 -0400274int fdt_find_node_by_path(const void *fdt, const char *path)
Gerald Van Baren35748172007-03-31 12:00:56 -0400275{
276 const char *end = path + strlen(path);
277 const char *p = path;
278 int offset = 0;
279
280 CHECK_HEADER(fdt);
281
Gerald Van Baren3af0d582007-03-31 12:13:43 -0400282 /* Paths must be absolute */
Gerald Van Baren35748172007-03-31 12:00:56 -0400283 if (*path != '/')
284 return -FDT_ERR_BADPATH;
285
286 while (*p) {
287 const char *q;
288
Gerald Van Baren3af0d582007-03-31 12:13:43 -0400289 /* Skip path separator(s) */
Gerald Van Baren35748172007-03-31 12:00:56 -0400290 while (*p == '/')
291 p++;
292 if (! *p)
293 return -FDT_ERR_BADPATH;
Gerald Van Baren3af0d582007-03-31 12:13:43 -0400294
295 /*
296 * Find the next path separator. The characters between
297 * p and q are the next segment of the the path to find.
298 */
Gerald Van Baren35748172007-03-31 12:00:56 -0400299 q = strchr(p, '/');
300 if (! q)
301 q = end;
302
Gerald Van Baren3af0d582007-03-31 12:13:43 -0400303 /*
304 * Find the offset corresponding to the this path segment.
305 */
Gerald Van Baren35748172007-03-31 12:00:56 -0400306 offset = fdt_subnode_offset_namelen(fdt, offset, p, q-p);
Gerald Van Baren3af0d582007-03-31 12:13:43 -0400307
308 /* Oops, error, abort abort abort */
Gerald Van Baren35748172007-03-31 12:00:56 -0400309 if (offset < 0)
310 return offset;
311
312 p = q;
313 }
314
Gerald Van Barenaea03c42007-03-31 14:30:53 -0400315 return offset;
Gerald Van Baren35748172007-03-31 12:00:56 -0400316}
317
Gerald Van Baren3af0d582007-03-31 12:13:43 -0400318/*
319 * Given the offset of a node and a name of a property in that node, return
320 * a pointer to the property struct.
321 */
Gerald Van Baren35748172007-03-31 12:00:56 -0400322struct fdt_property *fdt_get_property(const void *fdt,
323 int nodeoffset,
324 const char *name, int *lenp)
325{
326 int level = 0;
327 uint32_t tag;
328 struct fdt_property *prop;
Gerald Van Baren35748172007-03-31 12:00:56 -0400329 int offset, nextoffset;
330 int err;
331
Gerald Van Baren6679f922007-04-06 14:17:14 -0400332 if ((err = fdt_check_header(fdt)) != 0)
Gerald Van Baren35748172007-03-31 12:00:56 -0400333 goto fail;
334
335 err = -FDT_ERR_BADOFFSET;
336 if (nodeoffset % FDT_TAGSIZE)
337 goto fail;
338
Gerald Van Baren3af0d582007-03-31 12:13:43 -0400339 tag = fdt_next_tag(fdt, nodeoffset, &nextoffset, NULL);
Gerald Van Baren35748172007-03-31 12:00:56 -0400340 if (tag != FDT_BEGIN_NODE)
341 goto fail;
342
343 do {
344 offset = nextoffset;
345
Gerald Van Baren3af0d582007-03-31 12:13:43 -0400346 tag = fdt_next_tag(fdt, offset, &nextoffset, NULL);
Gerald Van Baren35748172007-03-31 12:00:56 -0400347 switch (tag) {
348 case FDT_END:
349 err = -FDT_ERR_TRUNCATED;
350 goto fail;
351
352 case FDT_BEGIN_NODE:
353 level++;
354 break;
355
356 case FDT_END_NODE:
357 level--;
358 break;
359
360 case FDT_PROP:
Gerald Van Baren3af0d582007-03-31 12:13:43 -0400361 /*
362 * If we are nested down levels, ignore the strings
363 * until we get back to the proper level.
364 */
Gerald Van Baren35748172007-03-31 12:00:56 -0400365 if (level != 0)
366 continue;
367
Gerald Van Baren9675ee72007-05-17 23:54:36 -0400368 err = prop_name_eq(fdt, offset, name, &prop, lenp);
369 if (err > 0)
Gerald Van Baren35748172007-03-31 12:00:56 -0400370 return prop;
Gerald Van Baren9675ee72007-05-17 23:54:36 -0400371 else if (err < 0)
372 goto fail;
Gerald Van Baren35748172007-03-31 12:00:56 -0400373 break;
374
375 case FDT_NOP:
376 break;
377
378 default:
379 err = -FDT_ERR_BADSTRUCTURE;
380 goto fail;
381 }
382 } while (level >= 0);
383
384 err = -FDT_ERR_NOTFOUND;
Wolfgang Denk94abd7c2007-04-04 01:49:15 +0200385fail:
Gerald Van Baren35748172007-03-31 12:00:56 -0400386 if (lenp)
387 *lenp = err;
388 return NULL;
389}
390
Gerald Van Baren3af0d582007-03-31 12:13:43 -0400391/*
392 * Given the offset of a node and a name of a property in that node, return
393 * a pointer to the property data (ONLY).
394 */
Gerald Van Baren35748172007-03-31 12:00:56 -0400395void *fdt_getprop(const void *fdt, int nodeoffset,
396 const char *name, int *lenp)
397{
398 const struct fdt_property *prop;
399
400 prop = fdt_get_property(fdt, nodeoffset, name, lenp);
401 if (! prop)
402 return NULL;
403
Gerald Van Baren3af0d582007-03-31 12:13:43 -0400404 return (void *)prop->data;
Gerald Van Baren35748172007-03-31 12:00:56 -0400405}
Gerald Van Baren3af0d582007-03-31 12:13:43 -0400406
407
408uint32_t fdt_next_tag(const void *fdt, int offset, int *nextoffset, char **namep)
409{
410 const uint32_t *tagp, *lenp;
411 uint32_t tag;
412 const char *p;
413
414 if (offset % FDT_TAGSIZE)
415 return -1;
416
417 tagp = fdt_offset_ptr(fdt, offset, FDT_TAGSIZE);
418 if (! tagp)
419 return FDT_END; /* premature end */
420 tag = fdt32_to_cpu(*tagp);
421 offset += FDT_TAGSIZE;
422
423 switch (tag) {
424 case FDT_BEGIN_NODE:
425 if(namep)
426 *namep = fdt_offset_ptr(fdt, offset, 1);
427
428 /* skip name */
429 do {
430 p = fdt_offset_ptr(fdt, offset++, 1);
431 } while (p && (*p != '\0'));
432 if (! p)
433 return FDT_END;
434 break;
435 case FDT_PROP:
436 lenp = fdt_offset_ptr(fdt, offset, sizeof(*lenp));
437 if (! lenp)
438 return FDT_END;
439 /*
440 * Get the property and set the namep to the name.
441 */
442 if(namep) {
443 struct fdt_property *prop;
444
445 prop = fdt_offset_ptr_typed(fdt, offset - FDT_TAGSIZE, prop);
446 if (! prop)
447 return -FDT_ERR_BADSTRUCTURE;
448 *namep = fdt_string(fdt, fdt32_to_cpu(prop->nameoff));
449 }
450 /* skip name offset, length and value */
451 offset += 2*FDT_TAGSIZE + fdt32_to_cpu(*lenp);
452 break;
453 }
454
455 if (nextoffset)
456 *nextoffset = ALIGN(offset, FDT_TAGSIZE);
457
458 return tag;
459}
Gerald Van Baren3f9f08c2007-04-14 22:46:41 -0400460
461/*
462 * Return the number of used reserve map entries and total slots available.
463 */
464int fdt_num_reservemap(void *fdt, int *used, int *total)
465{
466 struct fdt_reserve_entry *re;
467 int start;
468 int end;
469 int err = fdt_check_header(fdt);
470
471 if (err != 0)
472 return err;
473
474 start = fdt_off_mem_rsvmap(fdt);
475
476 /*
477 * Convention is that the reserve map is before the dt_struct,
478 * but it does not have to be.
479 */
480 end = fdt_totalsize(fdt);
481 if (end > fdt_off_dt_struct(fdt))
482 end = fdt_off_dt_struct(fdt);
483 if (end > fdt_off_dt_strings(fdt))
484 end = fdt_off_dt_strings(fdt);
485
486 /*
487 * Since the reserved area list is zero terminated, you get one fewer.
488 */
489 if (total)
490 *total = ((end - start) / sizeof(struct fdt_reserve_entry)) - 1;
491
492 if (used) {
493 *used = 0;
494 while (start < end) {
495 re = (struct fdt_reserve_entry *)(fdt + start);
496 if (re->size == 0)
497 return 0; /* zero size terminates the list */
498
499 *used += 1;
500 start += sizeof(struct fdt_reserve_entry);
501 }
502 /*
503 * If we get here, there was no zero size termination.
504 */
505 return -FDT_ERR_BADLAYOUT;
506 }
507 return 0;
508}
509
510/*
511 * Return the nth reserve map entry.
512 */
513int fdt_get_reservemap(void *fdt, int n, struct fdt_reserve_entry *re)
514{
515 int used;
516 int total;
517 int err;
518
519 err = fdt_num_reservemap(fdt, &used, &total);
520 if (err != 0)
521 return err;
522
523 if (n >= total)
524 return -FDT_ERR_NOSPACE;
525 if (re) {
526 *re = *(struct fdt_reserve_entry *)
527 _fdt_offset_ptr(fdt, n * sizeof(struct fdt_reserve_entry));
528 }
529 return 0;
530}
Gerald Van Baren8096b3b2007-04-20 22:46:53 -0400531
532#endif /* CONFIG_OF_LIBFDT */
533