84 lines
1.6 KiB
C
84 lines
1.6 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* Linux kernel module helpers.
|
|
*/
|
|
|
|
#include <linux/of.h>
|
|
#include <linux/module.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/string.h>
|
|
|
|
ssize_t of_modalias(const struct device_node *np, char *str, ssize_t len)
|
|
{
|
|
const char *compat;
|
|
char *c;
|
|
struct property *p;
|
|
ssize_t csize;
|
|
ssize_t tsize;
|
|
|
|
/*
|
|
* Prevent a kernel oops in vsnprintf() -- it only allows passing a
|
|
* NULL ptr when the length is also 0. Also filter out the negative
|
|
* lengths...
|
|
*/
|
|
if ((len > 0 && !str) || len < 0)
|
|
return -EINVAL;
|
|
|
|
/* Name & Type */
|
|
/* %p eats all alphanum characters, so %c must be used here */
|
|
csize = snprintf(str, len, "of:N%pOFn%c%s", np, 'T',
|
|
of_node_get_device_type(np));
|
|
tsize = csize;
|
|
if (csize >= len)
|
|
csize = len > 0 ? len - 1 : 0;
|
|
len -= csize;
|
|
str += csize;
|
|
|
|
of_property_for_each_string(np, "compatible", p, compat) {
|
|
csize = strlen(compat) + 1;
|
|
tsize += csize;
|
|
if (csize >= len)
|
|
continue;
|
|
|
|
csize = snprintf(str, len, "C%s", compat);
|
|
for (c = str; c; ) {
|
|
c = strchr(c, ' ');
|
|
if (c)
|
|
*c++ = '_';
|
|
}
|
|
len -= csize;
|
|
str += csize;
|
|
}
|
|
|
|
return tsize;
|
|
}
|
|
|
|
int of_request_module(const struct device_node *np)
|
|
{
|
|
char *str;
|
|
ssize_t size;
|
|
int ret;
|
|
|
|
if (!np)
|
|
return -ENODEV;
|
|
|
|
size = of_modalias(np, NULL, 0);
|
|
if (size < 0)
|
|
return size;
|
|
|
|
/* Reserve an additional byte for the trailing '\0' */
|
|
size++;
|
|
|
|
str = kmalloc(size, GFP_KERNEL);
|
|
if (!str)
|
|
return -ENOMEM;
|
|
|
|
of_modalias(np, str, size);
|
|
str[size - 1] = '\0';
|
|
ret = request_module(str);
|
|
kfree(str);
|
|
|
|
return ret;
|
|
}
|
|
EXPORT_SYMBOL_GPL(of_request_module);
|