Parse and read the link-frequencies property in v4l2_of_parse_endpoint(). The property is an u64 array of undefined length, thus the memory allocation may fail, leading - v4l2_of_parse_endpoint() to return an error in such a case (as well as when failing to parse the property) and - to requiring releasing the memory reserved for the array (v4l2_of_release_endpoint()). If a driver does not need to access properties that require memory allocation (such as link-frequencies), it may choose to call v4l2_of_release_endpoint() right after calling v4l2_of_parse_endpoint(). Signed-off-by: Sakari Ailus <sakari.ailus@xxxxxx> --- drivers/media/i2c/adv7604.c | 1 + drivers/media/i2c/s5c73m3/s5c73m3-core.c | 1 + drivers/media/i2c/s5k5baf.c | 1 + drivers/media/i2c/smiapp/smiapp-core.c | 32 ++++++++-------- drivers/media/i2c/tvp514x.c | 1 + drivers/media/i2c/tvp7002.c | 1 + drivers/media/platform/am437x/am437x-vpfe.c | 1 + drivers/media/platform/exynos4-is/media-dev.c | 1 + drivers/media/platform/exynos4-is/mipi-csis.c | 1 + drivers/media/platform/soc_camera/atmel-isi.c | 1 + drivers/media/platform/soc_camera/pxa_camera.c | 1 + drivers/media/platform/soc_camera/rcar_vin.c | 1 + drivers/media/v4l2-core/v4l2-of.c | 47 +++++++++++++++++++++++- include/media/v4l2-of.h | 9 +++++ 14 files changed, 81 insertions(+), 18 deletions(-) diff --git a/drivers/media/i2c/adv7604.c b/drivers/media/i2c/adv7604.c index aaab9c9..9c6c2a5 100644 --- a/drivers/media/i2c/adv7604.c +++ b/drivers/media/i2c/adv7604.c @@ -2624,6 +2624,7 @@ static int adv76xx_parse_dt(struct adv76xx_state *state) return -EINVAL; v4l2_of_parse_endpoint(endpoint, &bus_cfg); + v4l2_of_release_endpoint(&bus_cfg); of_node_put(endpoint); flags = bus_cfg.bus.parallel.flags; diff --git a/drivers/media/i2c/s5c73m3/s5c73m3-core.c b/drivers/media/i2c/s5c73m3/s5c73m3-core.c index ee0f57e..f248a47 100644 --- a/drivers/media/i2c/s5c73m3/s5c73m3-core.c +++ b/drivers/media/i2c/s5c73m3/s5c73m3-core.c @@ -1624,6 +1624,7 @@ static int s5c73m3_get_platform_data(struct s5c73m3 *state) } v4l2_of_parse_endpoint(node_ep, &ep); + v4l2_of_release_endpoint(&ep); of_node_put(node_ep); if (ep.bus_type != V4L2_MBUS_CSI2) { diff --git a/drivers/media/i2c/s5k5baf.c b/drivers/media/i2c/s5k5baf.c index a3d7d03..222cd62 100644 --- a/drivers/media/i2c/s5k5baf.c +++ b/drivers/media/i2c/s5k5baf.c @@ -1867,6 +1867,7 @@ static int s5k5baf_parse_device_node(struct s5k5baf *state, struct device *dev) } v4l2_of_parse_endpoint(node_ep, &ep); + v4l2_of_release_endpoint(&ep); of_node_put(node_ep); state->bus_type = ep.bus_type; diff --git a/drivers/media/i2c/smiapp/smiapp-core.c b/drivers/media/i2c/smiapp/smiapp-core.c index ecae76b..2b3cd9e 100644 --- a/drivers/media/i2c/smiapp/smiapp-core.c +++ b/drivers/media/i2c/smiapp/smiapp-core.c @@ -2977,7 +2977,7 @@ static struct smiapp_platform_data *smiapp_get_pdata(struct device *dev) struct smiapp_platform_data *pdata; struct v4l2_of_endpoint bus_cfg; struct device_node *ep; - uint32_t asize; + int i; int rval; if (!dev->of_node) @@ -2987,12 +2987,14 @@ static struct smiapp_platform_data *smiapp_get_pdata(struct device *dev) if (!ep) return NULL; + rval = v4l2_of_parse_endpoint(ep, &bus_cfg); + if (rval < 0) + goto out_err; + pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); if (!pdata) goto out_err; - v4l2_of_parse_endpoint(ep, &bus_cfg); - switch (bus_cfg.bus_type) { case V4L2_MBUS_CSI2: pdata->csi_signalling_mode = SMIAPP_CSI_SIGNALLING_MODE_CSI2; @@ -3022,34 +3024,30 @@ static struct smiapp_platform_data *smiapp_get_pdata(struct device *dev) dev_dbg(dev, "reset %d, nvm %d, clk %d, csi %d\n", pdata->xshutdown, pdata->nvm_size, pdata->ext_clk, pdata->csi_signalling_mode); - rval = of_get_property(ep, "link-frequencies", &asize) ? 0 : -ENOENT; - if (rval) { - dev_warn(dev, "can't get link-frequencies array size\n"); + if (!bus_cfg.nr_of_link_frequencies) { + dev_warn(dev, "no link frequencies defined\n"); goto out_err; } - pdata->op_sys_clock = devm_kzalloc(dev, asize, GFP_KERNEL); + pdata->op_sys_clock = devm_kmalloc_array( + dev, bus_cfg.nr_of_link_frequencies + 1 /* guardian */, + sizeof(*bus_cfg.link_frequencies), GFP_KERNEL); if (!pdata->op_sys_clock) { rval = -ENOMEM; goto out_err; } - asize /= sizeof(*pdata->op_sys_clock); - rval = of_property_read_u64_array( - ep, "link-frequencies", pdata->op_sys_clock, asize); - if (rval) { - dev_warn(dev, "can't get link-frequencies\n"); - goto out_err; + for (i = 0; i < bus_cfg.nr_of_link_frequencies; i++) { + pdata->op_sys_clock[i] = bus_cfg.link_frequencies[i]; + dev_dbg(dev, "freq %d: %lld\n", i, pdata->op_sys_clock[i]); } - for (; asize > 0; asize--) - dev_dbg(dev, "freq %d: %lld\n", asize - 1, - pdata->op_sys_clock[asize - 1]); - + v4l2_of_release_endpoint(&bus_cfg); of_node_put(ep); return pdata; out_err: + v4l2_of_release_endpoint(&bus_cfg); of_node_put(ep); return NULL; } diff --git a/drivers/media/i2c/tvp514x.c b/drivers/media/i2c/tvp514x.c index c6b3dc5..91d7dc9 100644 --- a/drivers/media/i2c/tvp514x.c +++ b/drivers/media/i2c/tvp514x.c @@ -1078,6 +1078,7 @@ tvp514x_get_pdata(struct i2c_client *client) goto done; v4l2_of_parse_endpoint(endpoint, &bus_cfg); + v4l2_of_release_endpoint(&bus_cfg); flags = bus_cfg.bus.parallel.flags; if (flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH) diff --git a/drivers/media/i2c/tvp7002.c b/drivers/media/i2c/tvp7002.c index 9233194..70119ea 100644 --- a/drivers/media/i2c/tvp7002.c +++ b/drivers/media/i2c/tvp7002.c @@ -965,6 +965,7 @@ tvp7002_get_pdata(struct i2c_client *client) goto done; v4l2_of_parse_endpoint(endpoint, &bus_cfg); + v4l2_of_release_endpoint(&bus_cfg); flags = bus_cfg.bus.parallel.flags; if (flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH) diff --git a/drivers/media/platform/am437x/am437x-vpfe.c b/drivers/media/platform/am437x/am437x-vpfe.c index 56a5cb0..19ec20a 100644 --- a/drivers/media/platform/am437x/am437x-vpfe.c +++ b/drivers/media/platform/am437x/am437x-vpfe.c @@ -2469,6 +2469,7 @@ vpfe_get_pdata(struct platform_device *pdev) } err = v4l2_of_parse_endpoint(endpoint, &bus_cfg); + v4l2_of_release_endpoint(&bus_cfg); if (err) { dev_err(&pdev->dev, "Could not parse the endpoint\n"); goto done; diff --git a/drivers/media/platform/exynos4-is/media-dev.c b/drivers/media/platform/exynos4-is/media-dev.c index f315ef9..4d28305 100644 --- a/drivers/media/platform/exynos4-is/media-dev.c +++ b/drivers/media/platform/exynos4-is/media-dev.c @@ -340,6 +340,7 @@ static int fimc_md_parse_port_node(struct fimc_md *fmd, return 0; v4l2_of_parse_endpoint(ep, &endpoint); + v4l2_of_release_endpoint(&endpoint); if (WARN_ON(endpoint.base.port == 0) || index >= FIMC_MAX_SENSORS) return -EINVAL; diff --git a/drivers/media/platform/exynos4-is/mipi-csis.c b/drivers/media/platform/exynos4-is/mipi-csis.c index 2504aa8..03bf468 100644 --- a/drivers/media/platform/exynos4-is/mipi-csis.c +++ b/drivers/media/platform/exynos4-is/mipi-csis.c @@ -751,6 +751,7 @@ static int s5pcsis_parse_dt(struct platform_device *pdev, } /* Get port node and validate MIPI-CSI channel id. */ v4l2_of_parse_endpoint(node, &endpoint); + v4l2_of_release_endpoint(&endpoint); state->index = endpoint.base.port - FIMC_INPUT_MIPI_CSI2_0; if (state->index >= CSIS_MAX_ENTITIES) diff --git a/drivers/media/platform/soc_camera/atmel-isi.c b/drivers/media/platform/soc_camera/atmel-isi.c index 8526bf5..245e3a4 100644 --- a/drivers/media/platform/soc_camera/atmel-isi.c +++ b/drivers/media/platform/soc_camera/atmel-isi.c @@ -900,6 +900,7 @@ static int atmel_isi_probe_dt(struct atmel_isi *isi, } err = v4l2_of_parse_endpoint(np, &ep); + v4l2_of_release_endpoint(&ep); if (err) { dev_err(&pdev->dev, "Could not parse the endpoint\n"); goto err_probe_dt; diff --git a/drivers/media/platform/soc_camera/pxa_camera.c b/drivers/media/platform/soc_camera/pxa_camera.c index 8d6e343..1e37b9b 100644 --- a/drivers/media/platform/soc_camera/pxa_camera.c +++ b/drivers/media/platform/soc_camera/pxa_camera.c @@ -1672,6 +1672,7 @@ static int pxa_camera_pdata_from_dt(struct device *dev, } err = v4l2_of_parse_endpoint(np, &ep); + v4l2_of_release_endpoint(&ep); if (err) { dev_err(dev, "could not parse endpoint\n"); goto out; diff --git a/drivers/media/platform/soc_camera/rcar_vin.c b/drivers/media/platform/soc_camera/rcar_vin.c index 279ab9f..cf0a328 100644 --- a/drivers/media/platform/soc_camera/rcar_vin.c +++ b/drivers/media/platform/soc_camera/rcar_vin.c @@ -1863,6 +1863,7 @@ static int rcar_vin_probe(struct platform_device *pdev) } ret = v4l2_of_parse_endpoint(np, &ep); + v4l2_of_release_endpoint(&ep); if (ret) { dev_err(&pdev->dev, "could not parse endpoint\n"); return ret; diff --git a/drivers/media/v4l2-core/v4l2-of.c b/drivers/media/v4l2-core/v4l2-of.c index b4ed9a9..e24610c 100644 --- a/drivers/media/v4l2-core/v4l2-of.c +++ b/drivers/media/v4l2-core/v4l2-of.c @@ -14,6 +14,7 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/of.h> +#include <linux/slab.h> #include <linux/string.h> #include <linux/types.h> @@ -109,6 +110,26 @@ static void v4l2_of_parse_parallel_bus(const struct device_node *node, } /** + * v4l2_of_release_endpoint() - release resources acquired by + * v4l2_of_parse_endpoint() + * @endpoint - the endpoint the resources of which are to be released + * + * It is safe to call this function on an endpoint which is not parsed or + * and endpoint the parsing of which failed. However in the former case the + * argument must point to a struct the memory of which has been set to zero. + * + * Values in the struct v4l2_of_endpoint that are not connected to resources + * acquired by v4l2_of_parse_endpoint() are guaranteed to remain untouched. + */ +void v4l2_of_release_endpoint(struct v4l2_of_endpoint *endpoint) +{ + kfree(endpoint->link_frequencies); + endpoint->link_frequencies = NULL; + endpoint->nr_of_link_frequencies = 0; +} +EXPORT_SYMBOL(v4l2_of_parse_endpoint); + +/** * v4l2_of_parse_endpoint() - parse all endpoint node properties * @node: pointer to endpoint device_node * @endpoint: pointer to the V4L2 OF endpoint data structure @@ -122,15 +143,39 @@ static void v4l2_of_parse_parallel_bus(const struct device_node *node, * V4L2_MBUS_CSI2_CONTINUOUS_CLOCK flag. * The caller should hold a reference to @node. * + * An endpoint parsed using v4l2_of_parse_endpoint() must be released using + * v4l2_of_release_endpoint(). + * * Return: 0. */ int v4l2_of_parse_endpoint(const struct device_node *node, struct v4l2_of_endpoint *endpoint) { + int len; + of_graph_parse_endpoint(node, &endpoint->base); endpoint->bus_type = 0; memset(&endpoint->bus, 0, sizeof(endpoint->bus)); + if (of_get_property(node, "link-frequencies", &len)) { + int rval; + + endpoint->link_frequencies = kmalloc(len, GFP_KERNEL); + if (!endpoint->link_frequencies) + return -ENOMEM; + + endpoint->nr_of_link_frequencies = + len / sizeof(*endpoint->link_frequencies); + + rval = of_property_read_u64_array( + node, "link-frequencies", endpoint->link_frequencies, + endpoint->nr_of_link_frequencies); + if (rval < 0) { + v4l2_of_release_endpoint(endpoint); + return rval; + } + } + v4l2_of_parse_csi_bus(node, endpoint); /* * Parse the parallel video bus properties only if none @@ -141,4 +186,4 @@ int v4l2_of_parse_endpoint(const struct device_node *node, return 0; } -EXPORT_SYMBOL(v4l2_of_parse_endpoint); +EXPORT_SYMBOL(v4l2_of_release_endpoint); diff --git a/include/media/v4l2-of.h b/include/media/v4l2-of.h index 70fa7b7..8c123ff 100644 --- a/include/media/v4l2-of.h +++ b/include/media/v4l2-of.h @@ -54,6 +54,8 @@ struct v4l2_of_bus_parallel { * @base: struct of_endpoint containing port, id, and local of_node * @bus_type: bus type * @bus: bus configuration data structure + * @link_frequencies: array of supported link frequencies + * @nr_of_link_frequencies: number of elements in link_frequenccies array * @head: list head for this structure */ struct v4l2_of_endpoint { @@ -63,12 +65,15 @@ struct v4l2_of_endpoint { struct v4l2_of_bus_parallel parallel; struct v4l2_of_bus_mipi_csi2 mipi_csi2; } bus; + u64 *link_frequencies; + unsigned int nr_of_link_frequencies; struct list_head head; }; #ifdef CONFIG_OF int v4l2_of_parse_endpoint(const struct device_node *node, struct v4l2_of_endpoint *endpoint); +void v4l2_of_release_endpoint(struct v4l2_of_endpoint *endpoint); #else /* CONFIG_OF */ static inline int v4l2_of_parse_endpoint(const struct device_node *node, @@ -77,6 +82,10 @@ static inline int v4l2_of_parse_endpoint(const struct device_node *node, return -ENOSYS; } +static void v4l2_of_release_endpoint(struct v4l2_of_endpoint *endpoint) +{ +} + #endif /* CONFIG_OF */ #endif /* _V4L2_OF_H */ -- 1.7.10.4 -- To unsubscribe from this list: send the line "unsubscribe linux-media" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html