xref: /linux-6.15/drivers/of/module.c (revision 2e030910)
1bd7a7ed7SMiquel Raynal // SPDX-License-Identifier: GPL-2.0
2bd7a7ed7SMiquel Raynal /*
3bd7a7ed7SMiquel Raynal  * Linux kernel module helpers.
4bd7a7ed7SMiquel Raynal  */
5bd7a7ed7SMiquel Raynal 
6bd7a7ed7SMiquel Raynal #include <linux/of.h>
7e6506f06SMiquel Raynal #include <linux/module.h>
8bd7a7ed7SMiquel Raynal #include <linux/slab.h>
9bd7a7ed7SMiquel Raynal #include <linux/string.h>
10bd7a7ed7SMiquel Raynal 
of_modalias(const struct device_node * np,char * str,ssize_t len)11bd7a7ed7SMiquel Raynal ssize_t of_modalias(const struct device_node *np, char *str, ssize_t len)
12bd7a7ed7SMiquel Raynal {
13bd7a7ed7SMiquel Raynal 	const char *compat;
14bd7a7ed7SMiquel Raynal 	char *c;
15bd7a7ed7SMiquel Raynal 	struct property *p;
16bd7a7ed7SMiquel Raynal 	ssize_t csize;
17bd7a7ed7SMiquel Raynal 	ssize_t tsize;
18bd7a7ed7SMiquel Raynal 
19a1aa5390SSergey Shtylyov 	/*
20a1aa5390SSergey Shtylyov 	 * Prevent a kernel oops in vsnprintf() -- it only allows passing a
21a1aa5390SSergey Shtylyov 	 * NULL ptr when the length is also 0. Also filter out the negative
22a1aa5390SSergey Shtylyov 	 * lengths...
23a1aa5390SSergey Shtylyov 	 */
24a1aa5390SSergey Shtylyov 	if ((len > 0 && !str) || len < 0)
25a1aa5390SSergey Shtylyov 		return -EINVAL;
26a1aa5390SSergey Shtylyov 
27bd7a7ed7SMiquel Raynal 	/* Name & Type */
28bd7a7ed7SMiquel Raynal 	/* %p eats all alphanum characters, so %c must be used here */
29bd7a7ed7SMiquel Raynal 	csize = snprintf(str, len, "of:N%pOFn%c%s", np, 'T',
30bd7a7ed7SMiquel Raynal 			 of_node_get_device_type(np));
31bd7a7ed7SMiquel Raynal 	tsize = csize;
32cf7385cbSSergey Shtylyov 	if (csize >= len)
33cf7385cbSSergey Shtylyov 		csize = len > 0 ? len - 1 : 0;
34bd7a7ed7SMiquel Raynal 	len -= csize;
35bd7a7ed7SMiquel Raynal 	str += csize;
36bd7a7ed7SMiquel Raynal 
37bd7a7ed7SMiquel Raynal 	of_property_for_each_string(np, "compatible", p, compat) {
38*2e030910SSergey Shtylyov 		csize = snprintf(str, len, "C%s", compat);
39bd7a7ed7SMiquel Raynal 		tsize += csize;
40cf7385cbSSergey Shtylyov 		if (csize >= len)
41bd7a7ed7SMiquel Raynal 			continue;
42bd7a7ed7SMiquel Raynal 		for (c = str; c; ) {
43bd7a7ed7SMiquel Raynal 			c = strchr(c, ' ');
44bd7a7ed7SMiquel Raynal 			if (c)
45bd7a7ed7SMiquel Raynal 				*c++ = '_';
46bd7a7ed7SMiquel Raynal 		}
47bd7a7ed7SMiquel Raynal 		len -= csize;
48bd7a7ed7SMiquel Raynal 		str += csize;
49bd7a7ed7SMiquel Raynal 	}
50bd7a7ed7SMiquel Raynal 
51bd7a7ed7SMiquel Raynal 	return tsize;
52bd7a7ed7SMiquel Raynal }
53e6506f06SMiquel Raynal 
of_request_module(const struct device_node * np)54e6506f06SMiquel Raynal int of_request_module(const struct device_node *np)
55e6506f06SMiquel Raynal {
56e6506f06SMiquel Raynal 	char *str;
57e6506f06SMiquel Raynal 	ssize_t size;
58e6506f06SMiquel Raynal 	int ret;
59e6506f06SMiquel Raynal 
60e6506f06SMiquel Raynal 	if (!np)
61e6506f06SMiquel Raynal 		return -ENODEV;
62e6506f06SMiquel Raynal 
63e6506f06SMiquel Raynal 	size = of_modalias(np, NULL, 0);
64e6506f06SMiquel Raynal 	if (size < 0)
65e6506f06SMiquel Raynal 		return size;
66e6506f06SMiquel Raynal 
67e6506f06SMiquel Raynal 	/* Reserve an additional byte for the trailing '\0' */
68e6506f06SMiquel Raynal 	size++;
69e6506f06SMiquel Raynal 
70e6506f06SMiquel Raynal 	str = kmalloc(size, GFP_KERNEL);
71e6506f06SMiquel Raynal 	if (!str)
72e6506f06SMiquel Raynal 		return -ENOMEM;
73e6506f06SMiquel Raynal 
74e6506f06SMiquel Raynal 	of_modalias(np, str, size);
75e6506f06SMiquel Raynal 	str[size - 1] = '\0';
76e6506f06SMiquel Raynal 	ret = request_module(str);
77e6506f06SMiquel Raynal 	kfree(str);
78e6506f06SMiquel Raynal 
79e6506f06SMiquel Raynal 	return ret;
80e6506f06SMiquel Raynal }
81e6506f06SMiquel Raynal EXPORT_SYMBOL_GPL(of_request_module);
82