|
@@ -19,32 +19,14 @@
|
|
#include <linux/etherdevice.h>
|
|
#include <linux/etherdevice.h>
|
|
#include <linux/phy.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(struct fwnode_handle *fwnode)
|
|
|
|
|
|
+static inline bool is_pset_node(struct fwnode_handle *fwnode)
|
|
{
|
|
{
|
|
return fwnode && fwnode->type == FWNODE_PDATA;
|
|
return fwnode && fwnode->type == FWNODE_PDATA;
|
|
}
|
|
}
|
|
|
|
|
|
-static inline struct property_set *to_pset(struct fwnode_handle *fwnode)
|
|
|
|
|
|
+static inline struct property_set *to_pset_node(struct fwnode_handle *fwnode)
|
|
{
|
|
{
|
|
- return is_pset(fwnode) ?
|
|
|
|
|
|
+ return is_pset_node(fwnode) ?
|
|
container_of(fwnode, struct property_set, fwnode) : NULL;
|
|
container_of(fwnode, struct property_set, fwnode) : NULL;
|
|
}
|
|
}
|
|
|
|
|
|
@@ -63,45 +45,135 @@ static struct property_entry *pset_prop_get(struct property_set *pset,
|
|
return NULL;
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
|
|
-static int pset_prop_read_array(struct property_set *pset, const char *name,
|
|
|
|
- enum dev_prop_type type, void *val, size_t nval)
|
|
|
|
|
|
+static void *pset_prop_find(struct property_set *pset, const char *propname,
|
|
|
|
+ size_t length)
|
|
{
|
|
{
|
|
struct property_entry *prop;
|
|
struct property_entry *prop;
|
|
- unsigned int item_size;
|
|
|
|
|
|
+ void *pointer;
|
|
|
|
|
|
- prop = pset_prop_get(pset, name);
|
|
|
|
|
|
+ prop = pset_prop_get(pset, propname);
|
|
if (!prop)
|
|
if (!prop)
|
|
- return -ENODATA;
|
|
|
|
|
|
+ return ERR_PTR(-EINVAL);
|
|
|
|
+ if (prop->is_array)
|
|
|
|
+ pointer = prop->pointer.raw_data;
|
|
|
|
+ else
|
|
|
|
+ pointer = &prop->value.raw_data;
|
|
|
|
+ if (!pointer)
|
|
|
|
+ return ERR_PTR(-ENODATA);
|
|
|
|
+ if (length > prop->length)
|
|
|
|
+ return ERR_PTR(-EOVERFLOW);
|
|
|
|
+ return pointer;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static int pset_prop_read_u8_array(struct property_set *pset,
|
|
|
|
+ const char *propname,
|
|
|
|
+ u8 *values, size_t nval)
|
|
|
|
+{
|
|
|
|
+ void *pointer;
|
|
|
|
+ size_t length = nval * sizeof(*values);
|
|
|
|
+
|
|
|
|
+ pointer = pset_prop_find(pset, propname, length);
|
|
|
|
+ if (IS_ERR(pointer))
|
|
|
|
+ return PTR_ERR(pointer);
|
|
|
|
+
|
|
|
|
+ memcpy(values, pointer, length);
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static int pset_prop_read_u16_array(struct property_set *pset,
|
|
|
|
+ const char *propname,
|
|
|
|
+ u16 *values, size_t nval)
|
|
|
|
+{
|
|
|
|
+ void *pointer;
|
|
|
|
+ size_t length = nval * sizeof(*values);
|
|
|
|
+
|
|
|
|
+ pointer = pset_prop_find(pset, propname, length);
|
|
|
|
+ if (IS_ERR(pointer))
|
|
|
|
+ return PTR_ERR(pointer);
|
|
|
|
+
|
|
|
|
+ memcpy(values, pointer, length);
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static int pset_prop_read_u32_array(struct property_set *pset,
|
|
|
|
+ const char *propname,
|
|
|
|
+ u32 *values, size_t nval)
|
|
|
|
+{
|
|
|
|
+ void *pointer;
|
|
|
|
+ size_t length = nval * sizeof(*values);
|
|
|
|
+
|
|
|
|
+ pointer = pset_prop_find(pset, propname, length);
|
|
|
|
+ if (IS_ERR(pointer))
|
|
|
|
+ return PTR_ERR(pointer);
|
|
|
|
+
|
|
|
|
+ memcpy(values, pointer, length);
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static int pset_prop_read_u64_array(struct property_set *pset,
|
|
|
|
+ const char *propname,
|
|
|
|
+ u64 *values, size_t nval)
|
|
|
|
+{
|
|
|
|
+ void *pointer;
|
|
|
|
+ size_t length = nval * sizeof(*values);
|
|
|
|
+
|
|
|
|
+ pointer = pset_prop_find(pset, propname, length);
|
|
|
|
+ if (IS_ERR(pointer))
|
|
|
|
+ return PTR_ERR(pointer);
|
|
|
|
+
|
|
|
|
+ memcpy(values, pointer, length);
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static int pset_prop_count_elems_of_size(struct property_set *pset,
|
|
|
|
+ const char *propname, size_t length)
|
|
|
|
+{
|
|
|
|
+ struct property_entry *prop;
|
|
|
|
+
|
|
|
|
+ prop = pset_prop_get(pset, propname);
|
|
|
|
+ if (!prop)
|
|
|
|
+ return -EINVAL;
|
|
|
|
+
|
|
|
|
+ return prop->length / length;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static int pset_prop_read_string_array(struct property_set *pset,
|
|
|
|
+ const char *propname,
|
|
|
|
+ const char **strings, size_t nval)
|
|
|
|
+{
|
|
|
|
+ void *pointer;
|
|
|
|
+ size_t length = nval * sizeof(*strings);
|
|
|
|
+
|
|
|
|
+ pointer = pset_prop_find(pset, propname, length);
|
|
|
|
+ if (IS_ERR(pointer))
|
|
|
|
+ return PTR_ERR(pointer);
|
|
|
|
+
|
|
|
|
+ memcpy(strings, pointer, length);
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static int pset_prop_read_string(struct property_set *pset,
|
|
|
|
+ const char *propname, const char **strings)
|
|
|
|
+{
|
|
|
|
+ struct property_entry *prop;
|
|
|
|
+ const char **pointer;
|
|
|
|
|
|
- if (prop->type != type)
|
|
|
|
- return -EPROTO;
|
|
|
|
-
|
|
|
|
- if (!val)
|
|
|
|
- return prop->nval;
|
|
|
|
-
|
|
|
|
- if (prop->nval < nval)
|
|
|
|
- return -EOVERFLOW;
|
|
|
|
-
|
|
|
|
- switch (type) {
|
|
|
|
- case DEV_PROP_U8:
|
|
|
|
- item_size = sizeof(u8);
|
|
|
|
- break;
|
|
|
|
- case DEV_PROP_U16:
|
|
|
|
- item_size = sizeof(u16);
|
|
|
|
- break;
|
|
|
|
- case DEV_PROP_U32:
|
|
|
|
- item_size = sizeof(u32);
|
|
|
|
- break;
|
|
|
|
- case DEV_PROP_U64:
|
|
|
|
- item_size = sizeof(u64);
|
|
|
|
- break;
|
|
|
|
- case DEV_PROP_STRING:
|
|
|
|
- item_size = sizeof(const char *);
|
|
|
|
- break;
|
|
|
|
- default:
|
|
|
|
|
|
+ prop = pset_prop_get(pset, propname);
|
|
|
|
+ if (!prop)
|
|
return -EINVAL;
|
|
return -EINVAL;
|
|
|
|
+ if (!prop->is_string)
|
|
|
|
+ return -EILSEQ;
|
|
|
|
+ if (prop->is_array) {
|
|
|
|
+ pointer = prop->pointer.str;
|
|
|
|
+ if (!pointer)
|
|
|
|
+ return -ENODATA;
|
|
|
|
+ } else {
|
|
|
|
+ pointer = &prop->value.str;
|
|
|
|
+ if (*pointer && strnlen(*pointer, prop->length) >= prop->length)
|
|
|
|
+ return -EILSEQ;
|
|
}
|
|
}
|
|
- memcpy(val, prop->value.raw_data, nval * item_size);
|
|
|
|
|
|
+
|
|
|
|
+ *strings = *pointer;
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
@@ -124,6 +196,18 @@ bool device_property_present(struct device *dev, const char *propname)
|
|
}
|
|
}
|
|
EXPORT_SYMBOL_GPL(device_property_present);
|
|
EXPORT_SYMBOL_GPL(device_property_present);
|
|
|
|
|
|
|
|
+static bool __fwnode_property_present(struct fwnode_handle *fwnode,
|
|
|
|
+ const char *propname)
|
|
|
|
+{
|
|
|
|
+ if (is_of_node(fwnode))
|
|
|
|
+ return of_property_read_bool(to_of_node(fwnode), propname);
|
|
|
|
+ else if (is_acpi_node(fwnode))
|
|
|
|
+ return !acpi_node_prop_get(fwnode, propname, NULL);
|
|
|
|
+ else if (is_pset_node(fwnode))
|
|
|
|
+ return !!pset_prop_get(to_pset_node(fwnode), propname);
|
|
|
|
+ return false;
|
|
|
|
+}
|
|
|
|
+
|
|
/**
|
|
/**
|
|
* fwnode_property_present - check if a property of a firmware node is present
|
|
* fwnode_property_present - check if a property of a firmware node is present
|
|
* @fwnode: Firmware node whose property to check
|
|
* @fwnode: Firmware node whose property to check
|
|
@@ -131,12 +215,12 @@ EXPORT_SYMBOL_GPL(device_property_present);
|
|
*/
|
|
*/
|
|
bool fwnode_property_present(struct fwnode_handle *fwnode, const char *propname)
|
|
bool fwnode_property_present(struct fwnode_handle *fwnode, const char *propname)
|
|
{
|
|
{
|
|
- if (is_of_node(fwnode))
|
|
|
|
- return of_property_read_bool(to_of_node(fwnode), propname);
|
|
|
|
- else if (is_acpi_node(fwnode))
|
|
|
|
- return !acpi_node_prop_get(fwnode, propname, NULL);
|
|
|
|
|
|
+ bool ret;
|
|
|
|
|
|
- return !!pset_prop_get(to_pset(fwnode), propname);
|
|
|
|
|
|
+ ret = __fwnode_property_present(fwnode, propname);
|
|
|
|
+ if (ret == false && fwnode && fwnode->secondary)
|
|
|
|
+ ret = __fwnode_property_present(fwnode->secondary, propname);
|
|
|
|
+ return ret;
|
|
}
|
|
}
|
|
EXPORT_SYMBOL_GPL(fwnode_property_present);
|
|
EXPORT_SYMBOL_GPL(fwnode_property_present);
|
|
|
|
|
|
@@ -309,25 +393,40 @@ int device_property_match_string(struct device *dev, const char *propname,
|
|
}
|
|
}
|
|
EXPORT_SYMBOL_GPL(device_property_match_string);
|
|
EXPORT_SYMBOL_GPL(device_property_match_string);
|
|
|
|
|
|
-#define OF_DEV_PROP_READ_ARRAY(node, propname, type, val, nval) \
|
|
|
|
- (val) ? of_property_read_##type##_array((node), (propname), (val), (nval)) \
|
|
|
|
|
|
+#define OF_DEV_PROP_READ_ARRAY(node, propname, type, val, nval) \
|
|
|
|
+ (val) ? of_property_read_##type##_array((node), (propname), (val), (nval)) \
|
|
: of_property_count_elems_of_size((node), (propname), sizeof(type))
|
|
: of_property_count_elems_of_size((node), (propname), sizeof(type))
|
|
|
|
|
|
-#define FWNODE_PROP_READ_ARRAY(_fwnode_, _propname_, _type_, _proptype_, _val_, _nval_) \
|
|
|
|
-({ \
|
|
|
|
- int _ret_; \
|
|
|
|
- if (is_of_node(_fwnode_)) \
|
|
|
|
- _ret_ = OF_DEV_PROP_READ_ARRAY(to_of_node(_fwnode_), _propname_, \
|
|
|
|
- _type_, _val_, _nval_); \
|
|
|
|
- else if (is_acpi_node(_fwnode_)) \
|
|
|
|
- _ret_ = acpi_node_prop_read(_fwnode_, _propname_, _proptype_, \
|
|
|
|
- _val_, _nval_); \
|
|
|
|
- else if (is_pset(_fwnode_)) \
|
|
|
|
- _ret_ = pset_prop_read_array(to_pset(_fwnode_), _propname_, \
|
|
|
|
- _proptype_, _val_, _nval_); \
|
|
|
|
- else \
|
|
|
|
- _ret_ = -ENXIO; \
|
|
|
|
- _ret_; \
|
|
|
|
|
|
+#define PSET_PROP_READ_ARRAY(node, propname, type, val, nval) \
|
|
|
|
+ (val) ? pset_prop_read_##type##_array((node), (propname), (val), (nval)) \
|
|
|
|
+ : pset_prop_count_elems_of_size((node), (propname), sizeof(type))
|
|
|
|
+
|
|
|
|
+#define FWNODE_PROP_READ(_fwnode_, _propname_, _type_, _proptype_, _val_, _nval_) \
|
|
|
|
+({ \
|
|
|
|
+ int _ret_; \
|
|
|
|
+ if (is_of_node(_fwnode_)) \
|
|
|
|
+ _ret_ = OF_DEV_PROP_READ_ARRAY(to_of_node(_fwnode_), _propname_, \
|
|
|
|
+ _type_, _val_, _nval_); \
|
|
|
|
+ else if (is_acpi_node(_fwnode_)) \
|
|
|
|
+ _ret_ = acpi_node_prop_read(_fwnode_, _propname_, _proptype_, \
|
|
|
|
+ _val_, _nval_); \
|
|
|
|
+ else if (is_pset_node(_fwnode_)) \
|
|
|
|
+ _ret_ = PSET_PROP_READ_ARRAY(to_pset_node(_fwnode_), _propname_, \
|
|
|
|
+ _type_, _val_, _nval_); \
|
|
|
|
+ else \
|
|
|
|
+ _ret_ = -ENXIO; \
|
|
|
|
+ _ret_; \
|
|
|
|
+})
|
|
|
|
+
|
|
|
|
+#define FWNODE_PROP_READ_ARRAY(_fwnode_, _propname_, _type_, _proptype_, _val_, _nval_) \
|
|
|
|
+({ \
|
|
|
|
+ int _ret_; \
|
|
|
|
+ _ret_ = FWNODE_PROP_READ(_fwnode_, _propname_, _type_, _proptype_, \
|
|
|
|
+ _val_, _nval_); \
|
|
|
|
+ if (_ret_ == -EINVAL && _fwnode_ && _fwnode_->secondary) \
|
|
|
|
+ _ret_ = FWNODE_PROP_READ(_fwnode_->secondary, _propname_, _type_, \
|
|
|
|
+ _proptype_, _val_, _nval_); \
|
|
|
|
+ _ret_; \
|
|
})
|
|
})
|
|
|
|
|
|
/**
|
|
/**
|
|
@@ -434,6 +533,41 @@ int fwnode_property_read_u64_array(struct fwnode_handle *fwnode,
|
|
}
|
|
}
|
|
EXPORT_SYMBOL_GPL(fwnode_property_read_u64_array);
|
|
EXPORT_SYMBOL_GPL(fwnode_property_read_u64_array);
|
|
|
|
|
|
|
|
+static int __fwnode_property_read_string_array(struct fwnode_handle *fwnode,
|
|
|
|
+ const char *propname,
|
|
|
|
+ const char **val, size_t nval)
|
|
|
|
+{
|
|
|
|
+ if (is_of_node(fwnode))
|
|
|
|
+ return val ?
|
|
|
|
+ of_property_read_string_array(to_of_node(fwnode),
|
|
|
|
+ propname, val, nval) :
|
|
|
|
+ of_property_count_strings(to_of_node(fwnode), propname);
|
|
|
|
+ else if (is_acpi_node(fwnode))
|
|
|
|
+ return acpi_node_prop_read(fwnode, propname, DEV_PROP_STRING,
|
|
|
|
+ val, nval);
|
|
|
|
+ else if (is_pset_node(fwnode))
|
|
|
|
+ return val ?
|
|
|
|
+ pset_prop_read_string_array(to_pset_node(fwnode),
|
|
|
|
+ propname, val, nval) :
|
|
|
|
+ pset_prop_count_elems_of_size(to_pset_node(fwnode),
|
|
|
|
+ propname,
|
|
|
|
+ sizeof(const char *));
|
|
|
|
+ return -ENXIO;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static int __fwnode_property_read_string(struct fwnode_handle *fwnode,
|
|
|
|
+ const char *propname, const char **val)
|
|
|
|
+{
|
|
|
|
+ if (is_of_node(fwnode))
|
|
|
|
+ return of_property_read_string(to_of_node(fwnode), propname, val);
|
|
|
|
+ else if (is_acpi_node(fwnode))
|
|
|
|
+ return acpi_node_prop_read(fwnode, propname, DEV_PROP_STRING,
|
|
|
|
+ val, 1);
|
|
|
|
+ else if (is_pset_node(fwnode))
|
|
|
|
+ return pset_prop_read_string(to_pset_node(fwnode), propname, val);
|
|
|
|
+ return -ENXIO;
|
|
|
|
+}
|
|
|
|
+
|
|
/**
|
|
/**
|
|
* fwnode_property_read_string_array - return string array property of a node
|
|
* fwnode_property_read_string_array - return string array property of a node
|
|
* @fwnode: Firmware node to get the property of
|
|
* @fwnode: Firmware node to get the property of
|
|
@@ -456,18 +590,13 @@ int fwnode_property_read_string_array(struct fwnode_handle *fwnode,
|
|
const char *propname, const char **val,
|
|
const char *propname, const char **val,
|
|
size_t nval)
|
|
size_t nval)
|
|
{
|
|
{
|
|
- if (is_of_node(fwnode))
|
|
|
|
- return val ?
|
|
|
|
- of_property_read_string_array(to_of_node(fwnode),
|
|
|
|
- propname, val, nval) :
|
|
|
|
- of_property_count_strings(to_of_node(fwnode), propname);
|
|
|
|
- else if (is_acpi_node(fwnode))
|
|
|
|
- return acpi_node_prop_read(fwnode, propname, DEV_PROP_STRING,
|
|
|
|
- val, nval);
|
|
|
|
- else if (is_pset(fwnode))
|
|
|
|
- return pset_prop_read_array(to_pset(fwnode), propname,
|
|
|
|
- DEV_PROP_STRING, val, nval);
|
|
|
|
- return -ENXIO;
|
|
|
|
|
|
+ int ret;
|
|
|
|
+
|
|
|
|
+ ret = __fwnode_property_read_string_array(fwnode, propname, val, nval);
|
|
|
|
+ if (ret == -EINVAL && fwnode && fwnode->secondary)
|
|
|
|
+ ret = __fwnode_property_read_string_array(fwnode->secondary,
|
|
|
|
+ propname, val, nval);
|
|
|
|
+ return ret;
|
|
}
|
|
}
|
|
EXPORT_SYMBOL_GPL(fwnode_property_read_string_array);
|
|
EXPORT_SYMBOL_GPL(fwnode_property_read_string_array);
|
|
|
|
|
|
@@ -489,14 +618,13 @@ EXPORT_SYMBOL_GPL(fwnode_property_read_string_array);
|
|
int fwnode_property_read_string(struct fwnode_handle *fwnode,
|
|
int fwnode_property_read_string(struct fwnode_handle *fwnode,
|
|
const char *propname, const char **val)
|
|
const char *propname, const char **val)
|
|
{
|
|
{
|
|
- if (is_of_node(fwnode))
|
|
|
|
- return of_property_read_string(to_of_node(fwnode), propname, val);
|
|
|
|
- else if (is_acpi_node(fwnode))
|
|
|
|
- return acpi_node_prop_read(fwnode, propname, DEV_PROP_STRING,
|
|
|
|
- val, 1);
|
|
|
|
|
|
+ int ret;
|
|
|
|
|
|
- return pset_prop_read_array(to_pset(fwnode), propname,
|
|
|
|
- DEV_PROP_STRING, val, 1);
|
|
|
|
|
|
+ ret = __fwnode_property_read_string(fwnode, propname, val);
|
|
|
|
+ if (ret == -EINVAL && fwnode && fwnode->secondary)
|
|
|
|
+ ret = __fwnode_property_read_string(fwnode->secondary,
|
|
|
|
+ propname, val);
|
|
|
|
+ return ret;
|
|
}
|
|
}
|
|
EXPORT_SYMBOL_GPL(fwnode_property_read_string);
|
|
EXPORT_SYMBOL_GPL(fwnode_property_read_string);
|
|
|
|
|
|
@@ -525,6 +653,9 @@ int fwnode_property_match_string(struct fwnode_handle *fwnode,
|
|
if (nval < 0)
|
|
if (nval < 0)
|
|
return nval;
|
|
return nval;
|
|
|
|
|
|
|
|
+ if (nval == 0)
|
|
|
|
+ return -ENODATA;
|
|
|
|
+
|
|
values = kcalloc(nval, sizeof(*values), GFP_KERNEL);
|
|
values = kcalloc(nval, sizeof(*values), GFP_KERNEL);
|
|
if (!values)
|
|
if (!values)
|
|
return -ENOMEM;
|
|
return -ENOMEM;
|
|
@@ -546,6 +677,182 @@ out:
|
|
}
|
|
}
|
|
EXPORT_SYMBOL_GPL(fwnode_property_match_string);
|
|
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->length)
|
|
|
|
+ return -ENODATA;
|
|
|
|
+
|
|
|
|
+ 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
|
|
* device_get_next_child_node - Return the next child node handle for a device
|
|
* @dev: Device to find the next child node for.
|
|
* @dev: Device to find the next child node for.
|