summaryrefslogtreecommitdiffstats
path: root/drivers/base/property.c
diff options
context:
space:
mode:
authorMika Westerberg <mika.westerberg@linux.intel.com>2015-11-30 10:11:37 -0500
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>2015-12-06 20:29:23 -0500
commit13141e1cb842ad6286c1cfa9a6b7c1577478d03b (patch)
tree1d0d58f00e8d6b4daa2c0c1f767e8c5b0a7d3ccd /drivers/base/property.c
parent362c0b30249e8639489b428ff5acc4a9d81c087f (diff)
device property: Take a copy of the property set
It is convenient if the property set associated with the device secondary firmware node is a copy of the original. This allows passing property set from a stack for example for devices created dynamically. This also ties the property set lifetime to the associated device. Because of that we provide new function device_remove_property_set() that is used to disassociate and release memory allocated for the property set. Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com> Signed-off-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com> Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Diffstat (limited to 'drivers/base/property.c')
-rw-r--r--drivers/base/property.c191
1 files changed, 173 insertions, 18 deletions
diff --git a/drivers/base/property.c b/drivers/base/property.c
index ebcbe342a77b..0b22c8a5b5db 100644
--- a/drivers/base/property.c
+++ b/drivers/base/property.c
@@ -19,24 +19,6 @@
19#include <linux/etherdevice.h> 19#include <linux/etherdevice.h>
20#include <linux/phy.h> 20#include <linux/phy.h>
21 21
22/**
23 * device_add_property_set - Add a collection of properties to a device object.
24 * @dev: Device to add properties to.
25 * @pset: Collection of properties to add.
26 *
27 * Associate a collection of device properties represented by @pset with @dev
28 * as its secondary firmware node.
29 */
30void device_add_property_set(struct device *dev, struct property_set *pset)
31{
32 if (!pset)
33 return;
34
35 pset->fwnode.type = FWNODE_PDATA;
36 set_secondary_fwnode(dev, &pset->fwnode);
37}
38EXPORT_SYMBOL_GPL(device_add_property_set);
39
40static inline bool is_pset_node(struct fwnode_handle *fwnode) 22static inline bool is_pset_node(struct fwnode_handle *fwnode)
41{ 23{
42 return fwnode && fwnode->type == FWNODE_PDATA; 24 return fwnode && fwnode->type == FWNODE_PDATA;
@@ -693,6 +675,179 @@ out:
693EXPORT_SYMBOL_GPL(fwnode_property_match_string); 675EXPORT_SYMBOL_GPL(fwnode_property_match_string);
694 676
695/** 677/**
678 * pset_free_set - releases memory allocated for copied property set
679 * @pset: Property set to release
680 *
681 * Function takes previously copied property set and releases all the
682 * memory allocated to it.
683 */
684static void pset_free_set(struct property_set *pset)
685{
686 const struct property_entry *prop;
687 size_t i, nval;
688
689 if (!pset)
690 return;
691
692 for (prop = pset->properties; prop->name; prop++) {
693 if (prop->is_array) {
694 if (prop->is_string && prop->pointer.str) {
695 nval = prop->length / sizeof(const char *);
696 for (i = 0; i < nval; i++)
697 kfree(prop->pointer.str[i]);
698 }
699 kfree(prop->pointer.raw_data);
700 } else if (prop->is_string) {
701 kfree(prop->value.str);
702 }
703 kfree(prop->name);
704 }
705
706 kfree(pset->properties);
707 kfree(pset);
708}
709
710static int pset_copy_entry(struct property_entry *dst,
711 const struct property_entry *src)
712{
713 const char **d, **s;
714 size_t i, nval;
715
716 dst->name = kstrdup(src->name, GFP_KERNEL);
717 if (!dst->name)
718 return -ENOMEM;
719
720 if (src->is_array) {
721 if (src->is_string) {
722 nval = src->length / sizeof(const char *);
723 dst->pointer.str = kcalloc(nval, sizeof(const char *),
724 GFP_KERNEL);
725 if (!dst->pointer.str)
726 return -ENOMEM;
727
728 d = dst->pointer.str;
729 s = src->pointer.str;
730 for (i = 0; i < nval; i++) {
731 d[i] = kstrdup(s[i], GFP_KERNEL);
732 if (!d[i] && s[i])
733 return -ENOMEM;
734 }
735 } else {
736 dst->pointer.raw_data = kmemdup(src->pointer.raw_data,
737 src->length, GFP_KERNEL);
738 if (!dst->pointer.raw_data)
739 return -ENOMEM;
740 }
741 } else if (src->is_string) {
742 dst->value.str = kstrdup(src->value.str, GFP_KERNEL);
743 if (!dst->value.str && src->value.str)
744 return -ENOMEM;
745 } else {
746 dst->value.raw_data = src->value.raw_data;
747 }
748
749 dst->length = src->length;
750 dst->is_array = src->is_array;
751 dst->is_string = src->is_string;
752
753 return 0;
754}
755
756/**
757 * pset_copy_set - copies property set
758 * @pset: Property set to copy
759 *
760 * This function takes a deep copy of the given property set and returns
761 * pointer to the copy. Call device_free_property_set() to free resources
762 * allocated in this function.
763 *
764 * Return: Pointer to the new property set or error pointer.
765 */
766static struct property_set *pset_copy_set(const struct property_set *pset)
767{
768 const struct property_entry *entry;
769 struct property_set *p;
770 size_t i, n = 0;
771
772 p = kzalloc(sizeof(*p), GFP_KERNEL);
773 if (!p)
774 return ERR_PTR(-ENOMEM);
775
776 while (pset->properties[n].name)
777 n++;
778
779 p->properties = kcalloc(n + 1, sizeof(*entry), GFP_KERNEL);
780 if (!p->properties) {
781 kfree(p);
782 return ERR_PTR(-ENOMEM);
783 }
784
785 for (i = 0; i < n; i++) {
786 int ret = pset_copy_entry(&p->properties[i],
787 &pset->properties[i]);
788 if (ret) {
789 pset_free_set(p);
790 return ERR_PTR(ret);
791 }
792 }
793
794 return p;
795}
796
797/**
798 * device_remove_property_set - Remove properties from a device object.
799 * @dev: Device whose properties to remove.
800 *
801 * The function removes properties previously associated to the device
802 * secondary firmware node with device_add_property_set(). Memory allocated
803 * to the properties will also be released.
804 */
805void device_remove_property_set(struct device *dev)
806{
807 struct fwnode_handle *fwnode;
808
809 fwnode = dev_fwnode(dev);
810 if (!fwnode)
811 return;
812 /*
813 * Pick either primary or secondary node depending which one holds
814 * the pset. If there is no real firmware node (ACPI/DT) primary
815 * will hold the pset.
816 */
817 if (!is_pset_node(fwnode))
818 fwnode = fwnode->secondary;
819 if (!IS_ERR(fwnode) && is_pset_node(fwnode))
820 pset_free_set(to_pset_node(fwnode));
821 set_secondary_fwnode(dev, NULL);
822}
823EXPORT_SYMBOL_GPL(device_remove_property_set);
824
825/**
826 * device_add_property_set - Add a collection of properties to a device object.
827 * @dev: Device to add properties to.
828 * @pset: Collection of properties to add.
829 *
830 * Associate a collection of device properties represented by @pset with @dev
831 * as its secondary firmware node. The function takes a copy of @pset.
832 */
833int device_add_property_set(struct device *dev, const struct property_set *pset)
834{
835 struct property_set *p;
836
837 if (!pset)
838 return -EINVAL;
839
840 p = pset_copy_set(pset);
841 if (IS_ERR(p))
842 return PTR_ERR(p);
843
844 p->fwnode.type = FWNODE_PDATA;
845 set_secondary_fwnode(dev, &p->fwnode);
846 return 0;
847}
848EXPORT_SYMBOL_GPL(device_add_property_set);
849
850/**
696 * device_get_next_child_node - Return the next child node handle for a device 851 * device_get_next_child_node - Return the next child node handle for a device
697 * @dev: Device to find the next child node for. 852 * @dev: Device to find the next child node for.
698 * @child: Handle to one of the device's child nodes or a null handle. 853 * @child: Handle to one of the device's child nodes or a null handle.