[PATCH] commands: implement of_compatible command
Sascha Hauer
sha at pengutronix.de
Tue May 2 04:17:27 PDT 2023
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 at pengutronix.de>
> ---
> 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 at pengutronix.de>
> +
> +#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 |
More information about the barebox
mailing list