xref: /openbmc/linux/drivers/of/module.c (revision b181f7029bd71238ac2754ce7052dffd69432085)
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 
19e4a44936SSergey Shtylyov 	/*
20e4a44936SSergey Shtylyov 	 * Prevent a kernel oops in vsnprintf() -- it only allows passing a
21e4a44936SSergey Shtylyov 	 * NULL ptr when the length is also 0. Also filter out the negative
22e4a44936SSergey Shtylyov 	 * lengths...
23e4a44936SSergey Shtylyov 	 */
24e4a44936SSergey Shtylyov 	if ((len > 0 && !str) || len < 0)
25e4a44936SSergey Shtylyov 		return -EINVAL;
26e4a44936SSergey 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;
32*0b0d5701SSergey Shtylyov 	if (csize >= len)
33*0b0d5701SSergey 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) {
38bd7a7ed7SMiquel Raynal 		csize = strlen(compat) + 1;
39bd7a7ed7SMiquel Raynal 		tsize += csize;
40*0b0d5701SSergey Shtylyov 		if (csize >= len)
41bd7a7ed7SMiquel Raynal 			continue;
42bd7a7ed7SMiquel Raynal 
43bd7a7ed7SMiquel Raynal 		csize = snprintf(str, len, "C%s", compat);
44bd7a7ed7SMiquel Raynal 		for (c = str; c; ) {
45bd7a7ed7SMiquel Raynal 			c = strchr(c, ' ');
46bd7a7ed7SMiquel Raynal 			if (c)
47bd7a7ed7SMiquel Raynal 				*c++ = '_';
48bd7a7ed7SMiquel Raynal 		}
49bd7a7ed7SMiquel Raynal 		len -= csize;
50bd7a7ed7SMiquel Raynal 		str += csize;
51bd7a7ed7SMiquel Raynal 	}
52bd7a7ed7SMiquel Raynal 
53bd7a7ed7SMiquel Raynal 	return tsize;
54bd7a7ed7SMiquel Raynal }
55e6506f06SMiquel Raynal 
of_request_module(const struct device_node * np)56e6506f06SMiquel Raynal int of_request_module(const struct device_node *np)
57e6506f06SMiquel Raynal {
58e6506f06SMiquel Raynal 	char *str;
59e6506f06SMiquel Raynal 	ssize_t size;
60e6506f06SMiquel Raynal 	int ret;
61e6506f06SMiquel Raynal 
62e6506f06SMiquel Raynal 	if (!np)
63e6506f06SMiquel Raynal 		return -ENODEV;
64e6506f06SMiquel Raynal 
65e6506f06SMiquel Raynal 	size = of_modalias(np, NULL, 0);
66e6506f06SMiquel Raynal 	if (size < 0)
67e6506f06SMiquel Raynal 		return size;
68e6506f06SMiquel Raynal 
69e6506f06SMiquel Raynal 	/* Reserve an additional byte for the trailing '\0' */
70e6506f06SMiquel Raynal 	size++;
71e6506f06SMiquel Raynal 
72e6506f06SMiquel Raynal 	str = kmalloc(size, GFP_KERNEL);
73e6506f06SMiquel Raynal 	if (!str)
74e6506f06SMiquel Raynal 		return -ENOMEM;
75e6506f06SMiquel Raynal 
76e6506f06SMiquel Raynal 	of_modalias(np, str, size);
77e6506f06SMiquel Raynal 	str[size - 1] = '\0';
78e6506f06SMiquel Raynal 	ret = request_module(str);
79e6506f06SMiquel Raynal 	kfree(str);
80e6506f06SMiquel Raynal 
81e6506f06SMiquel Raynal 	return ret;
82e6506f06SMiquel Raynal }
83e6506f06SMiquel Raynal EXPORT_SYMBOL_GPL(of_request_module);
84