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