#include <assert.h>
#include <ctype.h>
#include <getopt.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <libfdt.h>
#include "util.h"
enum oper_type {
OPER_WRITE_PROP,
OPER_CREATE_NODE,
OPER_REMOVE_NODE,
OPER_DELETE_PROP,
};
struct display_info {
enum oper_type oper;
int type;
int size;
int verbose;
int auto_path;
};
static void report_error(const char *name, int namelen, int err)
{
if (namelen == -1)
namelen = strlen(name);
fprintf(stderr, "Error at '%1.*s': %s\n", namelen, name,
fdt_strerror(err));
}
static int encode_value(struct display_info *disp, char **arg, int arg_count,
char **valuep, int *value_len)
{
char *value = NULL;
int value_size = 0;
char *ptr;
int len;
int ival;
int upto;
upto = 0;
if (disp->verbose)
fprintf(stderr, "Decoding value:\n");
for (; arg_count > 0; arg++, arg_count--, upto += len) {
if (disp->type == 's')
len = strlen(*arg) + 1;
else
len = disp->size == -1 ? 4 : disp->size;
if (upto + len > value_size) {
value_size = (upto + len) + 500;
value = xrealloc(value, value_size);
}
ptr = value + upto;
if (disp->type == 's') {
memcpy(ptr, *arg, len);
if (disp->verbose)
fprintf(stderr, "\tstring: '%s'\n", ptr);
} else {
fdt32_t *iptr = (fdt32_t *)ptr;
char *endptr;
errno = 0;
switch (disp->type) {
case 'x':
ival = strtoul(*arg, &endptr, 16);
break;
case 'o':
ival = strtoul(*arg, &endptr, 8);
break;
case 'i':
ival = strtol(*arg, &endptr, 0);
break;
case 'u':
ival = strtoul(*arg, &endptr, 0);
break;
default:
ival = strtol(*arg, &endptr, 10);
}
if (*endptr != '\0' || errno) {
if (disp->verbose) {
fprintf(stderr,
"Couldn't parse \"%s\" as an integer\n",
*arg);
}
return -1;
}
if (len == 4)
*iptr = cpu_to_fdt32(ival);
else
*ptr = (uint8_t)ival;
if (disp->verbose) {
fprintf(stderr, "\t%s: %d\n",
disp->size == 1 ? "byte" :
disp->size == 2 ? "short" : "int",
ival);
}
}
}
*value_len = upto;
*valuep = value;
if (disp->verbose)
fprintf(stderr, "Value size %d\n", upto);
return 0;
}
#define ALIGN(x) (((x) + (FDT_TAGSIZE) - 1) & ~((FDT_TAGSIZE) - 1))
static char *realloc_fdt(char *fdt, int delta)
{
int new_sz = fdt_totalsize(fdt) + delta;
fdt = xrealloc(fdt, new_sz);
fdt_open_into(fdt, fdt, new_sz);
return fdt;
}
static char *realloc_node(char *fdt, const char *name)
{
int delta;
delta = sizeof(struct fdt_node_header) + ALIGN(strlen(name) + 1)
+ FDT_TAGSIZE;
return realloc_fdt(fdt, delta);
}
static char *realloc_property(char *fdt, int nodeoffset,
const char *name, int newlen)
{
int delta = 0;
int oldlen = 0;
if (!fdt_get_property(fdt, nodeoffset, name, &oldlen))
delta = sizeof(struct fdt_property) + strlen(name) + 1;
if (newlen > oldlen)
delta += ALIGN(newlen) - ALIGN(oldlen);
return realloc_fdt(fdt, delta);
}
static int store_key_value(char **blob, const char *node_name,
const char *property, const char *buf, int len)
{
int node;
int err;
node = fdt_path_offset(*blob, node_name);
if (node < 0) {
report_error(node_name, -1, node);
return -1;
}
err = fdt_setprop(*blob, node, property, buf, len);
if (err == -FDT_ERR_NOSPACE) {
*blob = realloc_property(*blob, node, property, len);
err = fdt_setprop(*blob, node, property, buf, len);
}
if (err) {
report_error(property, -1, err);
return -1;
}
return 0;
}
static int create_paths(char **blob, const char *in_path)
{
const char *path = in_path;
const char *sep;
int node, offset = 0;
while (*path == '/')
path++;
for (sep = path; *sep; path = sep + 1, offset = node) {
sep = strchr(path, '/');
if (!sep)
sep = path + strlen(path);
node = fdt_subnode_offset_namelen(*blob, offset, path,
sep - path);
if (node == -FDT_ERR_NOTFOUND) {
*blob = realloc_node(*blob, path);
node = fdt_add_subnode_namelen(*blob, offset, path,
sep - path);
}
if (node < 0) {
report_error(path, sep - path, node);
return -1;
}
}
return 0;
}
static int create_node(char **blob, const char *node_name)
{
int node = 0;
const char *p;
char *path = NULL;
p = strrchr(node_name, '/');
if (!p) {
report_error(node_name, -1, -FDT_ERR_BADPATH);
return -1;
}
*blob = realloc_node(*blob, p + 1);
if (p > node_name) {
path = xstrndup(node_name, (size_t)(p - node_name));
node = fdt_path_offset(*blob, path);
free(path);
if (node < 0) {
report_error(node_name, -1, node);
return -1;
}
}
node = fdt_add_subnode(*blob, node, p + 1);
if (node < 0) {
report_error(p + 1, -1, node);
return -1;
}
return 0;
}
static int delete_prop(char *blob, const char *node_name, const char *prop_name)
{
int node = 0;
node = fdt_path_offset(blob, node_name);
if (node < 0) {
report_error(node_name, -1, node);
return -1;
}
node = fdt_delprop(blob, node, prop_name);
if (node < 0) {
report_error(node_name, -1, node);
return -1;
}
return 0;
}
static int delete_node(char *blob, const char *node_name)
{
int node = 0;
node = fdt_path_offset(blob, node_name);
if (node < 0) {
report_error(node_name, -1, node);
return -1;
}
node = fdt_del_node(blob, node);
if (node < 0) {
report_error(node_name, -1, node);
return -1;
}
return 0;
}
static int do_fdtput(struct display_info *disp, const char *filename,
char **arg, int arg_count)
{
char *value = NULL;
char *blob;
char *node;
int len, ret = 0;
blob = utilfdt_read(filename, NULL);
if (!blob)
return -1;
switch (disp->oper) {
case OPER_WRITE_PROP:
assert(arg_count >= 2);
if (disp->auto_path && create_paths(&blob, *arg))
return -1;
if (encode_value(disp, arg + 2, arg_count - 2, &value, &len) ||
store_key_value(&blob, *arg, arg[1], value, len))
ret = -1;
break;
case OPER_CREATE_NODE:
for (; ret >= 0 && arg_count--; arg++) {
if (disp->auto_path)
ret = create_paths(&blob, *arg);
else
ret = create_node(&blob, *arg);
}
break;
case OPER_REMOVE_NODE:
for (; ret >= 0 && arg_count--; arg++)
ret = delete_node(blob, *arg);
break;
case OPER_DELETE_PROP:
node = *arg;
for (arg++; ret >= 0 && arg_count-- > 1; arg++)
ret = delete_prop(blob, node, *arg);
break;
}
if (ret >= 0) {
fdt_pack(blob);
ret = utilfdt_write(filename, blob);
}
free(blob);
if (value) {
free(value);
}
return ret;
}
static const char usage_synopsis[] =
"write a property value to a device tree\n"
" fdtput <options> <dt file> <node> <property> [<value>...]\n"
" fdtput -c <options> <dt file> [<node>...]\n"
" fdtput -r <options> <dt file> [<node>...]\n"
" fdtput -d <options> <dt file> <node> [<property>...]\n"
"\n"
"The command line arguments are joined together into a single value.\n"
USAGE_TYPE_MSG;
static const char usage_short_opts[] = "crdpt:v" USAGE_COMMON_SHORT_OPTS;
static struct option const usage_long_opts[] = {
{"create", no_argument, NULL, 'c'},
{"remove", no_argument, NULL, 'r'},
{"delete", no_argument, NULL, 'd'},
{"auto-path", no_argument, NULL, 'p'},
{"type", a_argument, NULL, 't'},
{"verbose", no_argument, NULL, 'v'},
USAGE_COMMON_LONG_OPTS,
};
static const char * const usage_opts_help[] = {
"Create nodes",
"Delete nodes (and any subnodes)",
"Delete properties if they already exist",
"Automatically create nodes as needed for the node path",
"Type of data",
"Display each value decoded from command line",
USAGE_COMMON_OPTS_HELP
};
int main(int argc, char *argv[])
{
int opt;
struct display_info disp;
char *filename = NULL;
memset(&disp, '\0', sizeof(disp));
disp.size = -1;
disp.oper = OPER_WRITE_PROP;
while ((opt = util_getopt_long()) != EOF) {
switch (opt) {
case_USAGE_COMMON_FLAGS
case 'c':
disp.oper = OPER_CREATE_NODE;
break;
case 'r':
disp.oper = OPER_REMOVE_NODE;
break;
case 'd':
disp.oper = OPER_DELETE_PROP;
break;
case 'p':
disp.auto_path = 1;
break;
case 't':
if (utilfdt_decode_type(optarg, &disp.type,
&disp.size))
usage("Invalid type string");
if (disp.type == 'r')
usage("Unsupported raw data type");
break;
case 'v':
disp.verbose = 1;
break;
}
}
if (optind < argc)
filename = argv[optind++];
if (!filename)
usage("missing filename");
argv += optind;
argc -= optind;
if (disp.oper == OPER_WRITE_PROP) {
if (argc < 1)
usage("missing node");
if (argc < 2)
usage("missing property");
}
if (disp.oper == OPER_DELETE_PROP)
if (argc < 1)
usage("missing node");
if (do_fdtput(&disp, filename, argv, argc))
return 1;
return 0;
}