7ef4087f99
We have a better idea why the Yoga patches are affecting one touchpad. Bring them back so everyone can benefit from Yoga.
248 lines
7.0 KiB
Diff
248 lines
7.0 KiB
Diff
From 13141e1cb842ad6286c1cfa9a6b7c1577478d03b Mon Sep 17 00:00:00 2001
|
|
From: Mika Westerberg <mika.westerberg@linux.intel.com>
|
|
Date: Mon, 30 Nov 2015 17:11:37 +0200
|
|
Subject: [PATCH 09/16] 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>
|
|
---
|
|
drivers/base/property.c | 191 ++++++++++++++++++++++++++++++++++++++++++-----
|
|
include/linux/property.h | 3 +-
|
|
2 files changed, 175 insertions(+), 19 deletions(-)
|
|
|
|
diff --git a/drivers/base/property.c b/drivers/base/property.c
|
|
index ebcbe34..0b22c8a 100644
|
|
--- a/drivers/base/property.c
|
|
+++ b/drivers/base/property.c
|
|
@@ -19,24 +19,6 @@
|
|
#include <linux/etherdevice.h>
|
|
#include <linux/phy.h>
|
|
|
|
-/**
|
|
- * device_add_property_set - Add a collection of properties to a device object.
|
|
- * @dev: Device to add properties to.
|
|
- * @pset: Collection of properties to add.
|
|
- *
|
|
- * Associate a collection of device properties represented by @pset with @dev
|
|
- * as its secondary firmware node.
|
|
- */
|
|
-void device_add_property_set(struct device *dev, struct property_set *pset)
|
|
-{
|
|
- if (!pset)
|
|
- return;
|
|
-
|
|
- pset->fwnode.type = FWNODE_PDATA;
|
|
- set_secondary_fwnode(dev, &pset->fwnode);
|
|
-}
|
|
-EXPORT_SYMBOL_GPL(device_add_property_set);
|
|
-
|
|
static inline bool is_pset_node(struct fwnode_handle *fwnode)
|
|
{
|
|
return fwnode && fwnode->type == FWNODE_PDATA;
|
|
@@ -693,6 +675,179 @@ out:
|
|
EXPORT_SYMBOL_GPL(fwnode_property_match_string);
|
|
|
|
/**
|
|
+ * pset_free_set - releases memory allocated for copied property set
|
|
+ * @pset: Property set to release
|
|
+ *
|
|
+ * Function takes previously copied property set and releases all the
|
|
+ * memory allocated to it.
|
|
+ */
|
|
+static void pset_free_set(struct property_set *pset)
|
|
+{
|
|
+ const struct property_entry *prop;
|
|
+ size_t i, nval;
|
|
+
|
|
+ if (!pset)
|
|
+ return;
|
|
+
|
|
+ for (prop = pset->properties; prop->name; prop++) {
|
|
+ if (prop->is_array) {
|
|
+ if (prop->is_string && prop->pointer.str) {
|
|
+ nval = prop->length / sizeof(const char *);
|
|
+ for (i = 0; i < nval; i++)
|
|
+ kfree(prop->pointer.str[i]);
|
|
+ }
|
|
+ kfree(prop->pointer.raw_data);
|
|
+ } else if (prop->is_string) {
|
|
+ kfree(prop->value.str);
|
|
+ }
|
|
+ kfree(prop->name);
|
|
+ }
|
|
+
|
|
+ kfree(pset->properties);
|
|
+ kfree(pset);
|
|
+}
|
|
+
|
|
+static int pset_copy_entry(struct property_entry *dst,
|
|
+ const struct property_entry *src)
|
|
+{
|
|
+ const char **d, **s;
|
|
+ size_t i, nval;
|
|
+
|
|
+ dst->name = kstrdup(src->name, GFP_KERNEL);
|
|
+ if (!dst->name)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ if (src->is_array) {
|
|
+ if (src->is_string) {
|
|
+ nval = src->length / sizeof(const char *);
|
|
+ dst->pointer.str = kcalloc(nval, sizeof(const char *),
|
|
+ GFP_KERNEL);
|
|
+ if (!dst->pointer.str)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ d = dst->pointer.str;
|
|
+ s = src->pointer.str;
|
|
+ for (i = 0; i < nval; i++) {
|
|
+ d[i] = kstrdup(s[i], GFP_KERNEL);
|
|
+ if (!d[i] && s[i])
|
|
+ return -ENOMEM;
|
|
+ }
|
|
+ } else {
|
|
+ dst->pointer.raw_data = kmemdup(src->pointer.raw_data,
|
|
+ src->length, GFP_KERNEL);
|
|
+ if (!dst->pointer.raw_data)
|
|
+ return -ENOMEM;
|
|
+ }
|
|
+ } else if (src->is_string) {
|
|
+ dst->value.str = kstrdup(src->value.str, GFP_KERNEL);
|
|
+ if (!dst->value.str && src->value.str)
|
|
+ return -ENOMEM;
|
|
+ } else {
|
|
+ dst->value.raw_data = src->value.raw_data;
|
|
+ }
|
|
+
|
|
+ dst->length = src->length;
|
|
+ dst->is_array = src->is_array;
|
|
+ dst->is_string = src->is_string;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * pset_copy_set - copies property set
|
|
+ * @pset: Property set to copy
|
|
+ *
|
|
+ * This function takes a deep copy of the given property set and returns
|
|
+ * pointer to the copy. Call device_free_property_set() to free resources
|
|
+ * allocated in this function.
|
|
+ *
|
|
+ * Return: Pointer to the new property set or error pointer.
|
|
+ */
|
|
+static struct property_set *pset_copy_set(const struct property_set *pset)
|
|
+{
|
|
+ const struct property_entry *entry;
|
|
+ struct property_set *p;
|
|
+ size_t i, n = 0;
|
|
+
|
|
+ p = kzalloc(sizeof(*p), GFP_KERNEL);
|
|
+ if (!p)
|
|
+ return ERR_PTR(-ENOMEM);
|
|
+
|
|
+ while (pset->properties[n].name)
|
|
+ n++;
|
|
+
|
|
+ p->properties = kcalloc(n + 1, sizeof(*entry), GFP_KERNEL);
|
|
+ if (!p->properties) {
|
|
+ kfree(p);
|
|
+ return ERR_PTR(-ENOMEM);
|
|
+ }
|
|
+
|
|
+ for (i = 0; i < n; i++) {
|
|
+ int ret = pset_copy_entry(&p->properties[i],
|
|
+ &pset->properties[i]);
|
|
+ if (ret) {
|
|
+ pset_free_set(p);
|
|
+ return ERR_PTR(ret);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return p;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * device_remove_property_set - Remove properties from a device object.
|
|
+ * @dev: Device whose properties to remove.
|
|
+ *
|
|
+ * The function removes properties previously associated to the device
|
|
+ * secondary firmware node with device_add_property_set(). Memory allocated
|
|
+ * to the properties will also be released.
|
|
+ */
|
|
+void device_remove_property_set(struct device *dev)
|
|
+{
|
|
+ struct fwnode_handle *fwnode;
|
|
+
|
|
+ fwnode = dev_fwnode(dev);
|
|
+ if (!fwnode)
|
|
+ return;
|
|
+ /*
|
|
+ * Pick either primary or secondary node depending which one holds
|
|
+ * the pset. If there is no real firmware node (ACPI/DT) primary
|
|
+ * will hold the pset.
|
|
+ */
|
|
+ if (!is_pset_node(fwnode))
|
|
+ fwnode = fwnode->secondary;
|
|
+ if (!IS_ERR(fwnode) && is_pset_node(fwnode))
|
|
+ pset_free_set(to_pset_node(fwnode));
|
|
+ set_secondary_fwnode(dev, NULL);
|
|
+}
|
|
+EXPORT_SYMBOL_GPL(device_remove_property_set);
|
|
+
|
|
+/**
|
|
+ * device_add_property_set - Add a collection of properties to a device object.
|
|
+ * @dev: Device to add properties to.
|
|
+ * @pset: Collection of properties to add.
|
|
+ *
|
|
+ * Associate a collection of device properties represented by @pset with @dev
|
|
+ * as its secondary firmware node. The function takes a copy of @pset.
|
|
+ */
|
|
+int device_add_property_set(struct device *dev, const struct property_set *pset)
|
|
+{
|
|
+ struct property_set *p;
|
|
+
|
|
+ if (!pset)
|
|
+ return -EINVAL;
|
|
+
|
|
+ p = pset_copy_set(pset);
|
|
+ if (IS_ERR(p))
|
|
+ return PTR_ERR(p);
|
|
+
|
|
+ p->fwnode.type = FWNODE_PDATA;
|
|
+ set_secondary_fwnode(dev, &p->fwnode);
|
|
+ return 0;
|
|
+}
|
|
+EXPORT_SYMBOL_GPL(device_add_property_set);
|
|
+
|
|
+/**
|
|
* device_get_next_child_node - Return the next child node handle for a device
|
|
* @dev: Device to find the next child node for.
|
|
* @child: Handle to one of the device's child nodes or a null handle.
|
|
diff --git a/include/linux/property.h b/include/linux/property.h
|
|
index d1cf208..3a8c7d7 100644
|
|
--- a/include/linux/property.h
|
|
+++ b/include/linux/property.h
|
|
@@ -240,7 +240,8 @@ struct property_set {
|
|
struct property_entry *properties;
|
|
};
|
|
|
|
-void device_add_property_set(struct device *dev, struct property_set *pset);
|
|
+int device_add_property_set(struct device *dev, const struct property_set *pset);
|
|
+void device_remove_property_set(struct device *dev);
|
|
|
|
bool device_dma_supported(struct device *dev);
|
|
|
|
--
|
|
2.5.0
|
|
|