xref: /kernel/linux/linux-6.6/drivers/of/module.c (revision 62306a36)
162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Linux kernel module helpers.
462306a36Sopenharmony_ci */
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci#include <linux/of.h>
762306a36Sopenharmony_ci#include <linux/module.h>
862306a36Sopenharmony_ci#include <linux/slab.h>
962306a36Sopenharmony_ci#include <linux/string.h>
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_cissize_t of_modalias(const struct device_node *np, char *str, ssize_t len)
1262306a36Sopenharmony_ci{
1362306a36Sopenharmony_ci	const char *compat;
1462306a36Sopenharmony_ci	char *c;
1562306a36Sopenharmony_ci	struct property *p;
1662306a36Sopenharmony_ci	ssize_t csize;
1762306a36Sopenharmony_ci	ssize_t tsize;
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci	/* Name & Type */
2062306a36Sopenharmony_ci	/* %p eats all alphanum characters, so %c must be used here */
2162306a36Sopenharmony_ci	csize = snprintf(str, len, "of:N%pOFn%c%s", np, 'T',
2262306a36Sopenharmony_ci			 of_node_get_device_type(np));
2362306a36Sopenharmony_ci	tsize = csize;
2462306a36Sopenharmony_ci	len -= csize;
2562306a36Sopenharmony_ci	if (str)
2662306a36Sopenharmony_ci		str += csize;
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci	of_property_for_each_string(np, "compatible", p, compat) {
2962306a36Sopenharmony_ci		csize = strlen(compat) + 1;
3062306a36Sopenharmony_ci		tsize += csize;
3162306a36Sopenharmony_ci		if (csize > len)
3262306a36Sopenharmony_ci			continue;
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci		csize = snprintf(str, len, "C%s", compat);
3562306a36Sopenharmony_ci		for (c = str; c; ) {
3662306a36Sopenharmony_ci			c = strchr(c, ' ');
3762306a36Sopenharmony_ci			if (c)
3862306a36Sopenharmony_ci				*c++ = '_';
3962306a36Sopenharmony_ci		}
4062306a36Sopenharmony_ci		len -= csize;
4162306a36Sopenharmony_ci		str += csize;
4262306a36Sopenharmony_ci	}
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci	return tsize;
4562306a36Sopenharmony_ci}
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ciint of_request_module(const struct device_node *np)
4862306a36Sopenharmony_ci{
4962306a36Sopenharmony_ci	char *str;
5062306a36Sopenharmony_ci	ssize_t size;
5162306a36Sopenharmony_ci	int ret;
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci	if (!np)
5462306a36Sopenharmony_ci		return -ENODEV;
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci	size = of_modalias(np, NULL, 0);
5762306a36Sopenharmony_ci	if (size < 0)
5862306a36Sopenharmony_ci		return size;
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci	/* Reserve an additional byte for the trailing '\0' */
6162306a36Sopenharmony_ci	size++;
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci	str = kmalloc(size, GFP_KERNEL);
6462306a36Sopenharmony_ci	if (!str)
6562306a36Sopenharmony_ci		return -ENOMEM;
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci	of_modalias(np, str, size);
6862306a36Sopenharmony_ci	str[size - 1] = '\0';
6962306a36Sopenharmony_ci	ret = request_module(str);
7062306a36Sopenharmony_ci	kfree(str);
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci	return ret;
7362306a36Sopenharmony_ci}
7462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(of_request_module);
75