diff options
author | Stephen Warren <swarren@nvidia.com> | 2012-09-28 17:25:59 -0400 |
---|---|---|
committer | Rob Herring <rob.herring@calxeda.com> | 2012-10-01 12:11:35 -0400 |
commit | cd296721a9645f9f28800a072490fa15458d1fb7 (patch) | |
tree | 492b9a268a48af07844fbbd39519f95676ee73fe /scripts/dtc/fdtput.c | |
parent | acc2097934b5403b97f95763fe99fc115b818061 (diff) |
dtc: import latest upstream dtc
This updates scripts/dtc to commit 317a5d9 "dtc: zero out new label
objects" from git://git.jdl.com/software/dtc.git.
This adds features such as:
* /bits/ syntax for cell data.
* Math expressions within cell data.
* The ability to delete properties or nodes.
* Support for #line directives in the input file, which allows the use of
cpp on *.dts.
* -i command-line option (/include/ path)
* -W/-E command-line options for error/warning control.
* Removal of spew to STDOUT containing the filename being compiled.
* Many additions to the libfdt API.
Signed-off-by: Stephen Warren <swarren@nvidia.com>
Acked-by: Jon Loeliger <jdl@jdl.com>
Signed-off-by: Rob Herring <rob.herring@calxeda.com>
Diffstat (limited to 'scripts/dtc/fdtput.c')
-rw-r--r-- | scripts/dtc/fdtput.c | 362 |
1 files changed, 362 insertions, 0 deletions
diff --git a/scripts/dtc/fdtput.c b/scripts/dtc/fdtput.c new file mode 100644 index 000000000000..f2197f51930b --- /dev/null +++ b/scripts/dtc/fdtput.c | |||
@@ -0,0 +1,362 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2011 The Chromium OS Authors. All rights reserved. | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or | ||
5 | * modify it under the terms of the GNU General Public License as | ||
6 | * published by the Free Software Foundation; either version 2 of | ||
7 | * the License, or (at your option) any later version. | ||
8 | * | ||
9 | * This program is distributed in the hope that it will be useful, | ||
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
12 | * GNU General Public License for more details. | ||
13 | * | ||
14 | * You should have received a copy of the GNU General Public License | ||
15 | * along with this program; if not, write to the Free Software | ||
16 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, | ||
17 | * MA 02111-1307 USA | ||
18 | */ | ||
19 | |||
20 | #include <assert.h> | ||
21 | #include <ctype.h> | ||
22 | #include <getopt.h> | ||
23 | #include <stdio.h> | ||
24 | #include <stdlib.h> | ||
25 | #include <string.h> | ||
26 | |||
27 | #include <libfdt.h> | ||
28 | |||
29 | #include "util.h" | ||
30 | |||
31 | /* These are the operations we support */ | ||
32 | enum oper_type { | ||
33 | OPER_WRITE_PROP, /* Write a property in a node */ | ||
34 | OPER_CREATE_NODE, /* Create a new node */ | ||
35 | }; | ||
36 | |||
37 | struct display_info { | ||
38 | enum oper_type oper; /* operation to perform */ | ||
39 | int type; /* data type (s/i/u/x or 0 for default) */ | ||
40 | int size; /* data size (1/2/4) */ | ||
41 | int verbose; /* verbose output */ | ||
42 | int auto_path; /* automatically create all path components */ | ||
43 | }; | ||
44 | |||
45 | |||
46 | /** | ||
47 | * Report an error with a particular node. | ||
48 | * | ||
49 | * @param name Node name to report error on | ||
50 | * @param namelen Length of node name, or -1 to use entire string | ||
51 | * @param err Error number to report (-FDT_ERR_...) | ||
52 | */ | ||
53 | static void report_error(const char *name, int namelen, int err) | ||
54 | { | ||
55 | if (namelen == -1) | ||
56 | namelen = strlen(name); | ||
57 | fprintf(stderr, "Error at '%1.*s': %s\n", namelen, name, | ||
58 | fdt_strerror(err)); | ||
59 | } | ||
60 | |||
61 | /** | ||
62 | * Encode a series of arguments in a property value. | ||
63 | * | ||
64 | * @param disp Display information / options | ||
65 | * @param arg List of arguments from command line | ||
66 | * @param arg_count Number of arguments (may be 0) | ||
67 | * @param valuep Returns buffer containing value | ||
68 | * @param *value_len Returns length of value encoded | ||
69 | */ | ||
70 | static int encode_value(struct display_info *disp, char **arg, int arg_count, | ||
71 | char **valuep, int *value_len) | ||
72 | { | ||
73 | char *value = NULL; /* holding area for value */ | ||
74 | int value_size = 0; /* size of holding area */ | ||
75 | char *ptr; /* pointer to current value position */ | ||
76 | int len; /* length of this cell/string/byte */ | ||
77 | int ival; | ||
78 | int upto; /* the number of bytes we have written to buf */ | ||
79 | char fmt[3]; | ||
80 | |||
81 | upto = 0; | ||
82 | |||
83 | if (disp->verbose) | ||
84 | fprintf(stderr, "Decoding value:\n"); | ||
85 | |||
86 | fmt[0] = '%'; | ||
87 | fmt[1] = disp->type ? disp->type : 'd'; | ||
88 | fmt[2] = '\0'; | ||
89 | for (; arg_count > 0; arg++, arg_count--, upto += len) { | ||
90 | /* assume integer unless told otherwise */ | ||
91 | if (disp->type == 's') | ||
92 | len = strlen(*arg) + 1; | ||
93 | else | ||
94 | len = disp->size == -1 ? 4 : disp->size; | ||
95 | |||
96 | /* enlarge our value buffer by a suitable margin if needed */ | ||
97 | if (upto + len > value_size) { | ||
98 | value_size = (upto + len) + 500; | ||
99 | value = realloc(value, value_size); | ||
100 | if (!value) { | ||
101 | fprintf(stderr, "Out of mmory: cannot alloc " | ||
102 | "%d bytes\n", value_size); | ||
103 | return -1; | ||
104 | } | ||
105 | } | ||
106 | |||
107 | ptr = value + upto; | ||
108 | if (disp->type == 's') { | ||
109 | memcpy(ptr, *arg, len); | ||
110 | if (disp->verbose) | ||
111 | fprintf(stderr, "\tstring: '%s'\n", ptr); | ||
112 | } else { | ||
113 | int *iptr = (int *)ptr; | ||
114 | sscanf(*arg, fmt, &ival); | ||
115 | if (len == 4) | ||
116 | *iptr = cpu_to_fdt32(ival); | ||
117 | else | ||
118 | *ptr = (uint8_t)ival; | ||
119 | if (disp->verbose) { | ||
120 | fprintf(stderr, "\t%s: %d\n", | ||
121 | disp->size == 1 ? "byte" : | ||
122 | disp->size == 2 ? "short" : "int", | ||
123 | ival); | ||
124 | } | ||
125 | } | ||
126 | } | ||
127 | *value_len = upto; | ||
128 | *valuep = value; | ||
129 | if (disp->verbose) | ||
130 | fprintf(stderr, "Value size %d\n", upto); | ||
131 | return 0; | ||
132 | } | ||
133 | |||
134 | static int store_key_value(void *blob, const char *node_name, | ||
135 | const char *property, const char *buf, int len) | ||
136 | { | ||
137 | int node; | ||
138 | int err; | ||
139 | |||
140 | node = fdt_path_offset(blob, node_name); | ||
141 | if (node < 0) { | ||
142 | report_error(node_name, -1, node); | ||
143 | return -1; | ||
144 | } | ||
145 | |||
146 | err = fdt_setprop(blob, node, property, buf, len); | ||
147 | if (err) { | ||
148 | report_error(property, -1, err); | ||
149 | return -1; | ||
150 | } | ||
151 | return 0; | ||
152 | } | ||
153 | |||
154 | /** | ||
155 | * Create paths as needed for all components of a path | ||
156 | * | ||
157 | * Any components of the path that do not exist are created. Errors are | ||
158 | * reported. | ||
159 | * | ||
160 | * @param blob FDT blob to write into | ||
161 | * @param in_path Path to process | ||
162 | * @return 0 if ok, -1 on error | ||
163 | */ | ||
164 | static int create_paths(void *blob, const char *in_path) | ||
165 | { | ||
166 | const char *path = in_path; | ||
167 | const char *sep; | ||
168 | int node, offset = 0; | ||
169 | |||
170 | /* skip leading '/' */ | ||
171 | while (*path == '/') | ||
172 | path++; | ||
173 | |||
174 | for (sep = path; *sep; path = sep + 1, offset = node) { | ||
175 | /* equivalent to strchrnul(), but it requires _GNU_SOURCE */ | ||
176 | sep = strchr(path, '/'); | ||
177 | if (!sep) | ||
178 | sep = path + strlen(path); | ||
179 | |||
180 | node = fdt_subnode_offset_namelen(blob, offset, path, | ||
181 | sep - path); | ||
182 | if (node == -FDT_ERR_NOTFOUND) { | ||
183 | node = fdt_add_subnode_namelen(blob, offset, path, | ||
184 | sep - path); | ||
185 | } | ||
186 | if (node < 0) { | ||
187 | report_error(path, sep - path, node); | ||
188 | return -1; | ||
189 | } | ||
190 | } | ||
191 | |||
192 | return 0; | ||
193 | } | ||
194 | |||
195 | /** | ||
196 | * Create a new node in the fdt. | ||
197 | * | ||
198 | * This will overwrite the node_name string. Any error is reported. | ||
199 | * | ||
200 | * TODO: Perhaps create fdt_path_offset_namelen() so we don't need to do this. | ||
201 | * | ||
202 | * @param blob FDT blob to write into | ||
203 | * @param node_name Name of node to create | ||
204 | * @return new node offset if found, or -1 on failure | ||
205 | */ | ||
206 | static int create_node(void *blob, const char *node_name) | ||
207 | { | ||
208 | int node = 0; | ||
209 | char *p; | ||
210 | |||
211 | p = strrchr(node_name, '/'); | ||
212 | if (!p) { | ||
213 | report_error(node_name, -1, -FDT_ERR_BADPATH); | ||
214 | return -1; | ||
215 | } | ||
216 | *p = '\0'; | ||
217 | |||
218 | if (p > node_name) { | ||
219 | node = fdt_path_offset(blob, node_name); | ||
220 | if (node < 0) { | ||
221 | report_error(node_name, -1, node); | ||
222 | return -1; | ||
223 | } | ||
224 | } | ||
225 | |||
226 | node = fdt_add_subnode(blob, node, p + 1); | ||
227 | if (node < 0) { | ||
228 | report_error(p + 1, -1, node); | ||
229 | return -1; | ||
230 | } | ||
231 | |||
232 | return 0; | ||
233 | } | ||
234 | |||
235 | static int do_fdtput(struct display_info *disp, const char *filename, | ||
236 | char **arg, int arg_count) | ||
237 | { | ||
238 | char *value; | ||
239 | char *blob; | ||
240 | int len, ret = 0; | ||
241 | |||
242 | blob = utilfdt_read(filename); | ||
243 | if (!blob) | ||
244 | return -1; | ||
245 | |||
246 | switch (disp->oper) { | ||
247 | case OPER_WRITE_PROP: | ||
248 | /* | ||
249 | * Convert the arguments into a single binary value, then | ||
250 | * store them into the property. | ||
251 | */ | ||
252 | assert(arg_count >= 2); | ||
253 | if (disp->auto_path && create_paths(blob, *arg)) | ||
254 | return -1; | ||
255 | if (encode_value(disp, arg + 2, arg_count - 2, &value, &len) || | ||
256 | store_key_value(blob, *arg, arg[1], value, len)) | ||
257 | ret = -1; | ||
258 | break; | ||
259 | case OPER_CREATE_NODE: | ||
260 | for (; ret >= 0 && arg_count--; arg++) { | ||
261 | if (disp->auto_path) | ||
262 | ret = create_paths(blob, *arg); | ||
263 | else | ||
264 | ret = create_node(blob, *arg); | ||
265 | } | ||
266 | break; | ||
267 | } | ||
268 | if (ret >= 0) | ||
269 | ret = utilfdt_write(filename, blob); | ||
270 | |||
271 | free(blob); | ||
272 | return ret; | ||
273 | } | ||
274 | |||
275 | static const char *usage_msg = | ||
276 | "fdtput - write a property value to a device tree\n" | ||
277 | "\n" | ||
278 | "The command line arguments are joined together into a single value.\n" | ||
279 | "\n" | ||
280 | "Usage:\n" | ||
281 | " fdtput <options> <dt file> <node> <property> [<value>...]\n" | ||
282 | " fdtput -c <options> <dt file> [<node>...]\n" | ||
283 | "Options:\n" | ||
284 | "\t-c\t\tCreate nodes if they don't already exist\n" | ||
285 | "\t-p\t\tAutomatically create nodes as needed for the node path\n" | ||
286 | "\t-t <type>\tType of data\n" | ||
287 | "\t-v\t\tVerbose: display each value decoded from command line\n" | ||
288 | "\t-h\t\tPrint this help\n\n" | ||
289 | USAGE_TYPE_MSG; | ||
290 | |||
291 | static void usage(const char *msg) | ||
292 | { | ||
293 | if (msg) | ||
294 | fprintf(stderr, "Error: %s\n\n", msg); | ||
295 | |||
296 | fprintf(stderr, "%s", usage_msg); | ||
297 | exit(2); | ||
298 | } | ||
299 | |||
300 | int main(int argc, char *argv[]) | ||
301 | { | ||
302 | struct display_info disp; | ||
303 | char *filename = NULL; | ||
304 | |||
305 | memset(&disp, '\0', sizeof(disp)); | ||
306 | disp.size = -1; | ||
307 | disp.oper = OPER_WRITE_PROP; | ||
308 | for (;;) { | ||
309 | int c = getopt(argc, argv, "chpt:v"); | ||
310 | if (c == -1) | ||
311 | break; | ||
312 | |||
313 | /* | ||
314 | * TODO: add options to: | ||
315 | * - delete property | ||
316 | * - delete node (optionally recursively) | ||
317 | * - rename node | ||
318 | * - pack fdt before writing | ||
319 | * - set amount of free space when writing | ||
320 | * - expand fdt if value doesn't fit | ||
321 | */ | ||
322 | switch (c) { | ||
323 | case 'c': | ||
324 | disp.oper = OPER_CREATE_NODE; | ||
325 | break; | ||
326 | case 'h': | ||
327 | case '?': | ||
328 | usage(NULL); | ||
329 | case 'p': | ||
330 | disp.auto_path = 1; | ||
331 | break; | ||
332 | case 't': | ||
333 | if (utilfdt_decode_type(optarg, &disp.type, | ||
334 | &disp.size)) | ||
335 | usage("Invalid type string"); | ||
336 | break; | ||
337 | |||
338 | case 'v': | ||
339 | disp.verbose = 1; | ||
340 | break; | ||
341 | } | ||
342 | } | ||
343 | |||
344 | if (optind < argc) | ||
345 | filename = argv[optind++]; | ||
346 | if (!filename) | ||
347 | usage("Missing filename"); | ||
348 | |||
349 | argv += optind; | ||
350 | argc -= optind; | ||
351 | |||
352 | if (disp.oper == OPER_WRITE_PROP) { | ||
353 | if (argc < 1) | ||
354 | usage("Missing node"); | ||
355 | if (argc < 2) | ||
356 | usage("Missing property"); | ||
357 | } | ||
358 | |||
359 | if (do_fdtput(&disp, filename, argv, argc)) | ||
360 | return 1; | ||
361 | return 0; | ||
362 | } | ||