blob: f2197f51930ba0cd81bef1e9aa6fd54948eb350d [file] [log] [blame]
Stephen Warrencd296722012-09-28 21:25:59 +00001/*
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 */
32enum oper_type {
33 OPER_WRITE_PROP, /* Write a property in a node */
34 OPER_CREATE_NODE, /* Create a new node */
35};
36
37struct 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 */
53static 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 */
70static 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
134static 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 */
164static 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 */
206static 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
235static 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
275static 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
291static 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
300int 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}