diff options
Diffstat (limited to 'drivers/media/platform/tegra/camera.c')
-rw-r--r-- | drivers/media/platform/tegra/camera.c | 85 |
1 files changed, 73 insertions, 12 deletions
diff --git a/drivers/media/platform/tegra/camera.c b/drivers/media/platform/tegra/camera.c index 2542f4210de6..9017e7fcd2cc 100644 --- a/drivers/media/platform/tegra/camera.c +++ b/drivers/media/platform/tegra/camera.c @@ -1,7 +1,7 @@ /* * camera.c - generic camera device driver * - * Copyright (c) 2013-2014, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2013-2016, NVIDIA CORPORATION. All rights reserved. * * Contributors: * Charlie Huang <chahuang@nvidia.com> @@ -131,12 +131,12 @@ int camera_copy_user_params(unsigned long arg, struct nvc_param *prm) } #endif -int camera_get_params( +int __camera_get_params( struct camera_info *cam, unsigned long arg, int u_size, - struct nvc_param *prm, void **data) + struct nvc_param *prm, void **data, bool zero_size_ok) { void *buf; - unsigned size; + size_t size; #ifdef CONFIG_COMPAT memset(prm, 0, sizeof(*prm)); @@ -156,9 +156,14 @@ int camera_get_params( if (!data) return 0; + if (zero_size_ok && prm->sizeofvalue == 0) { + *data = ZERO_SIZE_PTR; + return 0; + } + size = prm->sizeofvalue * u_size; - buf = kzalloc(size, GFP_KERNEL); - if (!buf) { + buf = kcalloc(prm->sizeofvalue, u_size, GFP_KERNEL); + if (ZERO_OR_NULL_PTR(buf)) { dev_err(cam->dev, "%s allocate memory failed!\n", __func__); return -ENOMEM; } @@ -173,6 +178,20 @@ int camera_get_params( return 0; } +static int camera_validate_p_i2c_table(struct camera_info *cam, + const struct nvc_param *params, + const struct camera_reg *p_i2c_table, const char *caller) +{ + u32 idx, last_idx = params->sizeofvalue / sizeof(p_i2c_table[0]); + + for (idx = 0; idx < last_idx; idx++) + if (p_i2c_table[idx].addr == CAMERA_TABLE_END) + return 0; + + dev_err(cam->dev, "%s: table is not properly terminated\n", caller); + return -EINVAL; +} + static int camera_seq_rd(struct camera_info *cam, unsigned long arg) { struct nvc_param params; @@ -184,6 +203,10 @@ static int camera_seq_rd(struct camera_info *cam, unsigned long arg) if (err) return err; + err = camera_validate_p_i2c_table(cam, ¶ms, p_i2c_table, __func__); + if (err) + goto seq_rd_end; + err = camera_dev_rd_table(cam->cdev, p_i2c_table); if (!err && copy_to_user(MAKE_USER_PTR(params.p_value), p_i2c_table, params.sizeofvalue)) { @@ -192,6 +215,7 @@ static int camera_seq_rd(struct camera_info *cam, unsigned long arg) err = -EINVAL; } +seq_rd_end: kfree(p_i2c_table); return err; } @@ -231,7 +255,7 @@ static int camera_seq_wr(struct camera_info *cam, unsigned long arg) } p_i2c_table = devm_kzalloc(cdev->dev, params.sizeofvalue, GFP_KERNEL); - if (p_i2c_table == NULL) { + if (ZERO_OR_NULL_PTR(p_i2c_table)) { dev_err(cam->dev, "%s devm_kzalloc err line %d\n", __func__, __LINE__); return -ENOMEM; @@ -246,6 +270,10 @@ static int camera_seq_wr(struct camera_info *cam, unsigned long arg) goto seq_wr_end; } + err = camera_validate_p_i2c_table(cam, ¶ms, p_i2c_table, __func__); + if (err) + goto seq_wr_end; + switch (params.param) { case CAMERA_SEQ_REGISTER_EXEC: case CAMERA_SEQ_REGISTER_ONLY: @@ -498,13 +526,13 @@ static int camera_new_device(struct camera_info *cam, unsigned long arg) next_dev->client->addr == dev_info.addr) { dev_dbg(cam_desc.dev, "%s: device already exists.\n", __func__); - camera_remove_device(new_dev, false); if (atomic_xchg(&next_dev->in_use, 1)) { dev_err(cam_desc.dev, "%s device %s BUSY\n", __func__, next_dev->name); err = -EBUSY; goto new_device_err; - } + } else + camera_remove_device(new_dev, false); new_dev = next_dev; goto new_device_done; } @@ -586,7 +614,8 @@ static int camera_update(struct camera_info *cam, unsigned long arg) return err; } - err = camera_get_params(cam, arg, sizeof(*upd), ¶m, (void **)&upd); + err = __camera_get_params(cam, arg, sizeof(*upd), ¶m, (void **)&upd, + true); if (err) return err; @@ -657,9 +686,20 @@ static int camera_layout_get(struct camera_info *cam, unsigned long arg) if (err) return err; + if (param.variant > MAX_PARAM_VARIANT) { + dev_err(cam->dev, "%s param variant is too large: %u\n", + __func__, param.variant); + return -EINVAL; + } + if (param.sizeofvalue > MAX_PARAM_SIZE_OF_VALUE) { + dev_err(cam->dev, "%s size of param value is too large: %u\n", + __func__, param.sizeofvalue); + return -EINVAL; + } + len = (int)cam_desc.size_layout - param.variant; if (len <= 0) { - dev_err(cam->dev, "%s invalid offset %d\n", + dev_err(cam->dev, "%s invalid offset %u\n", __func__, param.variant); err = -EINVAL; goto getlayout_end; @@ -838,31 +878,46 @@ static long camera_ioctl(struct file *file, break; case PCLLK_IOCTL_DEV_DEL: mutex_lock(cam_desc.d_mutex); + if (!cam->cdev) { + err = -ENODEV; + mutex_unlock(cam_desc.d_mutex); + break; + } list_del(&cam->cdev->list); - mutex_unlock(cam_desc.d_mutex); camera_remove_device(cam->cdev, true); + mutex_unlock(cam_desc.d_mutex); break; case PCLLK_IOCTL_DEV_FREE: err = camera_free_device(cam, arg); break; case PCLLK_IOCTL_SEQ_WR: + mutex_lock(cam_desc.d_mutex); err = camera_seq_wr(cam, arg); + mutex_unlock(cam_desc.d_mutex); break; case PCLLK_IOCTL_SEQ_RD: + mutex_lock(cam_desc.d_mutex); err = camera_seq_rd(cam, arg); + mutex_unlock(cam_desc.d_mutex); break; case PCLLK_IOCTL_PARAM_RD: /* err = camera_param_rd(cam, arg); */ break; case PCLLK_IOCTL_PWR_WR: /* This is a Guaranteed Level of Service (GLOS) call */ + mutex_lock(cam_desc.d_mutex); err = camera_dev_pwr_set(cam, arg); + mutex_unlock(cam_desc.d_mutex); break; case PCLLK_IOCTL_PWR_RD: + mutex_lock(cam_desc.d_mutex); err = camera_dev_pwr_get(cam, arg); + mutex_unlock(cam_desc.d_mutex); break; case PCLLK_IOCTL_UPDATE: + mutex_lock(cam_desc.d_mutex); err = camera_update(cam, arg); + mutex_unlock(cam_desc.d_mutex); break; case PCLLK_IOCTL_LAYOUT_WR: err = camera_layout_update(cam, arg); @@ -884,16 +939,22 @@ static long camera_ioctl(struct file *file, err = virtual_device_add(cam_desc.dev, arg); break; case PCLLK_IOCTL_32_SEQ_WR: + mutex_lock(cam_desc.d_mutex); err = camera_seq_wr(cam, arg); + mutex_unlock(cam_desc.d_mutex); break; case PCLLK_IOCTL_32_SEQ_RD: + mutex_lock(cam_desc.d_mutex); err = camera_seq_rd(cam, arg); + mutex_unlock(cam_desc.d_mutex); break; case PCLLK_IOCTL_32_PARAM_RD: /* err = camera_param_rd(cam, arg); */ break; case PCLLK_IOCTL_32_UPDATE: + mutex_lock(cam_desc.d_mutex); err = camera_update(cam, arg); + mutex_unlock(cam_desc.d_mutex); break; case PCLLK_IOCTL_32_LAYOUT_WR: err = camera_layout_update(cam, arg); |