diff options
author | Mika Westerberg <mika.westerberg@linux.intel.com> | 2015-11-30 10:11:37 -0500 |
---|---|---|
committer | Rafael J. Wysocki <rafael.j.wysocki@intel.com> | 2015-12-06 20:29:23 -0500 |
commit | 13141e1cb842ad6286c1cfa9a6b7c1577478d03b (patch) | |
tree | 1d0d58f00e8d6b4daa2c0c1f767e8c5b0a7d3ccd /drivers/base/property.c | |
parent | 362c0b30249e8639489b428ff5acc4a9d81c087f (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.c | 191 |
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 | */ | ||
30 | void 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 | } | ||
38 | EXPORT_SYMBOL_GPL(device_add_property_set); | ||
39 | |||
40 | static inline bool is_pset_node(struct fwnode_handle *fwnode) | 22 | static 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: | |||
693 | EXPORT_SYMBOL_GPL(fwnode_property_match_string); | 675 | EXPORT_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 | */ | ||
684 | static 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 | |||
710 | static 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 | */ | ||
766 | static 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 | */ | ||
805 | void 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 | } | ||
823 | EXPORT_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 | */ | ||
833 | int 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 | } | ||
848 | EXPORT_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. |