Commit | Line | Data |
---|---|---|
bd7a7ed7 MR |
1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* | |
3 | * Linux kernel module helpers. | |
4 | */ | |
5 | ||
6 | #include <linux/of.h> | |
e6506f06 | 7 | #include <linux/module.h> |
bd7a7ed7 MR |
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 | /* Name & Type */ | |
20 | /* %p eats all alphanum characters, so %c must be used here */ | |
21 | csize = snprintf(str, len, "of:N%pOFn%c%s", np, 'T', | |
22 | of_node_get_device_type(np)); | |
23 | tsize = csize; | |
24 | len -= csize; | |
25 | if (str) | |
26 | str += csize; | |
27 | ||
28 | of_property_for_each_string(np, "compatible", p, compat) { | |
29 | csize = strlen(compat) + 1; | |
30 | tsize += csize; | |
31 | if (csize > len) | |
32 | continue; | |
33 | ||
34 | csize = snprintf(str, len, "C%s", compat); | |
35 | for (c = str; c; ) { | |
36 | c = strchr(c, ' '); | |
37 | if (c) | |
38 | *c++ = '_'; | |
39 | } | |
40 | len -= csize; | |
41 | str += csize; | |
42 | } | |
43 | ||
44 | return tsize; | |
45 | } | |
e6506f06 MR |
46 | |
47 | int of_request_module(const struct device_node *np) | |
48 | { | |
49 | char *str; | |
50 | ssize_t size; | |
51 | int ret; | |
52 | ||
53 | if (!np) | |
54 | return -ENODEV; | |
55 | ||
56 | size = of_modalias(np, NULL, 0); | |
57 | if (size < 0) | |
58 | return size; | |
59 | ||
60 | /* Reserve an additional byte for the trailing '\0' */ | |
61 | size++; | |
62 | ||
63 | str = kmalloc(size, GFP_KERNEL); | |
64 | if (!str) | |
65 | return -ENOMEM; | |
66 | ||
67 | of_modalias(np, str, size); | |
68 | str[size - 1] = '\0'; | |
69 | ret = request_module(str); | |
70 | kfree(str); | |
71 | ||
72 | return ret; | |
73 | } | |
74 | EXPORT_SYMBOL_GPL(of_request_module); |