On Thu, Apr 20, 2023 at 10:09:28PM +0200, Ahmad Fatoum wrote: > Currently, the usual way within scripts to differentiate between machines > is to compare global.model or global.hostname. Both are suboptimal, > because they may change between releases or overridden by the user. > > In C code, the machine compatible is used for this purpose. Add a new > of_compatible command that makes of_machine_is_compatible/ > of_device_is_compatible available to scripts. Example use: > > /env/init/fixups: > > #!/bin/sh > > if of_compatible -k radxa,rock3a ; then > of_property -df mmc0 sd-uhs-sdr104 > fi > > Signed-off-by: Ahmad Fatoum <a.fatoum@xxxxxxxxxxxxxx> > --- > commands/Kconfig | 15 +++++ > commands/Makefile | 1 + > commands/of_compatible.c | 139 +++++++++++++++++++++++++++++++++++++++ > 3 files changed, 155 insertions(+) > create mode 100644 commands/of_compatible.c > > diff --git a/commands/Kconfig b/commands/Kconfig > index b9abd8565523..59e8d6fb382d 100644 > --- a/commands/Kconfig > +++ b/commands/Kconfig > @@ -2201,6 +2201,21 @@ config CMD_LSMOD > help > List loaded barebox modules. > > +config CMD_OF_COMPATIBLE > + tristate > + select OFTREE > + prompt "of_compatible" > + help > + Check DT node's compatible > + > + Usage: [-fFnk] [COMPAT] > + > + Options: > + -f dtb work on dtb instead of internal devicetree > + -F apply fixups on devicetree before compare > + -n node node path or alias to compare its comptible (default is /) s/comptible/compatible/ > + -k compare $global.of.kernel.add_machine_compatible as well > + > config CMD_OF_DIFF > tristate > select OFTREE > diff --git a/commands/Makefile b/commands/Makefile > index e5cc21f1970a..0ac84076f83d 100644 > --- a/commands/Makefile > +++ b/commands/Makefile > @@ -82,6 +82,7 @@ obj-$(CONFIG_CMD_USB) += usb.o > obj-$(CONFIG_CMD_TIME) += time.o > obj-$(CONFIG_CMD_UPTIME) += uptime.o > obj-$(CONFIG_CMD_OFTREE) += oftree.o > +obj-$(CONFIG_CMD_OF_COMPATIBLE) += of_compatible.o > obj-$(CONFIG_CMD_OF_DIFF) += of_diff.o > obj-$(CONFIG_CMD_OF_PROPERTY) += of_property.o > obj-$(CONFIG_CMD_OF_NODE) += of_node.o > diff --git a/commands/of_compatible.c b/commands/of_compatible.c > new file mode 100644 > index 000000000000..e4684f23b71d > --- /dev/null > +++ b/commands/of_compatible.c > @@ -0,0 +1,139 @@ > +// SPDX-License-Identifier: GPL-2.0-only > +// SPDX-FileCopyrightText: © 2023 Ahmad Fatoum <a.fatoum@xxxxxxxxxxxxxx> > + > +#include <common.h> > +#include <libfile.h> > +#include <fdt.h> > +#include <of.h> > +#include <command.h> > +#include <complete.h> > +#include <errno.h> > +#include <getopt.h> > + > +static int do_of_compatible(int argc, char *argv[]) > +{ > + int opt; > + int ret = 0; > + bool fix = false, kernel_compat = false; > + struct device_node *root = NULL, *node, *of_free = NULL; > + char *dtbfile = NULL; > + const char *compat, *nodename = "/"; > + > + while ((opt = getopt(argc, argv, "f:n:Fk")) > 0) { > + switch (opt) { > + case 'f': > + dtbfile = optarg; > + break; > + case 'n': > + nodename = optarg; > + break; > + case 'F': > + fix = true; > + break; > + case 'k': > + kernel_compat = true; > + break; > + default: > + return COMMAND_ERROR_USAGE; > + } > + } > + > + if (argc - optind != 1) > + return COMMAND_ERROR_USAGE; > + > + compat = argv[optind]; > + > + if (dtbfile) { > + size_t size; > + void *fdt; > + > + fdt = read_file(dtbfile, &size); > + if (!fdt) { > + printf("unable to read %s: %s\n", dtbfile, strerror(errno)); > + return -errno; > + } > + > + root = of_unflatten_dtb(fdt, size); > + > + free(fdt); > + > + if (IS_ERR(root)) { > + ret = PTR_ERR(root); > + goto out; > + } I've seen this pattern once too much and created a helper function to read a dtb file and to unflatten it. See the patch I just sent. > + > + of_free = root; > + } else { > + root = of_get_root_node(); > + > + if (fix) { > + /* create a copy of internal devicetree */ > + void *fdt; > + fdt = of_flatten_dtb(root); > + root = of_unflatten_dtb(fdt, fdt_totalsize(fdt)); > + > + free(fdt); > + > + if (IS_ERR(root)) { > + ret = PTR_ERR(root); > + goto out; > + } > + > + of_free = root; > + } You could simplify this by doing: root = of_free = of_dup(root); > + } > + > + if (fix) { > + ret = of_fix_tree(root); > + if (ret) > + goto out; > + } > + > + node = of_find_node_by_path_or_alias(root, nodename); > + if (!node) { > + printf("Cannot find nodepath %s\n", nodename); > + ret = -ENOENT; > + goto out; > + } > + > + if (kernel_compat) { > + const char *compat_override; > + > + if (node->parent) { > + printf("-k only valid for root node\n"); > + ret = COMMAND_ERROR_USAGE; > + goto out; > + } > + > + compat_override = barebox_get_of_machine_compatible() ?: ""; > + if (strcmp(compat_override, compat) == 0) { > + ret = 0; > + goto out; > + } > + } > + > + ret = !of_device_is_compatible(node, compat); > + > +out: > + if (of_free) > + of_delete_node(of_free); of_delete_node() is safe for being called with a NULL pointer. Sascha -- Pengutronix e.K. | | Steuerwalder Str. 21 | http://www.pengutronix.de/ | 31137 Hildesheim, Germany | Phone: +49-5121-206917-0 | Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-5555 |