2009-12-09 14:38:49 +03:00
/*
2009-12-09 14:38:52 +03:00
* V4L2 sub - device
2009-12-09 14:38:49 +03:00
*
2009-12-09 14:38:52 +03:00
* Copyright ( C ) 2010 Nokia Corporation
2009-12-09 14:38:49 +03:00
*
2009-12-09 14:38:52 +03:00
* Contact : Laurent Pinchart < laurent . pinchart @ ideasonboard . com >
* Sakari Ailus < sakari . ailus @ iki . fi >
2009-12-09 14:38:49 +03:00
*
2009-12-09 14:38:52 +03:00
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation .
2009-12-09 14:38:49 +03:00
*
2009-12-09 14:38:52 +03:00
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
2009-12-09 14:38:49 +03:00
*
2009-12-09 14:38:52 +03:00
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 59 Temple Place , Suite 330 , Boston , MA 02111 - 1307 USA
2009-12-09 14:38:49 +03:00
*/
# include <linux/ioctl.h>
2010-03-03 18:49:38 +03:00
# include <linux/slab.h>
# include <linux/types.h>
2009-12-09 14:38:49 +03:00
# include <linux/videodev2.h>
2009-12-09 14:39:54 +03:00
# include <media/v4l2-ctrls.h>
2009-12-09 14:38:49 +03:00
# include <media/v4l2-device.h>
# include <media/v4l2-ioctl.h>
2010-03-03 18:49:38 +03:00
# include <media/v4l2-fh.h>
# include <media/v4l2-event.h>
2009-12-09 14:38:49 +03:00
2010-05-21 13:04:24 +04:00
static int subdev_fh_init ( struct v4l2_subdev_fh * fh , struct v4l2_subdev * sd )
{
# if defined(CONFIG_VIDEO_V4L2_SUBDEV_API)
/* Allocate try format and crop in the same memory block */
fh - > try_fmt = kzalloc ( ( sizeof ( * fh - > try_fmt ) + sizeof ( * fh - > try_crop ) )
* sd - > entity . num_pads , GFP_KERNEL ) ;
if ( fh - > try_fmt = = NULL )
return - ENOMEM ;
fh - > try_crop = ( struct v4l2_rect * )
( fh - > try_fmt + sd - > entity . num_pads ) ;
# endif
return 0 ;
}
static void subdev_fh_free ( struct v4l2_subdev_fh * fh )
{
# if defined(CONFIG_VIDEO_V4L2_SUBDEV_API)
kfree ( fh - > try_fmt ) ;
fh - > try_fmt = NULL ;
fh - > try_crop = NULL ;
# endif
}
2009-12-09 14:38:49 +03:00
static int subdev_open ( struct file * file )
{
2010-03-03 18:49:38 +03:00
struct video_device * vdev = video_devdata ( file ) ;
struct v4l2_subdev * sd = vdev_to_v4l2_subdev ( vdev ) ;
2010-05-21 13:04:24 +04:00
struct v4l2_subdev_fh * subdev_fh ;
2009-12-09 14:40:08 +03:00
# if defined(CONFIG_MEDIA_CONTROLLER)
2010-08-02 02:05:09 +04:00
struct media_entity * entity = NULL ;
2009-12-09 14:40:08 +03:00
# endif
2010-03-03 18:49:38 +03:00
int ret ;
2010-05-21 13:04:24 +04:00
subdev_fh = kzalloc ( sizeof ( * subdev_fh ) , GFP_KERNEL ) ;
if ( subdev_fh = = NULL )
return - ENOMEM ;
2010-03-03 18:49:38 +03:00
2010-05-21 13:04:24 +04:00
ret = subdev_fh_init ( subdev_fh , sd ) ;
if ( ret ) {
kfree ( subdev_fh ) ;
return ret ;
}
2011-06-14 00:44:42 +04:00
v4l2_fh_init ( & subdev_fh - > vfh , vdev ) ;
2010-05-21 13:04:24 +04:00
v4l2_fh_add ( & subdev_fh - > vfh ) ;
file - > private_data = & subdev_fh - > vfh ;
2009-12-09 14:40:08 +03:00
# if defined(CONFIG_MEDIA_CONTROLLER)
if ( sd - > v4l2_dev - > mdev ) {
entity = media_entity_get ( & sd - > entity ) ;
if ( ! entity ) {
ret = - EBUSY ;
goto err ;
}
}
# endif
2010-05-21 13:04:24 +04:00
2010-08-02 02:05:09 +04:00
if ( sd - > internal_ops & & sd - > internal_ops - > open ) {
ret = sd - > internal_ops - > open ( sd , subdev_fh ) ;
if ( ret < 0 )
goto err ;
}
2009-12-09 14:38:49 +03:00
return 0 ;
2010-03-03 18:49:38 +03:00
err :
2010-08-02 02:05:09 +04:00
# if defined(CONFIG_MEDIA_CONTROLLER)
if ( entity )
media_entity_put ( entity ) ;
# endif
2010-05-21 13:04:24 +04:00
v4l2_fh_del ( & subdev_fh - > vfh ) ;
v4l2_fh_exit ( & subdev_fh - > vfh ) ;
subdev_fh_free ( subdev_fh ) ;
kfree ( subdev_fh ) ;
2010-03-03 18:49:38 +03:00
return ret ;
2009-12-09 14:38:49 +03:00
}
static int subdev_close ( struct file * file )
{
2009-12-09 14:40:08 +03:00
struct video_device * vdev = video_devdata ( file ) ;
struct v4l2_subdev * sd = vdev_to_v4l2_subdev ( vdev ) ;
2010-03-03 18:49:38 +03:00
struct v4l2_fh * vfh = file - > private_data ;
2010-05-21 13:04:24 +04:00
struct v4l2_subdev_fh * subdev_fh = to_v4l2_subdev_fh ( vfh ) ;
2010-03-03 18:49:38 +03:00
2010-08-02 02:05:09 +04:00
if ( sd - > internal_ops & & sd - > internal_ops - > close )
sd - > internal_ops - > close ( sd , subdev_fh ) ;
2009-12-09 14:40:08 +03:00
# if defined(CONFIG_MEDIA_CONTROLLER)
if ( sd - > v4l2_dev - > mdev )
media_entity_put ( & sd - > entity ) ;
# endif
2010-05-21 13:04:24 +04:00
v4l2_fh_del ( vfh ) ;
v4l2_fh_exit ( vfh ) ;
subdev_fh_free ( subdev_fh ) ;
kfree ( subdev_fh ) ;
file - > private_data = NULL ;
2010-03-03 18:49:38 +03:00
2009-12-09 14:38:49 +03:00
return 0 ;
}
static long subdev_do_ioctl ( struct file * file , unsigned int cmd , void * arg )
{
2009-12-09 14:39:54 +03:00
struct video_device * vdev = video_devdata ( file ) ;
struct v4l2_subdev * sd = vdev_to_v4l2_subdev ( vdev ) ;
2010-05-21 13:04:24 +04:00
struct v4l2_fh * vfh = file - > private_data ;
2010-03-16 02:26:04 +03:00
# if defined(CONFIG_VIDEO_V4L2_SUBDEV_API)
struct v4l2_subdev_fh * subdev_fh = to_v4l2_subdev_fh ( vfh ) ;
# endif
2009-12-09 14:39:54 +03:00
2009-12-09 14:38:49 +03:00
switch ( cmd ) {
2009-12-09 14:39:54 +03:00
case VIDIOC_QUERYCTRL :
2011-05-25 15:52:13 +04:00
return v4l2_queryctrl ( vfh - > ctrl_handler , arg ) ;
2009-12-09 14:39:54 +03:00
case VIDIOC_QUERYMENU :
2011-05-25 15:52:13 +04:00
return v4l2_querymenu ( vfh - > ctrl_handler , arg ) ;
2009-12-09 14:39:54 +03:00
case VIDIOC_G_CTRL :
2011-05-25 15:52:13 +04:00
return v4l2_g_ctrl ( vfh - > ctrl_handler , arg ) ;
2009-12-09 14:39:54 +03:00
case VIDIOC_S_CTRL :
2011-06-07 13:47:18 +04:00
return v4l2_s_ctrl ( vfh , vfh - > ctrl_handler , arg ) ;
2009-12-09 14:39:54 +03:00
case VIDIOC_G_EXT_CTRLS :
2011-05-25 15:52:13 +04:00
return v4l2_g_ext_ctrls ( vfh - > ctrl_handler , arg ) ;
2009-12-09 14:39:54 +03:00
case VIDIOC_S_EXT_CTRLS :
2011-06-07 13:47:18 +04:00
return v4l2_s_ext_ctrls ( vfh , vfh - > ctrl_handler , arg ) ;
2009-12-09 14:39:54 +03:00
case VIDIOC_TRY_EXT_CTRLS :
2011-05-25 15:52:13 +04:00
return v4l2_try_ext_ctrls ( vfh - > ctrl_handler , arg ) ;
2009-12-09 14:39:54 +03:00
2010-03-03 18:49:38 +03:00
case VIDIOC_DQEVENT :
if ( ! ( sd - > flags & V4L2_SUBDEV_FL_HAS_EVENTS ) )
return - ENOIOCTLCMD ;
2010-05-21 13:04:24 +04:00
return v4l2_event_dequeue ( vfh , arg , file - > f_flags & O_NONBLOCK ) ;
2010-03-03 18:49:38 +03:00
case VIDIOC_SUBSCRIBE_EVENT :
2010-05-21 13:04:24 +04:00
return v4l2_subdev_call ( sd , core , subscribe_event , vfh , arg ) ;
2010-03-03 18:49:38 +03:00
case VIDIOC_UNSUBSCRIBE_EVENT :
2010-05-21 13:04:24 +04:00
return v4l2_subdev_call ( sd , core , unsubscribe_event , vfh , arg ) ;
2010-03-16 02:26:04 +03:00
# if defined(CONFIG_VIDEO_V4L2_SUBDEV_API)
case VIDIOC_SUBDEV_G_FMT : {
struct v4l2_subdev_format * format = arg ;
if ( format - > which ! = V4L2_SUBDEV_FORMAT_TRY & &
format - > which ! = V4L2_SUBDEV_FORMAT_ACTIVE )
return - EINVAL ;
if ( format - > pad > = sd - > entity . num_pads )
return - EINVAL ;
return v4l2_subdev_call ( sd , pad , get_fmt , subdev_fh , format ) ;
}
case VIDIOC_SUBDEV_S_FMT : {
struct v4l2_subdev_format * format = arg ;
if ( format - > which ! = V4L2_SUBDEV_FORMAT_TRY & &
format - > which ! = V4L2_SUBDEV_FORMAT_ACTIVE )
return - EINVAL ;
if ( format - > pad > = sd - > entity . num_pads )
return - EINVAL ;
2010-03-03 18:49:38 +03:00
2010-03-16 02:26:04 +03:00
return v4l2_subdev_call ( sd , pad , set_fmt , subdev_fh , format ) ;
}
2010-06-23 12:03:42 +04:00
case VIDIOC_SUBDEV_G_CROP : {
struct v4l2_subdev_crop * crop = arg ;
if ( crop - > which ! = V4L2_SUBDEV_FORMAT_TRY & &
crop - > which ! = V4L2_SUBDEV_FORMAT_ACTIVE )
return - EINVAL ;
if ( crop - > pad > = sd - > entity . num_pads )
return - EINVAL ;
return v4l2_subdev_call ( sd , pad , get_crop , subdev_fh , crop ) ;
}
case VIDIOC_SUBDEV_S_CROP : {
struct v4l2_subdev_crop * crop = arg ;
if ( crop - > which ! = V4L2_SUBDEV_FORMAT_TRY & &
crop - > which ! = V4L2_SUBDEV_FORMAT_ACTIVE )
return - EINVAL ;
if ( crop - > pad > = sd - > entity . num_pads )
return - EINVAL ;
return v4l2_subdev_call ( sd , pad , set_crop , subdev_fh , crop ) ;
}
2010-03-16 02:26:04 +03:00
case VIDIOC_SUBDEV_ENUM_MBUS_CODE : {
struct v4l2_subdev_mbus_code_enum * code = arg ;
if ( code - > pad > = sd - > entity . num_pads )
return - EINVAL ;
return v4l2_subdev_call ( sd , pad , enum_mbus_code , subdev_fh ,
code ) ;
}
case VIDIOC_SUBDEV_ENUM_FRAME_SIZE : {
struct v4l2_subdev_frame_size_enum * fse = arg ;
if ( fse - > pad > = sd - > entity . num_pads )
return - EINVAL ;
return v4l2_subdev_call ( sd , pad , enum_frame_size , subdev_fh ,
fse ) ;
}
2010-05-05 18:38:35 +04:00
case VIDIOC_SUBDEV_G_FRAME_INTERVAL :
return v4l2_subdev_call ( sd , video , g_frame_interval , arg ) ;
case VIDIOC_SUBDEV_S_FRAME_INTERVAL :
return v4l2_subdev_call ( sd , video , s_frame_interval , arg ) ;
case VIDIOC_SUBDEV_ENUM_FRAME_INTERVAL : {
struct v4l2_subdev_frame_interval_enum * fie = arg ;
if ( fie - > pad > = sd - > entity . num_pads )
return - EINVAL ;
return v4l2_subdev_call ( sd , pad , enum_frame_interval , subdev_fh ,
fie ) ;
}
2010-03-16 02:26:04 +03:00
# endif
2009-12-09 14:38:49 +03:00
default :
2010-02-26 18:23:10 +03:00
return v4l2_subdev_call ( sd , core , ioctl , cmd , arg ) ;
2009-12-09 14:38:49 +03:00
}
return 0 ;
}
static long subdev_ioctl ( struct file * file , unsigned int cmd ,
unsigned long arg )
{
return video_usercopy ( file , cmd , arg , subdev_do_ioctl ) ;
}
2010-03-03 18:49:38 +03:00
static unsigned int subdev_poll ( struct file * file , poll_table * wait )
{
struct video_device * vdev = video_devdata ( file ) ;
struct v4l2_subdev * sd = vdev_to_v4l2_subdev ( vdev ) ;
struct v4l2_fh * fh = file - > private_data ;
if ( ! ( sd - > flags & V4L2_SUBDEV_FL_HAS_EVENTS ) )
return POLLERR ;
2011-06-14 00:44:42 +04:00
poll_wait ( file , & fh - > wait , wait ) ;
2010-03-03 18:49:38 +03:00
if ( v4l2_event_pending ( fh ) )
return POLLPRI ;
return 0 ;
}
2009-12-09 14:38:49 +03:00
const struct v4l2_file_operations v4l2_subdev_fops = {
. owner = THIS_MODULE ,
. open = subdev_open ,
. unlocked_ioctl = subdev_ioctl ,
. release = subdev_close ,
2010-03-03 18:49:38 +03:00
. poll = subdev_poll ,
2009-12-09 14:38:49 +03:00
} ;
2009-12-09 14:38:52 +03:00
void v4l2_subdev_init ( struct v4l2_subdev * sd , const struct v4l2_subdev_ops * ops )
{
INIT_LIST_HEAD ( & sd - > list ) ;
BUG_ON ( ! ops ) ;
sd - > ops = ops ;
sd - > v4l2_dev = NULL ;
sd - > flags = 0 ;
sd - > name [ 0 ] = ' \0 ' ;
sd - > grp_id = 0 ;
sd - > dev_priv = NULL ;
sd - > host_priv = NULL ;
2009-12-09 14:40:08 +03:00
# if defined(CONFIG_MEDIA_CONTROLLER)
sd - > entity . name = sd - > name ;
sd - > entity . type = MEDIA_ENT_T_V4L2_SUBDEV ;
# endif
2009-12-09 14:38:52 +03:00
}
EXPORT_SYMBOL ( v4l2_subdev_init ) ;