blob: b58029a39118d201d84727fa995fb46d4ce33354 [file] [log] [blame]
David Gibson3da0f9a2006-11-27 16:21:28 +11001/*
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
David Gibson3da0f9a2006-11-27 16:21:28 +110026#define OFFSET_CHECK_HEADER(fdt) \
27 { \
28 int err; \
David Gibson42369762006-12-01 15:07:19 +110029 if ((err = _fdt_check_header(fdt)) != 0) \
David Gibson3da0f9a2006-11-27 16:21:28 +110030 return OFFSET_ERROR(err); \
31 }
32
David Gibson94993f42006-12-11 16:15:34 +110033#define PTR_CHECK_HEADER(fdt) \
34 { \
35 int err; \
36 if ((err = _fdt_check_header(fdt)) != 0) \
37 return PTR_ERROR(err); \
38 }
39
David Gibson73d60922006-12-15 15:12:47 +110040static int offset_streq(const void *fdt, int offset,
David Gibson3da0f9a2006-11-27 16:21:28 +110041 const char *s, int len)
42{
43 const char *p = fdt_offset_ptr(fdt, offset, len+1);
44
45 if (! p)
46 /* short match */
47 return 0;
48
49 if (memcmp(p, s, len) != 0)
50 return 0;
51
52 if (p[len] != '\0')
53 return 0;
54
55 return 1;
56}
57
David Gibson73d60922006-12-15 15:12:47 +110058char *fdt_string(const void *fdt, int stroffset)
David Gibson3aa4cfd2006-11-29 16:34:30 +110059{
David Gibsonede25de2006-12-01 15:02:10 +110060 return (char *)fdt + fdt_off_dt_strings(fdt) + stroffset;
David Gibson3aa4cfd2006-11-29 16:34:30 +110061}
62
David Gibson73d60922006-12-15 15:12:47 +110063int fdt_subnode_offset_namelen(const void *fdt, int parentoffset,
David Gibson3da0f9a2006-11-27 16:21:28 +110064 const char *name, int namelen)
65{
66 int level = 0;
67 uint32_t tag;
68 int offset, nextoffset;
69
70 OFFSET_CHECK_HEADER(fdt);
71
72 tag = _fdt_next_tag(fdt, parentoffset, &nextoffset);
73 if (tag != FDT_BEGIN_NODE)
74 return OFFSET_ERROR(FDT_ERR_BADOFFSET);
75
76 do {
77 offset = nextoffset;
78 tag = _fdt_next_tag(fdt, offset, &nextoffset);
79
80 switch (tag) {
81 case FDT_END:
82 return OFFSET_ERROR(FDT_ERR_TRUNCATED);
83
84 case FDT_BEGIN_NODE:
85 level++;
86 if (level != 1)
87 continue;
88 if (offset_streq(fdt, offset+FDT_TAGSIZE, name, namelen))
89 /* Found it! */
90 return offset;
91 break;
92
93 case FDT_END_NODE:
94 level--;
95 break;
96
97 case FDT_PROP:
98 case FDT_NOP:
99 break;
100
101 default:
102 return OFFSET_ERROR(FDT_ERR_BADSTRUCTURE);
103 }
104 } while (level >= 0);
105
106 return OFFSET_ERROR(FDT_ERR_NOTFOUND);
107}
108
David Gibson73d60922006-12-15 15:12:47 +1100109int fdt_subnode_offset(const void *fdt, int parentoffset,
David Gibson3da0f9a2006-11-27 16:21:28 +1100110 const char *name)
111{
112 return fdt_subnode_offset_namelen(fdt, parentoffset, name, strlen(name));
113}
114
David Gibson73d60922006-12-15 15:12:47 +1100115int fdt_path_offset(const void *fdt, const char *path)
David Gibson3da0f9a2006-11-27 16:21:28 +1100116{
117 const char *end = path + strlen(path);
118 const char *p = path;
119 int offset = 0;
120
121 OFFSET_CHECK_HEADER(fdt);
122
123 if (*path != '/')
124 return OFFSET_ERROR(FDT_ERR_BADPATH);
125
126 while (*p) {
127 const char *q;
128
129 while (*p == '/')
130 p++;
131 if (! *p)
132 return OFFSET_ERROR(FDT_ERR_BADPATH);
133 q = strchr(p, '/');
134 if (! q)
135 q = end;
136
137 offset = fdt_subnode_offset_namelen(fdt, offset, p, q-p);
138 if (fdt_offset_error(offset))
139 return offset;
140
141 p = q;
142 }
143
144 return offset;
145}
146
David Gibson73d60922006-12-15 15:12:47 +1100147struct fdt_property *fdt_get_property(const void *fdt,
David Gibson94993f42006-12-11 16:15:34 +1100148 int nodeoffset,
149 const char *name, int *lenp)
David Gibson3da0f9a2006-11-27 16:21:28 +1100150{
David Gibson94993f42006-12-11 16:15:34 +1100151 int level = 0;
152 uint32_t tag;
David Gibson3da0f9a2006-11-27 16:21:28 +1100153 struct fdt_property *prop;
David Gibson94993f42006-12-11 16:15:34 +1100154 int namestroff;
155 int offset, nextoffset;
David Gibson3da0f9a2006-11-27 16:21:28 +1100156
David Gibson94993f42006-12-11 16:15:34 +1100157 PTR_CHECK_HEADER(fdt);
David Gibson3da0f9a2006-11-27 16:21:28 +1100158
David Gibson94993f42006-12-11 16:15:34 +1100159 if (nodeoffset % FDT_TAGSIZE)
160 return PTR_ERROR(FDT_ERR_BADOFFSET);
David Gibson3da0f9a2006-11-27 16:21:28 +1100161
David Gibson94993f42006-12-11 16:15:34 +1100162 tag = _fdt_next_tag(fdt, nodeoffset, &nextoffset);
163 if (tag != FDT_BEGIN_NODE)
164 return PTR_ERROR(FDT_ERR_BADOFFSET);
David Gibson3da0f9a2006-11-27 16:21:28 +1100165
David Gibson94993f42006-12-11 16:15:34 +1100166 do {
167 offset = nextoffset;
168 if (offset % FDT_TAGSIZE)
169 return PTR_ERROR(FDT_ERR_INTERNAL);
170
171 tag = _fdt_next_tag(fdt, offset, &nextoffset);
172 switch (tag) {
173 case FDT_END:
174 return PTR_ERROR(FDT_ERR_TRUNCATED);
175
176 case FDT_BEGIN_NODE:
177 level++;
178 break;
179
180 case FDT_END_NODE:
181 level--;
182 break;
183
184 case FDT_PROP:
185 if (level != 0)
186 continue;
187
188 prop = fdt_offset_ptr_typed(fdt, offset, prop);
189 if (! prop)
190 return PTR_ERROR(FDT_ERR_BADSTRUCTURE);
191 namestroff = fdt32_to_cpu(prop->nameoff);
192 if (streq(fdt_string(fdt, namestroff), name)) {
193 /* Found it! */
194 int len = fdt32_to_cpu(prop->len);
195 prop = fdt_offset_ptr(fdt, offset,
David Gibson9825f822006-12-14 15:29:25 +1100196 sizeof(*prop)+len);
David Gibson94993f42006-12-11 16:15:34 +1100197 if (! prop)
198 return PTR_ERROR(FDT_ERR_BADSTRUCTURE);
199
200 if (lenp)
201 *lenp = len;
202
203 return prop;
204 }
205 break;
206
207 case FDT_NOP:
208 break;
209
210 default:
211 return PTR_ERROR(FDT_ERR_BADSTRUCTURE);
212 }
213 } while (level >= 0);
214
215 return PTR_ERROR(FDT_ERR_NOTFOUND);
David Gibson3da0f9a2006-11-27 16:21:28 +1100216}
217
David Gibson73d60922006-12-15 15:12:47 +1100218void *fdt_getprop(const void *fdt, int nodeoffset,
David Gibson3da0f9a2006-11-27 16:21:28 +1100219 const char *name, int *lenp)
220{
221 const struct fdt_property *prop;
222 int err;
223
David Gibson94993f42006-12-11 16:15:34 +1100224 prop = fdt_get_property(fdt, nodeoffset, name, lenp);
David Gibson3da0f9a2006-11-27 16:21:28 +1100225 if ((err = fdt_ptr_error(prop)))
226 return PTR_ERROR(err);
227
228 return prop->data;
229}