diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2012-10-03 12:44:08 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2012-10-03 12:44:08 -0400 |
commit | a54dfb1a845c38a97686268d8c4086a63d9493aa (patch) | |
tree | 3b31c81672fa89102aae929cc6c1e48e6e9382f4 /scripts/dtc/fdtget.c | |
parent | eb0ad9c06d51edb5d18a7007fd4d77a8805b2ba7 (diff) | |
parent | 36165f55055781a0e4bf32d775241796414504b0 (diff) |
Merge tag 'dt-for-3.7' of git://sources.calxeda.com/kernel/linux
Pull devicetree updates from Rob Herring:
- Import of latest upstream device tree compiler (dtc)
- New function of_get_child_by_name
- Support for #size-cells of 0 and #addr-cells of >2
- Couple of DT binding documentation updates
Fix up trivial conflicts due to of_get_child_by_name() having been added
next to the new of_get_next_available_child().
* tag 'dt-for-3.7' of git://sources.calxeda.com/kernel/linux:
MAINTAINERS: add scripts/dtc under Devicetree maintainers
dtc: import latest upstream dtc
dt: Document general interrupt controller bindings
dt/s3c64xx/spi: Use of_get_child_by_name to get a named child
dt: introduce of_get_child_by_name to get child node by name
of: i2c: add support for wakeup-source property
of/address: Handle #address-cells > 2 specially
DT: export of_irq_to_resource_table()
devicetree: serial: Add documentation for imx serial
devicetree: pwm: mxs-pwm.txt: Fix reg field annotation
of: Allow busses with #size-cells=0
Diffstat (limited to 'scripts/dtc/fdtget.c')
-rw-r--r-- | scripts/dtc/fdtget.c | 366 |
1 files changed, 366 insertions, 0 deletions
diff --git a/scripts/dtc/fdtget.c b/scripts/dtc/fdtget.c new file mode 100644 index 000000000000..c2fbab2a5476 --- /dev/null +++ b/scripts/dtc/fdtget.c | |||
@@ -0,0 +1,366 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2011 The Chromium OS Authors. All rights reserved. | ||
3 | * | ||
4 | * Portions from U-Boot cmd_fdt.c (C) Copyright 2007 | ||
5 | * Gerald Van Baren, Custom IDEAS, vanbaren@cideas.com | ||
6 | * Based on code written by: | ||
7 | * Pantelis Antoniou <pantelis.antoniou@gmail.com> and | ||
8 | * Matthew McClintock <msm@freescale.com> | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or | ||
11 | * modify it under the terms of the GNU General Public License as | ||
12 | * published by the Free Software Foundation; either version 2 of | ||
13 | * the License, or (at your option) any later version. | ||
14 | * | ||
15 | * This program is distributed in the hope that it will be useful, | ||
16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
18 | * GNU General Public License for more details. | ||
19 | * | ||
20 | * You should have received a copy of the GNU General Public License | ||
21 | * along with this program; if not, write to the Free Software | ||
22 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, | ||
23 | * MA 02111-1307 USA | ||
24 | */ | ||
25 | |||
26 | #include <assert.h> | ||
27 | #include <ctype.h> | ||
28 | #include <getopt.h> | ||
29 | #include <stdio.h> | ||
30 | #include <stdlib.h> | ||
31 | #include <string.h> | ||
32 | |||
33 | #include <libfdt.h> | ||
34 | |||
35 | #include "util.h" | ||
36 | |||
37 | enum display_mode { | ||
38 | MODE_SHOW_VALUE, /* show values for node properties */ | ||
39 | MODE_LIST_PROPS, /* list the properties for a node */ | ||
40 | MODE_LIST_SUBNODES, /* list the subnodes of a node */ | ||
41 | }; | ||
42 | |||
43 | /* Holds information which controls our output and options */ | ||
44 | struct display_info { | ||
45 | int type; /* data type (s/i/u/x or 0 for default) */ | ||
46 | int size; /* data size (1/2/4) */ | ||
47 | enum display_mode mode; /* display mode that we are using */ | ||
48 | const char *default_val; /* default value if node/property not found */ | ||
49 | }; | ||
50 | |||
51 | static void report_error(const char *where, int err) | ||
52 | { | ||
53 | fprintf(stderr, "Error at '%s': %s\n", where, fdt_strerror(err)); | ||
54 | } | ||
55 | |||
56 | /** | ||
57 | * Displays data of a given length according to selected options | ||
58 | * | ||
59 | * If a specific data type is provided in disp, then this is used. Otherwise | ||
60 | * we try to guess the data type / size from the contents. | ||
61 | * | ||
62 | * @param disp Display information / options | ||
63 | * @param data Data to display | ||
64 | * @param len Maximum length of buffer | ||
65 | * @return 0 if ok, -1 if data does not match format | ||
66 | */ | ||
67 | static int show_data(struct display_info *disp, const char *data, int len) | ||
68 | { | ||
69 | int i, size; | ||
70 | const uint8_t *p = (const uint8_t *)data; | ||
71 | const char *s; | ||
72 | int value; | ||
73 | int is_string; | ||
74 | char fmt[3]; | ||
75 | |||
76 | /* no data, don't print */ | ||
77 | if (len == 0) | ||
78 | return 0; | ||
79 | |||
80 | is_string = (disp->type) == 's' || | ||
81 | (!disp->type && util_is_printable_string(data, len)); | ||
82 | if (is_string) { | ||
83 | if (data[len - 1] != '\0') { | ||
84 | fprintf(stderr, "Unterminated string\n"); | ||
85 | return -1; | ||
86 | } | ||
87 | for (s = data; s - data < len; s += strlen(s) + 1) { | ||
88 | if (s != data) | ||
89 | printf(" "); | ||
90 | printf("%s", (const char *)s); | ||
91 | } | ||
92 | return 0; | ||
93 | } | ||
94 | size = disp->size; | ||
95 | if (size == -1) { | ||
96 | size = (len % 4) == 0 ? 4 : 1; | ||
97 | } else if (len % size) { | ||
98 | fprintf(stderr, "Property length must be a multiple of " | ||
99 | "selected data size\n"); | ||
100 | return -1; | ||
101 | } | ||
102 | fmt[0] = '%'; | ||
103 | fmt[1] = disp->type ? disp->type : 'd'; | ||
104 | fmt[2] = '\0'; | ||
105 | for (i = 0; i < len; i += size, p += size) { | ||
106 | if (i) | ||
107 | printf(" "); | ||
108 | value = size == 4 ? fdt32_to_cpu(*(const uint32_t *)p) : | ||
109 | size == 2 ? (*p << 8) | p[1] : *p; | ||
110 | printf(fmt, value); | ||
111 | } | ||
112 | return 0; | ||
113 | } | ||
114 | |||
115 | /** | ||
116 | * List all properties in a node, one per line. | ||
117 | * | ||
118 | * @param blob FDT blob | ||
119 | * @param node Node to display | ||
120 | * @return 0 if ok, or FDT_ERR... if not. | ||
121 | */ | ||
122 | static int list_properties(const void *blob, int node) | ||
123 | { | ||
124 | const struct fdt_property *data; | ||
125 | const char *name; | ||
126 | int prop; | ||
127 | |||
128 | prop = fdt_first_property_offset(blob, node); | ||
129 | do { | ||
130 | /* Stop silently when there are no more properties */ | ||
131 | if (prop < 0) | ||
132 | return prop == -FDT_ERR_NOTFOUND ? 0 : prop; | ||
133 | data = fdt_get_property_by_offset(blob, prop, NULL); | ||
134 | name = fdt_string(blob, fdt32_to_cpu(data->nameoff)); | ||
135 | if (name) | ||
136 | puts(name); | ||
137 | prop = fdt_next_property_offset(blob, prop); | ||
138 | } while (1); | ||
139 | } | ||
140 | |||
141 | #define MAX_LEVEL 32 /* how deeply nested we will go */ | ||
142 | |||
143 | /** | ||
144 | * List all subnodes in a node, one per line | ||
145 | * | ||
146 | * @param blob FDT blob | ||
147 | * @param node Node to display | ||
148 | * @return 0 if ok, or FDT_ERR... if not. | ||
149 | */ | ||
150 | static int list_subnodes(const void *blob, int node) | ||
151 | { | ||
152 | int nextoffset; /* next node offset from libfdt */ | ||
153 | uint32_t tag; /* current tag */ | ||
154 | int level = 0; /* keep track of nesting level */ | ||
155 | const char *pathp; | ||
156 | int depth = 1; /* the assumed depth of this node */ | ||
157 | |||
158 | while (level >= 0) { | ||
159 | tag = fdt_next_tag(blob, node, &nextoffset); | ||
160 | switch (tag) { | ||
161 | case FDT_BEGIN_NODE: | ||
162 | pathp = fdt_get_name(blob, node, NULL); | ||
163 | if (level <= depth) { | ||
164 | if (pathp == NULL) | ||
165 | pathp = "/* NULL pointer error */"; | ||
166 | if (*pathp == '\0') | ||
167 | pathp = "/"; /* root is nameless */ | ||
168 | if (level == 1) | ||
169 | puts(pathp); | ||
170 | } | ||
171 | level++; | ||
172 | if (level >= MAX_LEVEL) { | ||
173 | printf("Nested too deep, aborting.\n"); | ||
174 | return 1; | ||
175 | } | ||
176 | break; | ||
177 | case FDT_END_NODE: | ||
178 | level--; | ||
179 | if (level == 0) | ||
180 | level = -1; /* exit the loop */ | ||
181 | break; | ||
182 | case FDT_END: | ||
183 | return 1; | ||
184 | case FDT_PROP: | ||
185 | break; | ||
186 | default: | ||
187 | if (level <= depth) | ||
188 | printf("Unknown tag 0x%08X\n", tag); | ||
189 | return 1; | ||
190 | } | ||
191 | node = nextoffset; | ||
192 | } | ||
193 | return 0; | ||
194 | } | ||
195 | |||
196 | /** | ||
197 | * Show the data for a given node (and perhaps property) according to the | ||
198 | * display option provided. | ||
199 | * | ||
200 | * @param blob FDT blob | ||
201 | * @param disp Display information / options | ||
202 | * @param node Node to display | ||
203 | * @param property Name of property to display, or NULL if none | ||
204 | * @return 0 if ok, -ve on error | ||
205 | */ | ||
206 | static int show_data_for_item(const void *blob, struct display_info *disp, | ||
207 | int node, const char *property) | ||
208 | { | ||
209 | const void *value = NULL; | ||
210 | int len, err = 0; | ||
211 | |||
212 | switch (disp->mode) { | ||
213 | case MODE_LIST_PROPS: | ||
214 | err = list_properties(blob, node); | ||
215 | break; | ||
216 | |||
217 | case MODE_LIST_SUBNODES: | ||
218 | err = list_subnodes(blob, node); | ||
219 | break; | ||
220 | |||
221 | default: | ||
222 | assert(property); | ||
223 | value = fdt_getprop(blob, node, property, &len); | ||
224 | if (value) { | ||
225 | if (show_data(disp, value, len)) | ||
226 | err = -1; | ||
227 | else | ||
228 | printf("\n"); | ||
229 | } else if (disp->default_val) { | ||
230 | puts(disp->default_val); | ||
231 | } else { | ||
232 | report_error(property, len); | ||
233 | err = -1; | ||
234 | } | ||
235 | break; | ||
236 | } | ||
237 | |||
238 | return err; | ||
239 | } | ||
240 | |||
241 | /** | ||
242 | * Run the main fdtget operation, given a filename and valid arguments | ||
243 | * | ||
244 | * @param disp Display information / options | ||
245 | * @param filename Filename of blob file | ||
246 | * @param arg List of arguments to process | ||
247 | * @param arg_count Number of arguments | ||
248 | * @param return 0 if ok, -ve on error | ||
249 | */ | ||
250 | static int do_fdtget(struct display_info *disp, const char *filename, | ||
251 | char **arg, int arg_count, int args_per_step) | ||
252 | { | ||
253 | char *blob; | ||
254 | const char *prop; | ||
255 | int i, node; | ||
256 | |||
257 | blob = utilfdt_read(filename); | ||
258 | if (!blob) | ||
259 | return -1; | ||
260 | |||
261 | for (i = 0; i + args_per_step <= arg_count; i += args_per_step) { | ||
262 | node = fdt_path_offset(blob, arg[i]); | ||
263 | if (node < 0) { | ||
264 | if (disp->default_val) { | ||
265 | puts(disp->default_val); | ||
266 | continue; | ||
267 | } else { | ||
268 | report_error(arg[i], node); | ||
269 | return -1; | ||
270 | } | ||
271 | } | ||
272 | prop = args_per_step == 1 ? NULL : arg[i + 1]; | ||
273 | |||
274 | if (show_data_for_item(blob, disp, node, prop)) | ||
275 | return -1; | ||
276 | } | ||
277 | return 0; | ||
278 | } | ||
279 | |||
280 | static const char *usage_msg = | ||
281 | "fdtget - read values from device tree\n" | ||
282 | "\n" | ||
283 | "Each value is printed on a new line.\n\n" | ||
284 | "Usage:\n" | ||
285 | " fdtget <options> <dt file> [<node> <property>]...\n" | ||
286 | " fdtget -p <options> <dt file> [<node> ]...\n" | ||
287 | "Options:\n" | ||
288 | "\t-t <type>\tType of data\n" | ||
289 | "\t-p\t\tList properties for each node\n" | ||
290 | "\t-l\t\tList subnodes for each node\n" | ||
291 | "\t-d\t\tDefault value to display when the property is " | ||
292 | "missing\n" | ||
293 | "\t-h\t\tPrint this help\n\n" | ||
294 | USAGE_TYPE_MSG; | ||
295 | |||
296 | static void usage(const char *msg) | ||
297 | { | ||
298 | if (msg) | ||
299 | fprintf(stderr, "Error: %s\n\n", msg); | ||
300 | |||
301 | fprintf(stderr, "%s", usage_msg); | ||
302 | exit(2); | ||
303 | } | ||
304 | |||
305 | int main(int argc, char *argv[]) | ||
306 | { | ||
307 | char *filename = NULL; | ||
308 | struct display_info disp; | ||
309 | int args_per_step = 2; | ||
310 | |||
311 | /* set defaults */ | ||
312 | memset(&disp, '\0', sizeof(disp)); | ||
313 | disp.size = -1; | ||
314 | disp.mode = MODE_SHOW_VALUE; | ||
315 | for (;;) { | ||
316 | int c = getopt(argc, argv, "d:hlpt:"); | ||
317 | if (c == -1) | ||
318 | break; | ||
319 | |||
320 | switch (c) { | ||
321 | case 'h': | ||
322 | case '?': | ||
323 | usage(NULL); | ||
324 | |||
325 | case 't': | ||
326 | if (utilfdt_decode_type(optarg, &disp.type, | ||
327 | &disp.size)) | ||
328 | usage("Invalid type string"); | ||
329 | break; | ||
330 | |||
331 | case 'p': | ||
332 | disp.mode = MODE_LIST_PROPS; | ||
333 | args_per_step = 1; | ||
334 | break; | ||
335 | |||
336 | case 'l': | ||
337 | disp.mode = MODE_LIST_SUBNODES; | ||
338 | args_per_step = 1; | ||
339 | break; | ||
340 | |||
341 | case 'd': | ||
342 | disp.default_val = optarg; | ||
343 | break; | ||
344 | } | ||
345 | } | ||
346 | |||
347 | if (optind < argc) | ||
348 | filename = argv[optind++]; | ||
349 | if (!filename) | ||
350 | usage("Missing filename"); | ||
351 | |||
352 | argv += optind; | ||
353 | argc -= optind; | ||
354 | |||
355 | /* Allow no arguments, and silently succeed */ | ||
356 | if (!argc) | ||
357 | return 0; | ||
358 | |||
359 | /* Check for node, property arguments */ | ||
360 | if (args_per_step == 2 && (argc % 2)) | ||
361 | usage("Must have an even number of arguments"); | ||
362 | |||
363 | if (do_fdtget(&disp, filename, argv, argc, args_per_step)) | ||
364 | return 1; | ||
365 | return 0; | ||
366 | } | ||