A use-after-free bug occures when unbinding the device while it streams. The 'struct vimc_ent_device' allocated for the 'Sensor A' is freed when calling the sensor's 'rm' callback but the freed pointer is later accessed in the function 'vimc_streamer_pipeline_terminate'. To fix this bug, move the release callback of the vimc entities and vimc_device to the release callback of v4l2_device. The .rm callback of vimc_ent_config is replaced by two callbacks: .unregister - this is called upon removing the device and it unregisters the entity. This is an optional callback since subdevices don't need to implement it because they are already unregistered in v4l2_device_unregister. .release - this is called from the release callback of v4l2_device and it frees the entity. This ensures that the entities will be released when the last fh of any of the devices is closed. The commands that cause the crash and the KASAN report: media-ctl -d platform:vimc -V '"Sensor A":0[fmt:SBGGR8_1X8/640x480]' media-ctl -d platform:vimc -V '"Debayer A":0[fmt:SBGGR8_1X8/640x480]' v4l2-ctl -z platform:vimc -d "RGB/YUV Capture" -v width=1920,height=1440 v4l2-ctl -z platform:vimc -d "Raw Capture 0" -v pixelformat=BA81 v4l2-ctl --stream-mmap --stream-count=1000 -d /dev/video2 & sleep 1 echo -n vimc.0 >/sys/bus/platform/drivers/vimc/unbind [ 188.417934] BUG: KASAN: use-after-free in vimc_streamer_pipeline_terminate+0x75/0x140 [vimc] [ 188.420182] Read of size 8 at addr ffff8881e9c26008 by task bash/185 [ 188.421800] [ 188.422223] CPU: 0 PID: 185 Comm: bash Not tainted 5.5.0-rc1+ #1 [ 188.423681] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.12.0-1 04/01/2014 [ 188.425938] Call Trace: [ 188.426610] dump_stack+0x75/0xa0 [ 188.427519] ? vimc_streamer_pipeline_terminate+0x75/0x140 [vimc] [ 188.429057] print_address_description.constprop.6+0x16/0x220 [ 188.430462] ? vimc_streamer_pipeline_terminate+0x75/0x140 [vimc] [ 188.431979] ? vimc_streamer_pipeline_terminate+0x75/0x140 [vimc] [ 188.433455] __kasan_report.cold.9+0x1a/0x40 [ 188.434518] ? vimc_streamer_pipeline_terminate+0x75/0x140 [vimc] [ 188.436010] kasan_report+0xe/0x20 [ 188.436859] vimc_streamer_pipeline_terminate+0x75/0x140 [vimc] [ 188.438339] vimc_streamer_s_stream+0x8b/0x3c0 [vimc] [ 188.439576] vimc_cap_stop_streaming+0x22/0x40 [vimc] [ 188.440863] __vb2_queue_cancel+0x65/0x560 [videobuf2_common] [ 188.442391] vb2_core_queue_release+0x19/0x50 [videobuf2_common] [ 188.443974] vimc_cap_rm+0x10/0x20 [vimc] [ 188.444986] vimc_rm_subdevs+0x9e/0xe0 [vimc] [ 188.446179] vimc_remove+0x19/0x70 [vimc] [ 188.447301] platform_drv_remove+0x2f/0x50 [ 188.448468] device_release_driver_internal+0x133/0x260 [ 188.449814] unbind_store+0x121/0x150 [ 188.450726] kernfs_fop_write+0x142/0x230 [ 188.451724] ? sysfs_kf_bin_read+0x100/0x100 [ 188.452826] vfs_write+0xdc/0x230 [ 188.453760] ksys_write+0xaf/0x140 [ 188.454702] ? __ia32_sys_read+0x40/0x40 [ 188.455773] ? __do_page_fault+0x473/0x620 [ 188.456780] do_syscall_64+0x5e/0x1a0 [ 188.457711] entry_SYSCALL_64_after_hwframe+0x44/0xa9 [ 188.459079] RIP: 0033:0x7f80f1f13504 [ 188.459969] Code: 00 f7 d8 64 89 02 48 c7 c0 ff ff ff ff eb b3 0f 1f 80 00 00 00 00 48 8d 05 f9 61 0d 00 8b 00 85 c0 75 13 b8 01 00 00 00 0f 05 <48> 3d 00 f0 ff ff 77 54 c3 0f 1f 00 41 54 49 89 d4 55 48 89 f5 53 [ 188.464445] RSP: 002b:00007ffd7e843b58 EFLAGS: 00000246 ORIG_RAX: 0000000000000001 [ 188.466276] RAX: ffffffffffffffda RBX: 0000000000000006 RCX: 00007f80f1f13504 [ 188.467999] RDX: 0000000000000006 RSI: 000055ef2eb21b10 RDI: 0000000000000001 [ 188.469708] RBP: 000055ef2eb21b10 R08: 00007f80f1fe68c0 R09: 00007f80f1e26740 [ 188.471407] R10: 000055ef2eade010 R11: 0000000000000246 R12: 00007f80f1fe5760 [ 188.473381] R13: 0000000000000006 R14: 00007f80f1fe0760 R15: 0000000000000006 [ 188.475107] [ 188.475500] Allocated by task 473: [ 188.476351] save_stack+0x19/0x80 [ 188.477201] __kasan_kmalloc.constprop.6+0xc1/0xd0 [ 188.478507] vimc_sen_add+0x36/0x309 [vimc] [ 188.479649] vimc_probe+0x1e2/0x530 [vimc] [ 188.480776] platform_drv_probe+0x46/0xa0 [ 188.481829] really_probe+0x16c/0x520 [ 188.482732] driver_probe_device+0x114/0x170 [ 188.483783] device_driver_attach+0x85/0x90 [ 188.484800] __driver_attach+0xa8/0x190 [ 188.485734] bus_for_each_dev+0xe4/0x140 [ 188.486702] bus_add_driver+0x223/0x2d0 [ 188.487715] driver_register+0xca/0x140 [ 188.488767] 0xffffffffc037003d [ 188.489635] do_one_initcall+0x86/0x28f [ 188.490702] do_init_module+0xf8/0x340 [ 188.491773] load_module+0x3766/0x3a10 [ 188.492811] __do_sys_finit_module+0x11a/0x1b0 [ 188.494059] do_syscall_64+0x5e/0x1a0 [ 188.495079] entry_SYSCALL_64_after_hwframe+0x44/0xa9 [ 188.496481] [ 188.496893] Freed by task 185: [ 188.497670] save_stack+0x19/0x80 [ 188.498493] __kasan_slab_free+0x125/0x170 [ 188.499486] kfree+0x8c/0x230 [ 188.500254] v4l2_subdev_release+0x64/0x70 [videodev] [ 188.501498] v4l2_device_release_subdev_node+0x1c/0x30 [videodev] [ 188.502976] device_release+0x3c/0xd0 [ 188.503867] kobject_put+0xf4/0x240 [ 188.507802] vimc_rm_subdevs+0x9e/0xe0 [vimc] [ 188.508846] vimc_remove+0x19/0x70 [vimc] [ 188.509792] platform_drv_remove+0x2f/0x50 [ 188.510752] device_release_driver_internal+0x133/0x260 [ 188.512006] unbind_store+0x121/0x150 [ 188.512899] kernfs_fop_write+0x142/0x230 [ 188.513874] vfs_write+0xdc/0x230 [ 188.514698] ksys_write+0xaf/0x140 [ 188.515523] do_syscall_64+0x5e/0x1a0 [ 188.516543] entry_SYSCALL_64_after_hwframe+0x44/0xa9 [ 188.517710] [ 188.518034] The buggy address belongs to the object at ffff8881e9c26000 [ 188.518034] which belongs to the cache kmalloc-4k of size 4096 [ 188.520528] The buggy address is located 8 bytes inside of [ 188.520528] 4096-byte region [ffff8881e9c26000, ffff8881e9c27000) [ 188.523015] The buggy address belongs to the page: [ 188.524357] page:ffffea0007a70800 refcount:1 mapcount:0 mapping:ffff8881f6402140 index:0x0 compound_mapcount: 0 [ 188.527058] raw: 0200000000010200 dead000000000100 dead000000000122 ffff8881f6402140 [ 188.528983] raw: 0000000000000000 0000000000040004 00000001ffffffff 0000000000000000 [ 188.530883] page dumped because: kasan: bad access detected [ 188.532336] [ 188.532720] Memory state around the buggy address: [ 188.533871] ffff8881e9c25f00: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc [ 188.535631] ffff8881e9c25f80: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc [ 188.537370] >ffff8881e9c26000: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb [ 188.538996] ^ [ 188.539812] ffff8881e9c26080: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb [ 188.541549] ffff8881e9c26100: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb Signed-off-by: Dafna Hirschfeld <dafna.hirschfeld@collabora.com> Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl> Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
377 lines
9.6 KiB
C
377 lines
9.6 KiB
C
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
/*
|
|
* vimc-sensor.c Virtual Media Controller Driver
|
|
*
|
|
* Copyright (C) 2015-2017 Helen Koike <helen.fornazier@gmail.com>
|
|
*/
|
|
|
|
#include <linux/v4l2-mediabus.h>
|
|
#include <linux/vmalloc.h>
|
|
#include <media/v4l2-ctrls.h>
|
|
#include <media/v4l2-event.h>
|
|
#include <media/v4l2-subdev.h>
|
|
#include <media/tpg/v4l2-tpg.h>
|
|
|
|
#include "vimc-common.h"
|
|
|
|
struct vimc_sen_device {
|
|
struct vimc_ent_device ved;
|
|
struct v4l2_subdev sd;
|
|
struct tpg_data tpg;
|
|
u8 *frame;
|
|
/* The active format */
|
|
struct v4l2_mbus_framefmt mbus_format;
|
|
struct v4l2_ctrl_handler hdl;
|
|
struct media_pad pad;
|
|
};
|
|
|
|
static const struct v4l2_mbus_framefmt fmt_default = {
|
|
.width = 640,
|
|
.height = 480,
|
|
.code = MEDIA_BUS_FMT_RGB888_1X24,
|
|
.field = V4L2_FIELD_NONE,
|
|
.colorspace = V4L2_COLORSPACE_DEFAULT,
|
|
};
|
|
|
|
static int vimc_sen_init_cfg(struct v4l2_subdev *sd,
|
|
struct v4l2_subdev_pad_config *cfg)
|
|
{
|
|
unsigned int i;
|
|
|
|
for (i = 0; i < sd->entity.num_pads; i++) {
|
|
struct v4l2_mbus_framefmt *mf;
|
|
|
|
mf = v4l2_subdev_get_try_format(sd, cfg, i);
|
|
*mf = fmt_default;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int vimc_sen_enum_mbus_code(struct v4l2_subdev *sd,
|
|
struct v4l2_subdev_pad_config *cfg,
|
|
struct v4l2_subdev_mbus_code_enum *code)
|
|
{
|
|
const struct vimc_pix_map *vpix = vimc_pix_map_by_index(code->index);
|
|
|
|
if (!vpix)
|
|
return -EINVAL;
|
|
|
|
code->code = vpix->code;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int vimc_sen_enum_frame_size(struct v4l2_subdev *sd,
|
|
struct v4l2_subdev_pad_config *cfg,
|
|
struct v4l2_subdev_frame_size_enum *fse)
|
|
{
|
|
const struct vimc_pix_map *vpix;
|
|
|
|
if (fse->index)
|
|
return -EINVAL;
|
|
|
|
/* Only accept code in the pix map table */
|
|
vpix = vimc_pix_map_by_code(fse->code);
|
|
if (!vpix)
|
|
return -EINVAL;
|
|
|
|
fse->min_width = VIMC_FRAME_MIN_WIDTH;
|
|
fse->max_width = VIMC_FRAME_MAX_WIDTH;
|
|
fse->min_height = VIMC_FRAME_MIN_HEIGHT;
|
|
fse->max_height = VIMC_FRAME_MAX_HEIGHT;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int vimc_sen_get_fmt(struct v4l2_subdev *sd,
|
|
struct v4l2_subdev_pad_config *cfg,
|
|
struct v4l2_subdev_format *fmt)
|
|
{
|
|
struct vimc_sen_device *vsen =
|
|
container_of(sd, struct vimc_sen_device, sd);
|
|
|
|
fmt->format = fmt->which == V4L2_SUBDEV_FORMAT_TRY ?
|
|
*v4l2_subdev_get_try_format(sd, cfg, fmt->pad) :
|
|
vsen->mbus_format;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void vimc_sen_tpg_s_format(struct vimc_sen_device *vsen)
|
|
{
|
|
const struct vimc_pix_map *vpix =
|
|
vimc_pix_map_by_code(vsen->mbus_format.code);
|
|
|
|
tpg_reset_source(&vsen->tpg, vsen->mbus_format.width,
|
|
vsen->mbus_format.height, vsen->mbus_format.field);
|
|
tpg_s_bytesperline(&vsen->tpg, 0, vsen->mbus_format.width * vpix->bpp);
|
|
tpg_s_buf_height(&vsen->tpg, vsen->mbus_format.height);
|
|
tpg_s_fourcc(&vsen->tpg, vpix->pixelformat);
|
|
/* TODO: add support for V4L2_FIELD_ALTERNATE */
|
|
tpg_s_field(&vsen->tpg, vsen->mbus_format.field, false);
|
|
tpg_s_colorspace(&vsen->tpg, vsen->mbus_format.colorspace);
|
|
tpg_s_ycbcr_enc(&vsen->tpg, vsen->mbus_format.ycbcr_enc);
|
|
tpg_s_quantization(&vsen->tpg, vsen->mbus_format.quantization);
|
|
tpg_s_xfer_func(&vsen->tpg, vsen->mbus_format.xfer_func);
|
|
}
|
|
|
|
static void vimc_sen_adjust_fmt(struct v4l2_mbus_framefmt *fmt)
|
|
{
|
|
const struct vimc_pix_map *vpix;
|
|
|
|
/* Only accept code in the pix map table */
|
|
vpix = vimc_pix_map_by_code(fmt->code);
|
|
if (!vpix)
|
|
fmt->code = fmt_default.code;
|
|
|
|
fmt->width = clamp_t(u32, fmt->width, VIMC_FRAME_MIN_WIDTH,
|
|
VIMC_FRAME_MAX_WIDTH) & ~1;
|
|
fmt->height = clamp_t(u32, fmt->height, VIMC_FRAME_MIN_HEIGHT,
|
|
VIMC_FRAME_MAX_HEIGHT) & ~1;
|
|
|
|
/* TODO: add support for V4L2_FIELD_ALTERNATE */
|
|
if (fmt->field == V4L2_FIELD_ANY || fmt->field == V4L2_FIELD_ALTERNATE)
|
|
fmt->field = fmt_default.field;
|
|
|
|
vimc_colorimetry_clamp(fmt);
|
|
}
|
|
|
|
static int vimc_sen_set_fmt(struct v4l2_subdev *sd,
|
|
struct v4l2_subdev_pad_config *cfg,
|
|
struct v4l2_subdev_format *fmt)
|
|
{
|
|
struct vimc_sen_device *vsen = v4l2_get_subdevdata(sd);
|
|
struct v4l2_mbus_framefmt *mf;
|
|
|
|
if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) {
|
|
/* Do not change the format while stream is on */
|
|
if (vsen->frame)
|
|
return -EBUSY;
|
|
|
|
mf = &vsen->mbus_format;
|
|
} else {
|
|
mf = v4l2_subdev_get_try_format(sd, cfg, fmt->pad);
|
|
}
|
|
|
|
/* Set the new format */
|
|
vimc_sen_adjust_fmt(&fmt->format);
|
|
|
|
dev_dbg(vsen->ved.dev, "%s: format update: "
|
|
"old:%dx%d (0x%x, %d, %d, %d, %d) "
|
|
"new:%dx%d (0x%x, %d, %d, %d, %d)\n", vsen->sd.name,
|
|
/* old */
|
|
mf->width, mf->height, mf->code,
|
|
mf->colorspace, mf->quantization,
|
|
mf->xfer_func, mf->ycbcr_enc,
|
|
/* new */
|
|
fmt->format.width, fmt->format.height, fmt->format.code,
|
|
fmt->format.colorspace, fmt->format.quantization,
|
|
fmt->format.xfer_func, fmt->format.ycbcr_enc);
|
|
|
|
*mf = fmt->format;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const struct v4l2_subdev_pad_ops vimc_sen_pad_ops = {
|
|
.init_cfg = vimc_sen_init_cfg,
|
|
.enum_mbus_code = vimc_sen_enum_mbus_code,
|
|
.enum_frame_size = vimc_sen_enum_frame_size,
|
|
.get_fmt = vimc_sen_get_fmt,
|
|
.set_fmt = vimc_sen_set_fmt,
|
|
};
|
|
|
|
static void *vimc_sen_process_frame(struct vimc_ent_device *ved,
|
|
const void *sink_frame)
|
|
{
|
|
struct vimc_sen_device *vsen = container_of(ved, struct vimc_sen_device,
|
|
ved);
|
|
|
|
tpg_fill_plane_buffer(&vsen->tpg, 0, 0, vsen->frame);
|
|
return vsen->frame;
|
|
}
|
|
|
|
static int vimc_sen_s_stream(struct v4l2_subdev *sd, int enable)
|
|
{
|
|
struct vimc_sen_device *vsen =
|
|
container_of(sd, struct vimc_sen_device, sd);
|
|
|
|
if (enable) {
|
|
const struct vimc_pix_map *vpix;
|
|
unsigned int frame_size;
|
|
|
|
/* Calculate the frame size */
|
|
vpix = vimc_pix_map_by_code(vsen->mbus_format.code);
|
|
frame_size = vsen->mbus_format.width * vpix->bpp *
|
|
vsen->mbus_format.height;
|
|
|
|
/*
|
|
* Allocate the frame buffer. Use vmalloc to be able to
|
|
* allocate a large amount of memory
|
|
*/
|
|
vsen->frame = vmalloc(frame_size);
|
|
if (!vsen->frame)
|
|
return -ENOMEM;
|
|
|
|
/* configure the test pattern generator */
|
|
vimc_sen_tpg_s_format(vsen);
|
|
|
|
} else {
|
|
|
|
vfree(vsen->frame);
|
|
vsen->frame = NULL;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const struct v4l2_subdev_core_ops vimc_sen_core_ops = {
|
|
.log_status = v4l2_ctrl_subdev_log_status,
|
|
.subscribe_event = v4l2_ctrl_subdev_subscribe_event,
|
|
.unsubscribe_event = v4l2_event_subdev_unsubscribe,
|
|
};
|
|
|
|
static const struct v4l2_subdev_video_ops vimc_sen_video_ops = {
|
|
.s_stream = vimc_sen_s_stream,
|
|
};
|
|
|
|
static const struct v4l2_subdev_ops vimc_sen_ops = {
|
|
.core = &vimc_sen_core_ops,
|
|
.pad = &vimc_sen_pad_ops,
|
|
.video = &vimc_sen_video_ops,
|
|
};
|
|
|
|
static int vimc_sen_s_ctrl(struct v4l2_ctrl *ctrl)
|
|
{
|
|
struct vimc_sen_device *vsen =
|
|
container_of(ctrl->handler, struct vimc_sen_device, hdl);
|
|
|
|
switch (ctrl->id) {
|
|
case VIMC_CID_TEST_PATTERN:
|
|
tpg_s_pattern(&vsen->tpg, ctrl->val);
|
|
break;
|
|
case V4L2_CID_HFLIP:
|
|
tpg_s_hflip(&vsen->tpg, ctrl->val);
|
|
break;
|
|
case V4L2_CID_VFLIP:
|
|
tpg_s_vflip(&vsen->tpg, ctrl->val);
|
|
break;
|
|
case V4L2_CID_BRIGHTNESS:
|
|
tpg_s_brightness(&vsen->tpg, ctrl->val);
|
|
break;
|
|
case V4L2_CID_CONTRAST:
|
|
tpg_s_contrast(&vsen->tpg, ctrl->val);
|
|
break;
|
|
case V4L2_CID_HUE:
|
|
tpg_s_hue(&vsen->tpg, ctrl->val);
|
|
break;
|
|
case V4L2_CID_SATURATION:
|
|
tpg_s_saturation(&vsen->tpg, ctrl->val);
|
|
break;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static const struct v4l2_ctrl_ops vimc_sen_ctrl_ops = {
|
|
.s_ctrl = vimc_sen_s_ctrl,
|
|
};
|
|
|
|
void vimc_sen_release(struct vimc_ent_device *ved)
|
|
{
|
|
struct vimc_sen_device *vsen =
|
|
container_of(ved, struct vimc_sen_device, ved);
|
|
|
|
v4l2_ctrl_handler_free(&vsen->hdl);
|
|
tpg_free(&vsen->tpg);
|
|
media_entity_cleanup(vsen->ved.ent);
|
|
kfree(vsen);
|
|
}
|
|
|
|
/* Image Processing Controls */
|
|
static const struct v4l2_ctrl_config vimc_sen_ctrl_class = {
|
|
.flags = V4L2_CTRL_FLAG_READ_ONLY | V4L2_CTRL_FLAG_WRITE_ONLY,
|
|
.id = VIMC_CID_VIMC_CLASS,
|
|
.name = "VIMC Controls",
|
|
.type = V4L2_CTRL_TYPE_CTRL_CLASS,
|
|
};
|
|
|
|
static const struct v4l2_ctrl_config vimc_sen_ctrl_test_pattern = {
|
|
.ops = &vimc_sen_ctrl_ops,
|
|
.id = VIMC_CID_TEST_PATTERN,
|
|
.name = "Test Pattern",
|
|
.type = V4L2_CTRL_TYPE_MENU,
|
|
.max = TPG_PAT_NOISE,
|
|
.qmenu = tpg_pattern_strings,
|
|
};
|
|
|
|
struct vimc_ent_device *vimc_sen_add(struct vimc_device *vimc,
|
|
const char *vcfg_name)
|
|
{
|
|
struct v4l2_device *v4l2_dev = &vimc->v4l2_dev;
|
|
struct vimc_sen_device *vsen;
|
|
int ret;
|
|
|
|
/* Allocate the vsen struct */
|
|
vsen = kzalloc(sizeof(*vsen), GFP_KERNEL);
|
|
if (!vsen)
|
|
return NULL;
|
|
|
|
v4l2_ctrl_handler_init(&vsen->hdl, 4);
|
|
|
|
v4l2_ctrl_new_custom(&vsen->hdl, &vimc_sen_ctrl_class, NULL);
|
|
v4l2_ctrl_new_custom(&vsen->hdl, &vimc_sen_ctrl_test_pattern, NULL);
|
|
v4l2_ctrl_new_std(&vsen->hdl, &vimc_sen_ctrl_ops,
|
|
V4L2_CID_VFLIP, 0, 1, 1, 0);
|
|
v4l2_ctrl_new_std(&vsen->hdl, &vimc_sen_ctrl_ops,
|
|
V4L2_CID_HFLIP, 0, 1, 1, 0);
|
|
v4l2_ctrl_new_std(&vsen->hdl, &vimc_sen_ctrl_ops,
|
|
V4L2_CID_BRIGHTNESS, 0, 255, 1, 128);
|
|
v4l2_ctrl_new_std(&vsen->hdl, &vimc_sen_ctrl_ops,
|
|
V4L2_CID_CONTRAST, 0, 255, 1, 128);
|
|
v4l2_ctrl_new_std(&vsen->hdl, &vimc_sen_ctrl_ops,
|
|
V4L2_CID_HUE, -128, 127, 1, 0);
|
|
v4l2_ctrl_new_std(&vsen->hdl, &vimc_sen_ctrl_ops,
|
|
V4L2_CID_SATURATION, 0, 255, 1, 128);
|
|
vsen->sd.ctrl_handler = &vsen->hdl;
|
|
if (vsen->hdl.error) {
|
|
ret = vsen->hdl.error;
|
|
goto err_free_vsen;
|
|
}
|
|
|
|
/* Initialize the test pattern generator */
|
|
tpg_init(&vsen->tpg, vsen->mbus_format.width,
|
|
vsen->mbus_format.height);
|
|
ret = tpg_alloc(&vsen->tpg, VIMC_FRAME_MAX_WIDTH);
|
|
if (ret)
|
|
goto err_free_hdl;
|
|
|
|
/* Initialize ved and sd */
|
|
vsen->pad.flags = MEDIA_PAD_FL_SOURCE;
|
|
ret = vimc_ent_sd_register(&vsen->ved, &vsen->sd, v4l2_dev,
|
|
vcfg_name,
|
|
MEDIA_ENT_F_CAM_SENSOR, 1, &vsen->pad,
|
|
&vimc_sen_ops);
|
|
if (ret)
|
|
goto err_free_tpg;
|
|
|
|
vsen->ved.process_frame = vimc_sen_process_frame;
|
|
vsen->ved.dev = vimc->mdev.dev;
|
|
|
|
/* Initialize the frame format */
|
|
vsen->mbus_format = fmt_default;
|
|
|
|
return &vsen->ved;
|
|
|
|
err_free_tpg:
|
|
tpg_free(&vsen->tpg);
|
|
err_free_hdl:
|
|
v4l2_ctrl_handler_free(&vsen->hdl);
|
|
err_free_vsen:
|
|
kfree(vsen);
|
|
|
|
return NULL;
|
|
}
|