162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Linux kernel module helpers. 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci#include <linux/of.h> 762306a36Sopenharmony_ci#include <linux/module.h> 862306a36Sopenharmony_ci#include <linux/slab.h> 962306a36Sopenharmony_ci#include <linux/string.h> 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_cissize_t of_modalias(const struct device_node *np, char *str, ssize_t len) 1262306a36Sopenharmony_ci{ 1362306a36Sopenharmony_ci const char *compat; 1462306a36Sopenharmony_ci char *c; 1562306a36Sopenharmony_ci struct property *p; 1662306a36Sopenharmony_ci ssize_t csize; 1762306a36Sopenharmony_ci ssize_t tsize; 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci /* Name & Type */ 2062306a36Sopenharmony_ci /* %p eats all alphanum characters, so %c must be used here */ 2162306a36Sopenharmony_ci csize = snprintf(str, len, "of:N%pOFn%c%s", np, 'T', 2262306a36Sopenharmony_ci of_node_get_device_type(np)); 2362306a36Sopenharmony_ci tsize = csize; 2462306a36Sopenharmony_ci len -= csize; 2562306a36Sopenharmony_ci if (str) 2662306a36Sopenharmony_ci str += csize; 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci of_property_for_each_string(np, "compatible", p, compat) { 2962306a36Sopenharmony_ci csize = strlen(compat) + 1; 3062306a36Sopenharmony_ci tsize += csize; 3162306a36Sopenharmony_ci if (csize > len) 3262306a36Sopenharmony_ci continue; 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci csize = snprintf(str, len, "C%s", compat); 3562306a36Sopenharmony_ci for (c = str; c; ) { 3662306a36Sopenharmony_ci c = strchr(c, ' '); 3762306a36Sopenharmony_ci if (c) 3862306a36Sopenharmony_ci *c++ = '_'; 3962306a36Sopenharmony_ci } 4062306a36Sopenharmony_ci len -= csize; 4162306a36Sopenharmony_ci str += csize; 4262306a36Sopenharmony_ci } 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci return tsize; 4562306a36Sopenharmony_ci} 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ciint of_request_module(const struct device_node *np) 4862306a36Sopenharmony_ci{ 4962306a36Sopenharmony_ci char *str; 5062306a36Sopenharmony_ci ssize_t size; 5162306a36Sopenharmony_ci int ret; 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci if (!np) 5462306a36Sopenharmony_ci return -ENODEV; 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci size = of_modalias(np, NULL, 0); 5762306a36Sopenharmony_ci if (size < 0) 5862306a36Sopenharmony_ci return size; 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci /* Reserve an additional byte for the trailing '\0' */ 6162306a36Sopenharmony_ci size++; 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci str = kmalloc(size, GFP_KERNEL); 6462306a36Sopenharmony_ci if (!str) 6562306a36Sopenharmony_ci return -ENOMEM; 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci of_modalias(np, str, size); 6862306a36Sopenharmony_ci str[size - 1] = '\0'; 6962306a36Sopenharmony_ci ret = request_module(str); 7062306a36Sopenharmony_ci kfree(str); 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci return ret; 7362306a36Sopenharmony_ci} 7462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(of_request_module); 75