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