[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