1222 lines · 29.4 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0/* * Software nodes for the firmware node framework. * * Copyright (C) 2018, Intel Corporation * Author: Heikki Krogerus <heikki.krogerus@linux.intel.com> */ #include <linux/container_of.h>#include <linux/device.h>#include <linux/err.h>#include <linux/export.h>#include <linux/idr.h>#include <linux/init.h>#include <linux/kobject.h>#include <linux/kstrtox.h>#include <linux/list.h>#include <linux/property.h>#include <linux/slab.h>#include <linux/spinlock.h>#include <linux/string.h>#include <linux/sysfs.h>#include <linux/types.h> #include "base.h" struct swnode {	struct kobject kobj;	struct fwnode_handle fwnode;	const struct software_node *node;	int id; 	/* hierarchy */	struct ida child_ids;	struct list_head entry;	struct list_head children;	struct swnode *parent; 	unsigned int allocated:1;	unsigned int managed:1;}; static DEFINE_IDA(swnode_root_ids);static struct kset *swnode_kset; #define kobj_to_swnode(_kobj_) container_of(_kobj_, struct swnode, kobj) static const struct fwnode_operations software_node_ops; bool is_software_node(const struct fwnode_handle *fwnode){	return !IS_ERR_OR_NULL(fwnode) && fwnode->ops == &software_node_ops;}EXPORT_SYMBOL_GPL(is_software_node); #define to_swnode(__fwnode)						\	({								\		typeof(__fwnode) __to_swnode_fwnode = __fwnode;		\									\		is_software_node(__to_swnode_fwnode) ?			\			container_of(__to_swnode_fwnode,		\				     struct swnode, fwnode) : NULL;	\	}) static inline struct swnode *dev_to_swnode(struct device *dev){	struct fwnode_handle *fwnode = dev_fwnode(dev); 	if (!fwnode)		return NULL; 	if (!is_software_node(fwnode))		fwnode = fwnode->secondary; 	return to_swnode(fwnode);} static struct swnode *software_node_to_swnode(const struct software_node *node){	struct swnode *swnode = NULL;	struct kobject *k; 	if (!node)		return NULL; 	spin_lock(&swnode_kset->list_lock); 	list_for_each_entry(k, &swnode_kset->list, entry) {		swnode = kobj_to_swnode(k);		if (swnode->node == node)			break;		swnode = NULL;	} 	spin_unlock(&swnode_kset->list_lock); 	return swnode;} const struct software_node *to_software_node(const struct fwnode_handle *fwnode){	const struct swnode *swnode = to_swnode(fwnode); 	return swnode ? swnode->node : NULL;}EXPORT_SYMBOL_GPL(to_software_node); struct fwnode_handle *software_node_fwnode(const struct software_node *node){	struct swnode *swnode = software_node_to_swnode(node); 	return swnode ? &swnode->fwnode : NULL;}EXPORT_SYMBOL_GPL(software_node_fwnode); /* -------------------------------------------------------------------------- *//* property_entry processing */ static const struct property_entry *property_entry_get(const struct property_entry *prop, const char *name){	if (!prop)		return NULL; 	for (; prop->name; prop++)		if (!strcmp(name, prop->name))			return prop; 	return NULL;} static const void *property_get_pointer(const struct property_entry *prop){	if (!prop->length)		return NULL; 	return prop->is_inline ? &prop->value : prop->pointer;} static const void *property_entry_find(const struct property_entry *props,				       const char *propname, size_t length){	const struct property_entry *prop;	const void *pointer; 	prop = property_entry_get(props, propname);	if (!prop)		return ERR_PTR(-EINVAL);	pointer = property_get_pointer(prop);	if (!pointer)		return ERR_PTR(-ENODATA);	if (length > prop->length)		return ERR_PTR(-EOVERFLOW);	return pointer;} static intproperty_entry_count_elems_of_size(const struct property_entry *props,				   const char *propname, size_t length){	const struct property_entry *prop; 	prop = property_entry_get(props, propname);	if (!prop)		return -EINVAL; 	return prop->length / length;} static int property_entry_read_int_array(const struct property_entry *props,					 const char *name,					 unsigned int elem_size, void *val,					 size_t nval){	const void *pointer;	size_t length; 	if (!val)		return property_entry_count_elems_of_size(props, name,							  elem_size); 	if (!is_power_of_2(elem_size) || elem_size > sizeof(u64))		return -ENXIO; 	length = nval * elem_size; 	pointer = property_entry_find(props, name, length);	if (IS_ERR(pointer))		return PTR_ERR(pointer); 	memcpy(val, pointer, length);	return 0;} static int property_entry_read_string_array(const struct property_entry *props,					    const char *propname,					    const char **strings, size_t nval){	const void *pointer;	size_t length;	int array_len; 	/* Find out the array length. */	array_len = property_entry_count_elems_of_size(props, propname,						       sizeof(const char *));	if (array_len < 0)		return array_len; 	/* Return how many there are if strings is NULL. */	if (!strings)		return array_len; 	array_len = min_t(size_t, nval, array_len);	length = array_len * sizeof(*strings); 	pointer = property_entry_find(props, propname, length);	if (IS_ERR(pointer))		return PTR_ERR(pointer); 	memcpy(strings, pointer, length); 	return array_len;} static void property_entry_free_data(const struct property_entry *p){	const char * const *src_str;	size_t i, nval; 	if (p->type == DEV_PROP_STRING) {		src_str = property_get_pointer(p);		nval = p->length / sizeof(*src_str);		for (i = 0; i < nval; i++)			kfree(src_str[i]);	} 	if (!p->is_inline)		kfree(p->pointer); 	kfree(p->name);} static bool property_copy_string_array(const char **dst_ptr,				       const char * const *src_ptr,				       size_t nval){	int i; 	for (i = 0; i < nval; i++) {		dst_ptr[i] = kstrdup(src_ptr[i], GFP_KERNEL);		if (!dst_ptr[i] && src_ptr[i]) {			while (--i >= 0)				kfree(dst_ptr[i]);			return false;		}	} 	return true;} static int property_entry_copy_data(struct property_entry *dst,				    const struct property_entry *src){	const void *pointer = property_get_pointer(src);	void *dst_ptr;	size_t nval; 	/*	 * Properties with no data should not be marked as stored	 * out of line.	 */	if (!src->is_inline && !src->length)		return -ENODATA; 	/*	 * Reference properties are never stored inline as	 * they are too big.	 */	if (src->type == DEV_PROP_REF && src->is_inline)		return -EINVAL; 	if (src->length <= sizeof(dst->value)) {		dst_ptr = &dst->value;		dst->is_inline = true;	} else {		dst_ptr = kmalloc(src->length, GFP_KERNEL);		if (!dst_ptr)			return -ENOMEM;		dst->pointer = dst_ptr;	} 	if (src->type == DEV_PROP_STRING) {		nval = src->length / sizeof(const char *);		if (!property_copy_string_array(dst_ptr, pointer, nval)) {			if (!dst->is_inline)				kfree(dst->pointer);			return -ENOMEM;		}	} else {		memcpy(dst_ptr, pointer, src->length);	} 	dst->length = src->length;	dst->type = src->type;	dst->name = kstrdup(src->name, GFP_KERNEL);	if (!dst->name) {		property_entry_free_data(dst);		return -ENOMEM;	} 	return 0;} /** * property_entries_dup - duplicate array of properties * @properties: array of properties to copy * * This function creates a deep copy of the given NULL-terminated array * of property entries. */struct property_entry *property_entries_dup(const struct property_entry *properties){	struct property_entry *p;	int i, n = 0;	int ret; 	if (!properties)		return NULL; 	while (properties[n].name)		n++; 	p = kzalloc_objs(*p, n + 1);	if (!p)		return ERR_PTR(-ENOMEM); 	for (i = 0; i < n; i++) {		ret = property_entry_copy_data(&p[i], &properties[i]);		if (ret) {			while (--i >= 0)				property_entry_free_data(&p[i]);			kfree(p);			return ERR_PTR(ret);		}	} 	return p;}EXPORT_SYMBOL_GPL(property_entries_dup); /** * property_entries_free - free previously allocated array of properties * @properties: array of properties to destroy * * This function frees given NULL-terminated array of property entries, * along with their data. */void property_entries_free(const struct property_entry *properties){	const struct property_entry *p; 	if (!properties)		return; 	for (p = properties; p->name; p++)		property_entry_free_data(p); 	kfree(properties);}EXPORT_SYMBOL_GPL(property_entries_free); /* -------------------------------------------------------------------------- *//* fwnode operations */ static struct swnode *swnode_get(struct swnode *swnode){	kobject_get(&swnode->kobj); 	return swnode;} static void swnode_put(struct swnode *swnode){	kobject_put(&swnode->kobj);} static struct fwnode_handle *software_node_get(struct fwnode_handle *fwnode){	struct swnode *swnode = swnode_get(to_swnode(fwnode)); 	return &swnode->fwnode;} static void software_node_put(struct fwnode_handle *fwnode){	swnode_put(to_swnode(fwnode));} static bool software_node_property_present(const struct fwnode_handle *fwnode,					   const char *propname){	struct swnode *swnode = to_swnode(fwnode); 	return !!property_entry_get(swnode->node->properties, propname);} static int software_node_read_int_array(const struct fwnode_handle *fwnode,					const char *propname,					unsigned int elem_size, void *val,					size_t nval){	struct swnode *swnode = to_swnode(fwnode); 	return property_entry_read_int_array(swnode->node->properties, propname,					     elem_size, val, nval);} static int software_node_read_string_array(const struct fwnode_handle *fwnode,					   const char *propname,					   const char **val, size_t nval){	struct swnode *swnode = to_swnode(fwnode); 	return property_entry_read_string_array(swnode->node->properties,						propname, val, nval);} static const char *software_node_get_name(const struct fwnode_handle *fwnode){	const struct swnode *swnode = to_swnode(fwnode); 	return kobject_name(&swnode->kobj);} static const char *software_node_get_name_prefix(const struct fwnode_handle *fwnode){	struct fwnode_handle *parent;	const char *prefix; 	parent = fwnode_get_parent(fwnode);	if (!parent)		return ""; 	/* Figure out the prefix from the parents. */	while (is_software_node(parent))		parent = fwnode_get_next_parent(parent); 	prefix = fwnode_get_name_prefix(parent);	fwnode_handle_put(parent); 	/* Guess something if prefix was NULL. */	return prefix ?: "/";} static struct fwnode_handle *software_node_get_parent(const struct fwnode_handle *fwnode){	struct swnode *swnode = to_swnode(fwnode); 	if (!swnode || !swnode->parent)		return NULL; 	return fwnode_handle_get(&swnode->parent->fwnode);} static struct fwnode_handle *software_node_get_next_child(const struct fwnode_handle *fwnode,			     struct fwnode_handle *child){	struct swnode *p = to_swnode(fwnode);	struct swnode *c = to_swnode(child); 	if (!p || list_empty(&p->children) ||	    (c && list_is_last(&c->entry, &p->children))) {		fwnode_handle_put(child);		return NULL;	} 	if (c)		c = list_next_entry(c, entry);	else		c = list_first_entry(&p->children, struct swnode, entry); 	fwnode_handle_put(child);	return fwnode_handle_get(&c->fwnode);} static struct fwnode_handle *software_node_get_named_child_node(const struct fwnode_handle *fwnode,				   const char *childname){	struct swnode *swnode = to_swnode(fwnode);	struct swnode *child; 	if (!swnode || list_empty(&swnode->children))		return NULL;

Lines 1–500 of 1222.