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
of_modalias(const struct device_node * np,char * str,ssize_t len)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
of_request_module(const struct device_node * np)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