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