diff options
-rw-r--r-- | .gitignore | 1 | ||||
-rw-r--r-- | Makefile | 4 | ||||
-rw-r--r-- | Makefile.utils | 7 | ||||
-rw-r--r-- | fdtput.c | 235 | ||||
-rw-r--r-- | tests/fdtput-runtest.sh | 55 | ||||
-rwxr-xr-x | tests/run_tests.sh | 73 | ||||
-rw-r--r-- | tests/tests.sh | 1 |
7 files changed, 375 insertions, 1 deletions
@@ -11,3 +11,4 @@ lex.yy.c /convert-dtsv0 /version_gen.h /fdtget +/fdtput @@ -111,6 +111,7 @@ BIN += convert-dtsv0 BIN += dtc BIN += fdtdump BIN += fdtget +BIN += fdtput SCRIPTS = dtdiff @@ -122,6 +123,7 @@ ifneq ($(DEPTARGETS),) -include $(CONVERT_OBJS:%.o=%.d) -include $(FDTDUMP_OBJS:%.o=%.d) -include $(FDTGET_OBJS:%.o=%.d) +-include $(FDTPUT_OBJS:%.o=%.d) endif @@ -184,6 +186,8 @@ fdtdump: $(FDTDUMP_OBJS) fdtget: $(FDTGET_OBJS) $(LIBFDT_archive) +fdtput: $(FDTPUT_OBJS) $(LIBFDT_archive) + # # Testsuite rules diff --git a/Makefile.utils b/Makefile.utils index 38efa3c..48ece49 100644 --- a/Makefile.utils +++ b/Makefile.utils @@ -15,3 +15,10 @@ FDTGET_SRCS = \ util.c FDTGET_OBJS = $(FDTGET_SRCS:%.c=%.o) + + +FDTPUT_SRCS = \ + fdtput.c \ + util.c + +FDTPUT_OBJS = $(FDTPUT_SRCS:%.c=%.o) diff --git a/fdtput.c b/fdtput.c new file mode 100644 index 0000000..f6ebd24 --- /dev/null +++ b/fdtput.c @@ -0,0 +1,235 @@ +/* + * Copyright (c) 2011 The Chromium OS Authors. All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include <assert.h> +#include <ctype.h> +#include <getopt.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <libfdt.h> + +#include "util.h" + +struct display_info { + int type; /* data type (s/i/u/x or 0 for default) */ + int size; /* data size (1/2/4) */ + int verbose; /* verbose output */ +}; + +static void report_error(const char *where, int err) +{ + fprintf(stderr, "Error at '%s': %s\n", where, fdt_strerror(err)); +} + +/** + * Encode a series of arguments in a property value. + * + * @param disp Display information / options + * @param arg List of arguments from command line + * @param arg_count Number of arguments (may be 0) + * @param valuep Returns buffer containing value + * @param *value_len Returns length of value encoded + */ +static int encode_value(struct display_info *disp, char **arg, int arg_count, + char **valuep, int *value_len) +{ + char *value = NULL; /* holding area for value */ + int value_size = 0; /* size of holding area */ + char *ptr; /* pointer to current value position */ + int len; /* length of this cell/string/byte */ + int ival; + int upto; /* the number of bytes we have written to buf */ + char fmt[3]; + + upto = 0; + + if (disp->verbose) + fprintf(stderr, "Decoding value:\n"); + + fmt[0] = '%'; + fmt[1] = disp->type ? disp->type : 'd'; + fmt[2] = '\0'; + for (; arg_count > 0; arg++, arg_count--, upto += len) { + /* assume integer unless told otherwise */ + if (disp->type == 's') + len = strlen(*arg) + 1; + else + len = disp->size == -1 ? 4 : disp->size; + + /* enlarge our value buffer by a suitable margin if needed */ + if (upto + len > value_size) { + value_size = (upto + len) + 500; + value = realloc(value, value_size); + if (!value) { + fprintf(stderr, "Out of mmory: cannot alloc " + "%d bytes\n", value_size); + return -1; + } + } + + ptr = value + upto; + if (disp->type == 's') { + memcpy(ptr, *arg, len); + if (disp->verbose) + fprintf(stderr, "\tstring: '%s'\n", ptr); + } else { + int *iptr = (int *)ptr; + sscanf(*arg, fmt, &ival); + 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; +} + +static int store_key_value(void *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, node); + return -1; + } + + err = fdt_setprop(blob, node, property, buf, len); + if (err) { + report_error(property, err); + return -1; + } + return 0; +} + +static int do_fdtput(struct display_info *disp, const char *filename, + char **arg, int arg_count) +{ + char *value; + char *blob; + int len, ret = 0; + + blob = utilfdt_read(filename); + if (!blob) + return -1; + + /* convert the arguments into a single binary value, then store */ + assert(arg_count >= 2); + if (encode_value(disp, arg + 2, arg_count - 2, &value, &len) || + store_key_value(blob, *arg, arg[1], value, len)) + ret = -1; + + if (!ret) + ret = utilfdt_write(filename, blob); + + free(blob); + return ret; +} + +static const char *usage_msg = + "fdtput - write a property value to a device tree\n" + "\n" + "The command line arguments are joined together into a single value.\n" + "\n" + "Usage:\n" + " fdtput <options> <dt file> <<node> <property> [<value>...]\n" + "Options:\n" + "\t-t <type>\tType of data\n" + "\t-v\t\tVerbose: display each value decoded from command line\n" + "\t-h\t\tPrint this help\n\n" + USAGE_TYPE_MSG; + +static void usage(const char *msg) +{ + if (msg) + fprintf(stderr, "Error: %s\n\n", msg); + + fprintf(stderr, "%s", usage_msg); + exit(2); +} + +int main(int argc, char *argv[]) +{ + struct display_info disp; + char *filename = NULL; + + memset(&disp, '\0', sizeof(disp)); + disp.size = -1; + for (;;) { + int c = getopt(argc, argv, "ht:v"); + if (c == -1) + break; + + /* + * TODO: add options to: + * - delete property + * - delete node (optionally recursively) + * - rename node + * - pack fdt before writing + * - set amount of free space when writing + * - expand fdt if value doesn't fit + */ + switch (c) { + case 'h': + case '?': + usage(NULL); + + case 't': + if (utilfdt_decode_type(optarg, &disp.type, + &disp.size)) + usage("Invalid type string"); + break; + + case 'v': + disp.verbose = 1; + break; + } + } + + if (optind < argc) + filename = argv[optind++]; + if (!filename) + usage("Missing filename"); + + argv += optind; + argc -= optind; + + if (argc < 1) + usage("Missing node"); + if (argc < 2) + usage("Missing property"); + + if (do_fdtput(&disp, filename, argv, argc)) + return 1; + return 0; +} diff --git a/tests/fdtput-runtest.sh b/tests/fdtput-runtest.sh new file mode 100644 index 0000000..ea51569 --- /dev/null +++ b/tests/fdtput-runtest.sh @@ -0,0 +1,55 @@ +#! /bin/sh + +# Run script for fdtput tests +# We run fdtput to update the device tree, thn fdtget to check it + +# Usage +# fdtput-runtest.sh name expected_output dtb_file node property flags value + +. ./tests.sh + +LOG="tmp.log.$$" +EXPECT="tmp.expect.$$" + +rm -f $TMPFILE $LOG + +expect="$1" +echo "$expect" >$EXPECT +dtb="$2" +node="$3" +property="$4" +flags="$5" +shift 5 +value="$@" + +# First run fdtput +verbose_run $VALGRIND "$DTPUT" "$dtb" "$node" "$property" $value $flags +ret="$?" + +if [ "$ret" -ne 0 -a "$expect" = "ERR" ]; then + PASS +fi +if [ "$ret" -gt 127 ]; then + signame=$(kill -l $[ret - 128]) + FAIL "Killed by SIG$signame" +fi + +# Now fdtget to read the value +verbose_run_log "$LOG" $VALGRIND "$DTGET" "$dtb" "$node" "$property" $flags +ret="$?" + +if [ "$ret" -gt 127 ]; then + signame=$(kill -l $[ret - 128]) + FAIL "Killed by SIG$signame" +fi + +diff $EXPECT $LOG +ret="$?" + +rm -f $LOG $EXPECT + +if [ "$ret" -eq 0 ]; then + PASS +else + FAIL +fi diff --git a/tests/run_tests.sh b/tests/run_tests.sh index e6184df..2650559 100755 --- a/tests/run_tests.sh +++ b/tests/run_tests.sh @@ -90,6 +90,21 @@ run_fdtget_test () { base_run_test sh fdtget-runtest.sh "$@" } +run_fdtput_test () { + # run_fdtput_test name expected_output dtb_file node property flags value... + echo -n "$1: " + shift + output="$1" + dtb="$2" + node="$3" + property="$4" + flags="$5" + shift 5 + base_run_test sh fdtput-runtest.sh "$output" "$dtb" "$node" "$property" \ + "$flags" $@ +# base_run_test sh fdtput-runtest.sh "$@" +} + tree1_tests () { TREE=$1 @@ -440,6 +455,59 @@ fdtget_tests () { $file /randomnode doctor-who } +fdtput_tests () { + file=label01.dtb + src=label01.dts + + # Create some test files containing useful strings + base=tmp.test0 + file1=tmp.test1 + file2=tmp.test2 + bigfile1=tmp.test3 + bigfile2=tmp.test4 + + # Filter out anything the shell might not like + cat $src | tr -d "'\"\n\;/\.\*{}\-" | tr -s "[:blank:]" " " >$base + + # Make two small files + head -5 $base >$file1 + cat $file1 | tr a-z A-Z | cut -c10-30 | sort -r >$file2 + + # and two larger ones + cat $base > $bigfile1 + tac $base | tr a-z A-Z | sort -r >$bigfile2 + + # Allow just enough space for both file1 and file2 + (( space = $(stat -c %s $file1) + $(stat -c %s $file2) )) + $DTC -O dtb -p $space -o $file ${file%.dtb}.dts 2>/dev/null + + # run_fdtput_test <test-name> <expected-result> <file> <key> <flags> + # <args>... + run_fdtput_test "Simple string" "a_model" $file / model -ts "a_model" + run_fdtput_test "Multiple string s" "board1 board2" \ + $file / compatible -ts board1 board2 + run_fdtput_test "Single string with spaces" "board1 board2" \ + $file / compatible -ts "board1 board2" + run_fdtput_test "Integer" "32768" \ + $file /cpus/PowerPC,970@1 d-cache-size "" "32768" + run_fdtput_test "Integer hex" "8001" \ + $file /cpus/PowerPC,970@1 d-cache-size -tx 0x8001 + run_fdtput_test "Integer list" "2 3 12" \ + $file /randomnode tricky1 -tbi "02 003 12" + run_fdtput_test "Byte list short" "a b c ea ad be ef" \ + $file /randomnode blob -tbx "a b c ea ad be ef" + run_fdtput_test "Integer list short" "a0b0c0d deeaae ef000000" \ + $file /randomnode blob -tx "a0b0c0d deeaae ef000000" + run_fdtput_test "Large string list" "`cat $file1 $file2`" \ + $file /randomnode blob -ts "`cat $file1`" "`cat $file2`" + + # This should be larger than available space in the fdt ($space) + run_fdtput_test "Enormous string list" ERR \ + $file /randomnode blob -ts "`cat $bigfile1`" "`cat $bigfile2`" + + # TODO: Add tests for verbose mode? +} + utilfdt_tests () { run_test utilfdt_test } @@ -459,7 +527,7 @@ while getopts "vt:m" ARG ; do done if [ -z "$TESTSETS" ]; then - TESTSETS="libfdt utilfdt dtc dtbs_equal fdtget" + TESTSETS="libfdt utilfdt dtc dtbs_equal fdtget fdtput" fi # Make sure we don't have stale blobs lying around @@ -482,6 +550,9 @@ for set in $TESTSETS; do "fdtget") fdtget_tests ;; + "fdtput") + fdtput_tests + ;; esac done diff --git a/tests/tests.sh b/tests/tests.sh index d9a0524..6e5e76a 100644 --- a/tests/tests.sh +++ b/tests/tests.sh @@ -12,6 +12,7 @@ FAIL () { DTC=../dtc DTGET=../fdtget +DTPUT=../fdtput verbose_run () { if [ -z "$QUIET_TEST" ]; then |