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