]> git.proxmox.com Git - mirror_edk2.git/blame - EmbeddedPkg/Library/FdtLib/fdt_ro.c
EmbeddedPkg/FdtLib: Update FdtLib to v1.4.5
[mirror_edk2.git] / EmbeddedPkg / Library / FdtLib / fdt_ro.c
CommitLineData
1e57a462 1/*\r
2 * libfdt - Flat Device Tree manipulation\r
3 * Copyright (C) 2006 David Gibson, IBM Corporation.\r
4 *\r
5 * libfdt is dual licensed: you can use it either under the terms of\r
6 * the GPL, or the BSD license, at your option.\r
7 *\r
8 * a) This library is free software; you can redistribute it and/or\r
9 * modify it under the terms of the GNU General Public License as\r
10 * published by the Free Software Foundation; either version 2 of the\r
11 * License, or (at your option) any later version.\r
12 *\r
13 * This library is distributed in the hope that it will be useful,\r
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
16 * GNU General Public License for more details.\r
17 *\r
18 * You should have received a copy of the GNU General Public\r
19 * License along with this library; if not, write to the Free\r
20 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,\r
21 * MA 02110-1301 USA\r
22 *\r
23 * Alternatively,\r
24 *\r
25 * b) Redistribution and use in source and binary forms, with or\r
26 * without modification, are permitted provided that the following\r
27 * conditions are met:\r
28 *\r
29 * 1. Redistributions of source code must retain the above\r
30 * copyright notice, this list of conditions and the following\r
31 * disclaimer.\r
32 * 2. Redistributions in binary form must reproduce the above\r
33 * copyright notice, this list of conditions and the following\r
34 * disclaimer in the documentation and/or other materials\r
35 * provided with the distribution.\r
36 *\r
37 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND\r
38 * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,\r
39 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF\r
40 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\r
41 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR\r
42 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\r
43 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT\r
44 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\r
45 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\r
46 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\r
47 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR\r
48 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,\r
49 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\r
50 */\r
51#include "libfdt_env.h"\r
52\r
53#include <fdt.h>\r
54#include <libfdt.h>\r
55\r
56#include "libfdt_internal.h"\r
57\r
58static int _fdt_nodename_eq(const void *fdt, int offset,\r
59 const char *s, int len)\r
60{\r
61 const char *p = fdt_offset_ptr(fdt, offset + FDT_TAGSIZE, len+1);\r
62\r
a0992390 63 if (!p)\r
1e57a462 64 /* short match */\r
65 return 0;\r
66\r
67 if (memcmp(p, s, len) != 0)\r
68 return 0;\r
69\r
70 if (p[len] == '\0')\r
71 return 1;\r
72 else if (!memchr(s, '@', len) && (p[len] == '@'))\r
73 return 1;\r
74 else\r
75 return 0;\r
76}\r
77\r
78const char *fdt_string(const void *fdt, int stroffset)\r
79{\r
80 return (const char *)fdt + fdt_off_dt_strings(fdt) + stroffset;\r
81}\r
82\r
83static int _fdt_string_eq(const void *fdt, int stroffset,\r
84 const char *s, int len)\r
85{\r
86 const char *p = fdt_string(fdt, stroffset);\r
87\r
88 return (strlen(p) == len) && (memcmp(p, s, len) == 0);\r
89}\r
90\r
a0992390
PB
91uint32_t fdt_get_max_phandle(const void *fdt)\r
92{\r
93 uint32_t max_phandle = 0;\r
94 int offset;\r
95\r
96 for (offset = fdt_next_node(fdt, -1, NULL);;\r
97 offset = fdt_next_node(fdt, offset, NULL)) {\r
98 uint32_t phandle;\r
99\r
100 if (offset == -FDT_ERR_NOTFOUND)\r
101 return max_phandle;\r
102\r
103 if (offset < 0)\r
104 return (uint32_t)-1;\r
105\r
106 phandle = fdt_get_phandle(fdt, offset);\r
107 if (phandle == (uint32_t)-1)\r
108 continue;\r
109\r
110 if (phandle > max_phandle)\r
111 max_phandle = phandle;\r
112 }\r
113\r
114 return 0;\r
115}\r
116\r
1e57a462 117int fdt_get_mem_rsv(const void *fdt, int n, uint64_t *address, uint64_t *size)\r
118{\r
119 FDT_CHECK_HEADER(fdt);\r
120 *address = fdt64_to_cpu(_fdt_mem_rsv(fdt, n)->address);\r
121 *size = fdt64_to_cpu(_fdt_mem_rsv(fdt, n)->size);\r
122 return 0;\r
123}\r
124\r
125int fdt_num_mem_rsv(const void *fdt)\r
126{\r
127 int i = 0;\r
128\r
129 while (fdt64_to_cpu(_fdt_mem_rsv(fdt, i)->size) != 0)\r
130 i++;\r
131 return i;\r
132}\r
133\r
134static int _nextprop(const void *fdt, int offset)\r
135{\r
136 uint32_t tag;\r
137 int nextoffset;\r
138\r
139 do {\r
140 tag = fdt_next_tag(fdt, offset, &nextoffset);\r
141\r
142 switch (tag) {\r
143 case FDT_END:\r
144 if (nextoffset >= 0)\r
145 return -FDT_ERR_BADSTRUCTURE;\r
146 else\r
147 return nextoffset;\r
148\r
149 case FDT_PROP:\r
150 return offset;\r
151 }\r
152 offset = nextoffset;\r
153 } while (tag == FDT_NOP);\r
154\r
155 return -FDT_ERR_NOTFOUND;\r
156}\r
157\r
158int fdt_subnode_offset_namelen(const void *fdt, int offset,\r
159 const char *name, int namelen)\r
160{\r
161 int depth;\r
162\r
163 FDT_CHECK_HEADER(fdt);\r
164\r
165 for (depth = 0;\r
166 (offset >= 0) && (depth >= 0);\r
167 offset = fdt_next_node(fdt, offset, &depth))\r
168 if ((depth == 1)\r
169 && _fdt_nodename_eq(fdt, offset, name, namelen))\r
170 return offset;\r
171\r
172 if (depth < 0)\r
173 return -FDT_ERR_NOTFOUND;\r
174 return offset; /* error */\r
175}\r
176\r
177int fdt_subnode_offset(const void *fdt, int parentoffset,\r
178 const char *name)\r
179{\r
180 return fdt_subnode_offset_namelen(fdt, parentoffset, name, strlen(name));\r
181}\r
182\r
a0992390 183int fdt_path_offset_namelen(const void *fdt, const char *path, int namelen)\r
1e57a462 184{\r
a0992390 185 const char *end = path + namelen;\r
1e57a462 186 const char *p = path;\r
187 int offset = 0;\r
188\r
189 FDT_CHECK_HEADER(fdt);\r
190\r
191 /* see if we have an alias */\r
192 if (*path != '/') {\r
a0992390 193 const char *q = memchr(path, '/', end - p);\r
1e57a462 194\r
195 if (!q)\r
196 q = end;\r
197\r
198 p = fdt_get_alias_namelen(fdt, p, q - p);\r
199 if (!p)\r
200 return -FDT_ERR_BADPATH;\r
201 offset = fdt_path_offset(fdt, p);\r
202\r
203 p = q;\r
204 }\r
205\r
a0992390 206 while (p < end) {\r
1e57a462 207 const char *q;\r
208\r
a0992390 209 while (*p == '/') {\r
1e57a462 210 p++;\r
a0992390
PB
211 if (p == end)\r
212 return offset;\r
213 }\r
214 q = memchr(p, '/', end - p);\r
1e57a462 215 if (! q)\r
216 q = end;\r
217\r
218 offset = fdt_subnode_offset_namelen(fdt, offset, p, q-p);\r
219 if (offset < 0)\r
220 return offset;\r
221\r
222 p = q;\r
223 }\r
224\r
225 return offset;\r
226}\r
227\r
a0992390
PB
228int fdt_path_offset(const void *fdt, const char *path)\r
229{\r
230 return fdt_path_offset_namelen(fdt, path, strlen(path));\r
231}\r
232\r
1e57a462 233const char *fdt_get_name(const void *fdt, int nodeoffset, int *len)\r
234{\r
235 const struct fdt_node_header *nh = _fdt_offset_ptr(fdt, nodeoffset);\r
236 int err;\r
237\r
238 if (((err = fdt_check_header(fdt)) != 0)\r
239 || ((err = _fdt_check_node_offset(fdt, nodeoffset)) < 0))\r
240 goto fail;\r
241\r
242 if (len)\r
243 *len = strlen(nh->name);\r
244\r
245 return nh->name;\r
246\r
247 fail:\r
248 if (len)\r
249 *len = err;\r
250 return NULL;\r
251}\r
252\r
253int fdt_first_property_offset(const void *fdt, int nodeoffset)\r
254{\r
255 int offset;\r
256\r
257 if ((offset = _fdt_check_node_offset(fdt, nodeoffset)) < 0)\r
258 return offset;\r
259\r
260 return _nextprop(fdt, offset);\r
261}\r
262\r
263int fdt_next_property_offset(const void *fdt, int offset)\r
264{\r
265 if ((offset = _fdt_check_prop_offset(fdt, offset)) < 0)\r
266 return offset;\r
267\r
268 return _nextprop(fdt, offset);\r
269}\r
270\r
271const struct fdt_property *fdt_get_property_by_offset(const void *fdt,\r
272 int offset,\r
273 int *lenp)\r
274{\r
275 int err;\r
276 const struct fdt_property *prop;\r
277\r
278 if ((err = _fdt_check_prop_offset(fdt, offset)) < 0) {\r
279 if (lenp)\r
280 *lenp = err;\r
281 return NULL;\r
282 }\r
283\r
284 prop = _fdt_offset_ptr(fdt, offset);\r
285\r
286 if (lenp)\r
287 *lenp = fdt32_to_cpu(prop->len);\r
288\r
289 return prop;\r
290}\r
291\r
292const struct fdt_property *fdt_get_property_namelen(const void *fdt,\r
293 int offset,\r
294 const char *name,\r
295 int namelen, int *lenp)\r
296{\r
297 for (offset = fdt_first_property_offset(fdt, offset);\r
298 (offset >= 0);\r
299 (offset = fdt_next_property_offset(fdt, offset))) {\r
300 const struct fdt_property *prop;\r
301\r
a0992390 302 if (!(prop = fdt_get_property_by_offset(fdt, offset, lenp))) {\r
1e57a462 303 offset = -FDT_ERR_INTERNAL;\r
304 break;\r
305 }\r
306 if (_fdt_string_eq(fdt, fdt32_to_cpu(prop->nameoff),\r
307 name, namelen))\r
308 return prop;\r
309 }\r
310\r
311 if (lenp)\r
312 *lenp = offset;\r
313 return NULL;\r
314}\r
315\r
316const struct fdt_property *fdt_get_property(const void *fdt,\r
317 int nodeoffset,\r
318 const char *name, int *lenp)\r
319{\r
320 return fdt_get_property_namelen(fdt, nodeoffset, name,\r
321 strlen(name), lenp);\r
322}\r
323\r
324const void *fdt_getprop_namelen(const void *fdt, int nodeoffset,\r
325 const char *name, int namelen, int *lenp)\r
326{\r
327 const struct fdt_property *prop;\r
328\r
329 prop = fdt_get_property_namelen(fdt, nodeoffset, name, namelen, lenp);\r
a0992390 330 if (!prop)\r
1e57a462 331 return NULL;\r
332\r
333 return prop->data;\r
334}\r
335\r
336const void *fdt_getprop_by_offset(const void *fdt, int offset,\r
337 const char **namep, int *lenp)\r
338{\r
339 const struct fdt_property *prop;\r
340\r
341 prop = fdt_get_property_by_offset(fdt, offset, lenp);\r
342 if (!prop)\r
343 return NULL;\r
344 if (namep)\r
345 *namep = fdt_string(fdt, fdt32_to_cpu(prop->nameoff));\r
346 return prop->data;\r
347}\r
348\r
349const void *fdt_getprop(const void *fdt, int nodeoffset,\r
350 const char *name, int *lenp)\r
351{\r
352 return fdt_getprop_namelen(fdt, nodeoffset, name, strlen(name), lenp);\r
353}\r
354\r
355uint32_t fdt_get_phandle(const void *fdt, int nodeoffset)\r
356{\r
3e8576dd 357 const fdt32_t *php;\r
1e57a462 358 int len;\r
359\r
360 /* FIXME: This is a bit sub-optimal, since we potentially scan\r
361 * over all the properties twice. */\r
362 php = fdt_getprop(fdt, nodeoffset, "phandle", &len);\r
363 if (!php || (len != sizeof(*php))) {\r
364 php = fdt_getprop(fdt, nodeoffset, "linux,phandle", &len);\r
365 if (!php || (len != sizeof(*php)))\r
366 return 0;\r
367 }\r
368\r
369 return fdt32_to_cpu(*php);\r
370}\r
371\r
372const char *fdt_get_alias_namelen(const void *fdt,\r
373 const char *name, int namelen)\r
374{\r
375 int aliasoffset;\r
376\r
377 aliasoffset = fdt_path_offset(fdt, "/aliases");\r
378 if (aliasoffset < 0)\r
379 return NULL;\r
380\r
381 return fdt_getprop_namelen(fdt, aliasoffset, name, namelen, NULL);\r
382}\r
383\r
384const char *fdt_get_alias(const void *fdt, const char *name)\r
385{\r
386 return fdt_get_alias_namelen(fdt, name, strlen(name));\r
387}\r
388\r
389int fdt_get_path(const void *fdt, int nodeoffset, char *buf, int buflen)\r
390{\r
391 int pdepth = 0, p = 0;\r
392 int offset, depth, namelen;\r
393 const char *name;\r
394\r
395 FDT_CHECK_HEADER(fdt);\r
396\r
397 if (buflen < 2)\r
398 return -FDT_ERR_NOSPACE;\r
399\r
400 for (offset = 0, depth = 0;\r
401 (offset >= 0) && (offset <= nodeoffset);\r
402 offset = fdt_next_node(fdt, offset, &depth)) {\r
403 while (pdepth > depth) {\r
404 do {\r
405 p--;\r
406 } while (buf[p-1] != '/');\r
407 pdepth--;\r
408 }\r
409\r
410 if (pdepth >= depth) {\r
411 name = fdt_get_name(fdt, offset, &namelen);\r
412 if (!name)\r
413 return namelen;\r
414 if ((p + namelen + 1) <= buflen) {\r
415 memcpy(buf + p, name, namelen);\r
416 p += namelen;\r
417 buf[p++] = '/';\r
418 pdepth++;\r
419 }\r
420 }\r
421\r
422 if (offset == nodeoffset) {\r
423 if (pdepth < (depth + 1))\r
424 return -FDT_ERR_NOSPACE;\r
425\r
426 if (p > 1) /* special case so that root path is "/", not "" */\r
427 p--;\r
428 buf[p] = '\0';\r
429 return 0;\r
430 }\r
431 }\r
432\r
433 if ((offset == -FDT_ERR_NOTFOUND) || (offset >= 0))\r
434 return -FDT_ERR_BADOFFSET;\r
435 else if (offset == -FDT_ERR_BADOFFSET)\r
436 return -FDT_ERR_BADSTRUCTURE;\r
437\r
438 return offset; /* error from fdt_next_node() */\r
439}\r
440\r
441int fdt_supernode_atdepth_offset(const void *fdt, int nodeoffset,\r
442 int supernodedepth, int *nodedepth)\r
443{\r
444 int offset, depth;\r
445 int supernodeoffset = -FDT_ERR_INTERNAL;\r
446\r
447 FDT_CHECK_HEADER(fdt);\r
448\r
449 if (supernodedepth < 0)\r
450 return -FDT_ERR_NOTFOUND;\r
451\r
452 for (offset = 0, depth = 0;\r
453 (offset >= 0) && (offset <= nodeoffset);\r
454 offset = fdt_next_node(fdt, offset, &depth)) {\r
455 if (depth == supernodedepth)\r
456 supernodeoffset = offset;\r
457\r
458 if (offset == nodeoffset) {\r
459 if (nodedepth)\r
460 *nodedepth = depth;\r
461\r
462 if (supernodedepth > depth)\r
463 return -FDT_ERR_NOTFOUND;\r
464 else\r
465 return supernodeoffset;\r
466 }\r
467 }\r
468\r
469 if ((offset == -FDT_ERR_NOTFOUND) || (offset >= 0))\r
470 return -FDT_ERR_BADOFFSET;\r
471 else if (offset == -FDT_ERR_BADOFFSET)\r
472 return -FDT_ERR_BADSTRUCTURE;\r
473\r
474 return offset; /* error from fdt_next_node() */\r
475}\r
476\r
477int fdt_node_depth(const void *fdt, int nodeoffset)\r
478{\r
479 int nodedepth;\r
480 int err;\r
481\r
482 err = fdt_supernode_atdepth_offset(fdt, nodeoffset, 0, &nodedepth);\r
483 if (err)\r
484 return (err < 0) ? err : -FDT_ERR_INTERNAL;\r
485 return nodedepth;\r
486}\r
487\r
488int fdt_parent_offset(const void *fdt, int nodeoffset)\r
489{\r
490 int nodedepth = fdt_node_depth(fdt, nodeoffset);\r
491\r
492 if (nodedepth < 0)\r
493 return nodedepth;\r
494 return fdt_supernode_atdepth_offset(fdt, nodeoffset,\r
495 nodedepth - 1, NULL);\r
496}\r
497\r
498int fdt_node_offset_by_prop_value(const void *fdt, int startoffset,\r
499 const char *propname,\r
500 const void *propval, int proplen)\r
501{\r
502 int offset;\r
503 const void *val;\r
504 int len;\r
505\r
506 FDT_CHECK_HEADER(fdt);\r
507\r
508 /* FIXME: The algorithm here is pretty horrible: we scan each\r
509 * property of a node in fdt_getprop(), then if that didn't\r
510 * find what we want, we scan over them again making our way\r
511 * to the next node. Still it's the easiest to implement\r
512 * approach; performance can come later. */\r
513 for (offset = fdt_next_node(fdt, startoffset, NULL);\r
514 offset >= 0;\r
515 offset = fdt_next_node(fdt, offset, NULL)) {\r
516 val = fdt_getprop(fdt, offset, propname, &len);\r
517 if (val && (len == proplen)\r
518 && (memcmp(val, propval, len) == 0))\r
519 return offset;\r
520 }\r
521\r
522 return offset; /* error from fdt_next_node() */\r
523}\r
524\r
525int fdt_node_offset_by_phandle(const void *fdt, uint32_t phandle)\r
526{\r
527 int offset;\r
528\r
a0992390 529 if ((phandle == 0) || (phandle == -1))\r
1e57a462 530 return -FDT_ERR_BADPHANDLE;\r
531\r
532 FDT_CHECK_HEADER(fdt);\r
533\r
534 /* FIXME: The algorithm here is pretty horrible: we\r
535 * potentially scan each property of a node in\r
536 * fdt_get_phandle(), then if that didn't find what\r
537 * we want, we scan over them again making our way to the next\r
538 * node. Still it's the easiest to implement approach;\r
539 * performance can come later. */\r
540 for (offset = fdt_next_node(fdt, -1, NULL);\r
541 offset >= 0;\r
542 offset = fdt_next_node(fdt, offset, NULL)) {\r
543 if (fdt_get_phandle(fdt, offset) == phandle)\r
544 return offset;\r
545 }\r
546\r
547 return offset; /* error from fdt_next_node() */\r
548}\r
549\r
3e8576dd 550int fdt_stringlist_contains(const char *strlist, int listlen, const char *str)\r
1e57a462 551{\r
552 int len = strlen(str);\r
553 const char *p;\r
554\r
555 while (listlen >= len) {\r
556 if (memcmp(str, strlist, len+1) == 0)\r
557 return 1;\r
558 p = memchr(strlist, '\0', listlen);\r
559 if (!p)\r
560 return 0; /* malformed strlist.. */\r
561 listlen -= (p-strlist) + 1;\r
562 strlist = p + 1;\r
563 }\r
564 return 0;\r
565}\r
566\r
a0992390
PB
567int fdt_stringlist_count(const void *fdt, int nodeoffset, const char *property)\r
568{\r
569 const char *list, *end;\r
570 int length, count = 0;\r
571\r
572 list = fdt_getprop(fdt, nodeoffset, property, &length);\r
573 if (!list)\r
574 return length;\r
575\r
576 end = list + length;\r
577\r
578 while (list < end) {\r
579 length = strnlen(list, end - list) + 1;\r
580\r
581 /* Abort if the last string isn't properly NUL-terminated. */\r
582 if (list + length > end)\r
583 return -FDT_ERR_BADVALUE;\r
584\r
585 list += length;\r
586 count++;\r
587 }\r
588\r
589 return count;\r
590}\r
591\r
592int fdt_stringlist_search(const void *fdt, int nodeoffset, const char *property,\r
593 const char *string)\r
594{\r
595 int length, len, idx = 0;\r
596 const char *list, *end;\r
597\r
598 list = fdt_getprop(fdt, nodeoffset, property, &length);\r
599 if (!list)\r
600 return length;\r
601\r
602 len = strlen(string) + 1;\r
603 end = list + length;\r
604\r
605 while (list < end) {\r
606 length = strnlen(list, end - list) + 1;\r
607\r
608 /* Abort if the last string isn't properly NUL-terminated. */\r
609 if (list + length > end)\r
610 return -FDT_ERR_BADVALUE;\r
611\r
612 if (length == len && memcmp(list, string, length) == 0)\r
613 return idx;\r
614\r
615 list += length;\r
616 idx++;\r
617 }\r
618\r
619 return -FDT_ERR_NOTFOUND;\r
620}\r
621\r
622const char *fdt_stringlist_get(const void *fdt, int nodeoffset,\r
623 const char *property, int idx,\r
624 int *lenp)\r
625{\r
626 const char *list, *end;\r
627 int length;\r
628\r
629 list = fdt_getprop(fdt, nodeoffset, property, &length);\r
630 if (!list) {\r
631 if (lenp)\r
632 *lenp = length;\r
633\r
634 return NULL;\r
635 }\r
636\r
637 end = list + length;\r
638\r
639 while (list < end) {\r
640 length = strnlen(list, end - list) + 1;\r
641\r
642 /* Abort if the last string isn't properly NUL-terminated. */\r
643 if (list + length > end) {\r
644 if (lenp)\r
645 *lenp = -FDT_ERR_BADVALUE;\r
646\r
647 return NULL;\r
648 }\r
649\r
650 if (idx == 0) {\r
651 if (lenp)\r
652 *lenp = length - 1;\r
653\r
654 return list;\r
655 }\r
656\r
657 list += length;\r
658 idx--;\r
659 }\r
660\r
661 if (lenp)\r
662 *lenp = -FDT_ERR_NOTFOUND;\r
663\r
664 return NULL;\r
665}\r
666\r
1e57a462 667int fdt_node_check_compatible(const void *fdt, int nodeoffset,\r
668 const char *compatible)\r
669{\r
670 const void *prop;\r
671 int len;\r
672\r
673 prop = fdt_getprop(fdt, nodeoffset, "compatible", &len);\r
674 if (!prop)\r
675 return len;\r
a0992390
PB
676\r
677 return !fdt_stringlist_contains(prop, len, compatible);\r
1e57a462 678}\r
679\r
680int fdt_node_offset_by_compatible(const void *fdt, int startoffset,\r
681 const char *compatible)\r
682{\r
683 int offset, err;\r
684\r
685 FDT_CHECK_HEADER(fdt);\r
686\r
687 /* FIXME: The algorithm here is pretty horrible: we scan each\r
688 * property of a node in fdt_node_check_compatible(), then if\r
689 * that didn't find what we want, we scan over them again\r
690 * making our way to the next node. Still it's the easiest to\r
691 * implement approach; performance can come later. */\r
692 for (offset = fdt_next_node(fdt, startoffset, NULL);\r
693 offset >= 0;\r
694 offset = fdt_next_node(fdt, offset, NULL)) {\r
695 err = fdt_node_check_compatible(fdt, offset, compatible);\r
696 if ((err < 0) && (err != -FDT_ERR_NOTFOUND))\r
697 return err;\r
698 else if (err == 0)\r
699 return offset;\r
700 }\r
701\r
702 return offset; /* error from fdt_next_node() */\r
703}\r