V4L2源代码之旅八:ioctl

来源:互联网 发布:欧姆龙传感器淘宝 编辑:程序博客网 时间:2024/06/05 19:23

转自:http://www.cnblogs.com/ronnydm/p/5796821.html

我们从代码的角度看看,如何调用到我们设定的ioctl。

1. 我们在驱动程序中,分配了结构体struct video_device,并做了设定,然后调用video_register_device进行注册。在video_device的设置中,存在ioctl的设置。

复制代码
/* kernel/drivers/media/video/ovisp/ovisp-vide.c */static const struct v4l2_ioctl_ops ovisp_v4l2_ioctl_ops = {    /* VIDIOC_QUERYCAP handler */    .vidioc_querycap        = ovisp_vidioc_querycap,    .vidioc_s_tlb_base              = ovisp_vidioc_s_tlb_base,    /* Priority handling */    .vidioc_s_priority        = ovisp_vidioc_s_priority,    .vidioc_g_priority        = ovisp_vidioc_g_priority,    .vidioc_enum_fmt_vid_cap    = ovisp_vidioc_enum_fmt_vid_cap,    .vidioc_g_fmt_vid_cap        = ovisp_vidioc_g_fmt_vid_cap,    .vidioc_try_fmt_vid_cap        = ovisp_vidioc_try_fmt_vid_cap,    .vidioc_s_fmt_vid_cap        = ovisp_vidioc_s_fmt_vid_cap,    .vidioc_enum_framesizes         = ovisp_vidioc_enum_framesizes,    /*frame management*/    .vidioc_reqbufs             = ovisp_vidioc_reqbufs,    .vidioc_querybuf            = ovisp_vidioc_querybuf,    .vidioc_qbuf                = ovisp_vidioc_qbuf,    .vidioc_dqbuf               = ovisp_vidioc_dqbuf,    /**/    .vidioc_enum_input          = ovisp_vidioc_enum_input,    .vidioc_g_input             = ovisp_vidioc_g_input,    .vidioc_s_input             = ovisp_vidioc_s_input,    /*isp function, modified according to spec*/    .vidioc_g_ctrl                = ovisp_vidioc_g_ctrl,    .vidioc_s_ctrl              = ovisp_vidioc_s_ctrl,    .vidioc_cropcap             = ovisp_vidioc_cropcap,    .vidioc_g_crop              = ovisp_vidioc_g_crop,    .vidioc_s_crop              = ovisp_vidioc_s_crop,    .vidioc_s_parm              = ovisp_vidioc_s_parm,    .vidioc_g_parm              = ovisp_vidioc_g_parm,    .vidioc_streamon            = ovisp_vidioc_streamon,    .vidioc_streamoff           = ovisp_vidioc_streamoff,};static struct v4l2_file_operations ovisp_v4l2_fops = {    .owner         = THIS_MODULE,    .open         = ovisp_v4l2_open,    .release     = ovisp_v4l2_close,    .poll        = ovisp_v4l2_poll,    .unlocked_ioctl    = video_ioctl2,    .mmap         = ovisp_v4l2_mmap,};static struct video_device ovisp_camera = {    .name = "ovisp-video-camera",    .minor = -1,    .release = video_device_release,    .fops = &ovisp_v4l2_fops,    .ioctl_ops = &ovisp_v4l2_ioctl_ops,};
复制代码

  从上述代码可以看到,我们在驱动中只要这么设置,应用在调用IOCTL就是可以调用到我们设置的函数,那么如何调用呢?

2. v4l2-dev.c

在v4l2-dev.c中,我们看到,我们给应用设置的file_operations如下:

复制代码
static const struct file_operations v4l2_fops = {    .owner = THIS_MODULE,    .read = v4l2_read,    .write = v4l2_write,    .open = v4l2_open,    .get_unmapped_area = v4l2_get_unmapped_area,    .mmap = v4l2_mmap,    .unlocked_ioctl = v4l2_ioctl,          // ioctl肯定是从这儿调的#ifdef CONFIG_COMPAT    .compat_ioctl = v4l2_compat_ioctl32,#endif    .release = v4l2_release,    .poll = v4l2_poll,    .llseek = no_llseek,};
复制代码
复制代码
static long v4l2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg){    struct video_device *vdev = video_devdata(filp);    int ret = -ENODEV;    if (vdev->fops->unlocked_ioctl) {                                  // 我们在ovisp-video.c中设置了vdev->fops->inlocked_ioctl = video_ioctl2        if (vdev->lock && mutex_lock_interruptible(vdev->lock))            return -ERESTARTSYS;        if (video_is_registered(vdev))            ret = vdev->fops->unlocked_ioctl(filp, cmd, arg);          // 调用我们在ovisp-video.c中设置的video_ioctl2,那么video_ioctl2在哪里呢?        if (vdev->lock)            mutex_unlock(vdev->lock);    } else if (vdev->fops->ioctl) {        /* This code path is a replacement for the BKL. It is a major         * hack but it will have to do for those drivers that are not         * yet converted to use unlocked_ioctl.         *         * There are two options: if the driver implements struct         * v4l2_device, then the lock defined there is used to         * serialize the ioctls. Otherwise the v4l2 core lock defined         * below is used. This lock is really bad since it serializes         * completely independent devices.         *         * Both variants suffer from the same problem: if the driver         * sleeps, then it blocks all ioctls since the lock is still         * held. This is very common for VIDIOC_DQBUF since that         * normally waits for a frame to arrive. As a result any other         * ioctl calls will proceed very, very slowly since each call         * will have to wait for the VIDIOC_QBUF to finish. Things that         * should take 0.01s may now take 10-20 seconds.         *         * The workaround is to *not* take the lock for VIDIOC_DQBUF.         * This actually works OK for videobuf-based drivers, since         * videobuf will take its own internal lock.         */        static DEFINE_MUTEX(v4l2_ioctl_mutex);        struct mutex *m = vdev->v4l2_dev ?            &vdev->v4l2_dev->ioctl_lock : &v4l2_ioctl_mutex;        if (cmd != VIDIOC_DQBUF && mutex_lock_interruptible(m))            return -ERESTARTSYS;        if (video_is_registered(vdev))            ret = vdev->fops->ioctl(filp, cmd, arg);        if (cmd != VIDIOC_DQBUF)            mutex_unlock(m);    } else        ret = -ENOTTY;    return ret;}
复制代码

  video_ioctl2是Kernel提供的统一的接口(v4l2-ioctl.c):

复制代码
long video_ioctl2(struct file *file,           unsigned int cmd, unsigned long arg){    return video_usercopy(file, cmd, arg, __video_do_ioctl);  
  // 这句话意思是:1. 对用户发送的cmd进行合法性检测 2. 复制用户的arg参数到kernel空间 3. 调用__video_do_ioctl函数处理ioctl。
  // 所以我们接着看__video_do_ioctl函数
}EXPORT_SYMBOL(video_ioctl2);
复制代码
复制代码
static long __video_do_ioctl(struct file *file,        unsigned int cmd, void *arg){    struct video_device *vfd = video_devdata(file);    const struct v4l2_ioctl_ops *ops = vfd->ioctl_ops;  ......switch (cmd) {    /* --- capabilities ------------------------------------------ */    case VIDIOC_QUERYCAP:        ......break;    /* --- priority ------------------------------------------ */    case VIDIOC_G_PRIORITY:        ......break;    case VIDIOC_S_PRIORITY:        ......break;    /* --- capture ioctls ---------------------------------------- */    case VIDIOC_ENUM_FMT:        ......break;    case VIDIOC_G_FMT:
     ......
break; case VIDIOC_S_FMT: ......break; case VIDIOC_TRY_FMT: ......break; /* FIXME: Those buf reqs could be handled here, with some changes on videobuf to allow its header to be included at videodev2.h or being merged at videodev2. */ case VIDIOC_REQBUFS: ......break; case VIDIOC_QUERYBUF: ......break; case VIDIOC_QBUF:
     ......
break; case VIDIOC_DQBUF: ......break; case VIDIOC_OVERLAY: ......break; case VIDIOC_G_FBUF: ......break; case VIDIOC_S_FBUF: .....break; case VIDIOC_STREAMON: .....break; case VIDIOC_STREAMOFF: ......break; /* ---------- tv norms ---------- */ case VIDIOC_ENUMSTD: ......break; case VIDIOC_G_STD: ......break; case VIDIOC_S_STD: ......break; case VIDIOC_QUERYSTD: ......break; /* ------ input switching ---------- */ /* FIXME: Inputs can be handled inside videodev2 */ case VIDIOC_ENUMINPUT: ......break; case VIDIOC_G_INPUT: ......break; case VIDIOC_S_INPUT: ......break; /* ------ output switching ---------- */ case VIDIOC_ENUMOUTPUT: ......break; case VIDIOC_G_OUTPUT: ......break; case VIDIOC_S_OUTPUT: ......break; /* --- controls ---------------------------------------------- */ case VIDIOC_QUERYCTRL: ......break; case VIDIOC_G_CTRL: ......break; case VIDIOC_S_CTRL: ......break; case VIDIOC_G_EXT_CTRLS: ......break; case VIDIOC_S_EXT_CTRLS: ......break; case VIDIOC_TRY_EXT_CTRLS: ......break; case VIDIOC_QUERYMENU: ......break; /* --- audio ---------------------------------------------- */ case VIDIOC_ENUMAUDIO: ......break; case VIDIOC_G_AUDIO: ......break; case VIDIOC_S_AUDIO: ......break; case VIDIOC_ENUMAUDOUT: ......break; case VIDIOC_G_AUDOUT: ......break; case VIDIOC_S_AUDOUT: ......break; case VIDIOC_G_MODULATOR: ......break; case VIDIOC_S_MODULATOR: ......break; case VIDIOC_G_CROP: ......break; case VIDIOC_S_CROP: ......break; case VIDIOC_CROPCAP: ......break; case VIDIOC_G_JPEGCOMP: ......break; case VIDIOC_S_JPEGCOMP: ......break; case VIDIOC_G_ENC_INDEX: ......break; case VIDIOC_ENCODER_CMD: ......break; case VIDIOC_TRY_ENCODER_CMD: ......break; case VIDIOC_G_PARM: ......break; case VIDIOC_S_PARM: ......break; case VIDIOC_G_TUNER: ......break; case VIDIOC_S_TUNER: ......break; case VIDIOC_G_FREQUENCY: .....break; case VIDIOC_S_FREQUENCY: ......break; case VIDIOC_G_SLICED_VBI_CAP: ......break; case VIDIOC_LOG_STATUS: ......break;#ifdef CONFIG_VIDEO_ADV_DEBUG case VIDIOC_DBG_G_REGISTER: ......break; case VIDIOC_DBG_S_REGISTER: ......break;#endif case VIDIOC_DBG_G_CHIP_IDENT: ......break; case VIDIOC_S_HW_FREQ_SEEK: ......break; case VIDIOC_ENUM_FRAMESIZES: ......break; case VIDIOC_ENUM_FRAMEINTERVALS: ......break; case VIDIOC_ENUM_DV_PRESETS: ......break; case VIDIOC_S_DV_PRESET: ......break; case VIDIOC_G_DV_PRESET: ......break; case VIDIOC_QUERY_DV_PRESET: ......break; case VIDIOC_S_DV_TIMINGS: .....break; case VIDIOC_G_DV_TIMINGS: ......break; case VIDIOC_DQEVENT: ......break; case VIDIOC_SUBSCRIBE_EVENT: ......break; case VIDIOC_UNSUBSCRIBE_EVENT: .....break; case VIDIOC_SET_TLB_BASE: ......break; default: ......break; } /* switch */return ret;}
复制代码

  我们可以看到,这个函数就是对ioctl命令的分发。到底有多少命令呢?

复制代码
/* kernel/include/linux/videodev2.h *//* *    I O C T L   C O D E S   F O R   V I D E O   D E V I C E S * */#define VIDIOC_QUERYCAP         _IOR('V',  0, struct v4l2_capability)#define VIDIOC_RESERVED          _IO('V',  1)#define VIDIOC_ENUM_FMT         _IOWR('V',  2, struct v4l2_fmtdesc)#define VIDIOC_G_FMT        _IOWR('V',  4, struct v4l2_format)#define VIDIOC_S_FMT        _IOWR('V',  5, struct v4l2_format)#define VIDIOC_REQBUFS        _IOWR('V',  8, struct v4l2_requestbuffers)#define VIDIOC_QUERYBUF        _IOWR('V',  9, struct v4l2_buffer)#define VIDIOC_G_FBUF         _IOR('V', 10, struct v4l2_framebuffer)#define VIDIOC_S_FBUF         _IOW('V', 11, struct v4l2_framebuffer)#define VIDIOC_OVERLAY         _IOW('V', 14, int)#define VIDIOC_QBUF        _IOWR('V', 15, struct v4l2_buffer)#define VIDIOC_DQBUF        _IOWR('V', 17, struct v4l2_buffer)#define VIDIOC_STREAMON         _IOW('V', 18, int)#define VIDIOC_STREAMOFF     _IOW('V', 19, int)#define VIDIOC_G_PARM        _IOWR('V', 21, struct v4l2_streamparm)#define VIDIOC_S_PARM        _IOWR('V', 22, struct v4l2_streamparm)#define VIDIOC_G_STD         _IOR('V', 23, v4l2_std_id)#define VIDIOC_S_STD         _IOW('V', 24, v4l2_std_id)#define VIDIOC_ENUMSTD        _IOWR('V', 25, struct v4l2_standard)#define VIDIOC_ENUMINPUT    _IOWR('V', 26, struct v4l2_input)#define VIDIOC_G_CTRL        _IOWR('V', 27, struct v4l2_control)#define VIDIOC_S_CTRL        _IOWR('V', 28, struct v4l2_control)#define VIDIOC_G_TUNER        _IOWR('V', 29, struct v4l2_tuner)#define VIDIOC_S_TUNER         _IOW('V', 30, struct v4l2_tuner)#define VIDIOC_G_AUDIO         _IOR('V', 33, struct v4l2_audio)#define VIDIOC_S_AUDIO         _IOW('V', 34, struct v4l2_audio)#define VIDIOC_QUERYCTRL    _IOWR('V', 36, struct v4l2_queryctrl)#define VIDIOC_QUERYMENU    _IOWR('V', 37, struct v4l2_querymenu)#define VIDIOC_G_INPUT         _IOR('V', 38, int)#define VIDIOC_S_INPUT        _IOWR('V', 39, int)#define VIDIOC_G_OUTPUT         _IOR('V', 46, int)#define VIDIOC_S_OUTPUT        _IOWR('V', 47, int)#define VIDIOC_ENUMOUTPUT    _IOWR('V', 48, struct v4l2_output)#define VIDIOC_G_AUDOUT         _IOR('V', 49, struct v4l2_audioout)#define VIDIOC_S_AUDOUT         _IOW('V', 50, struct v4l2_audioout)#define VIDIOC_G_MODULATOR    _IOWR('V', 54, struct v4l2_modulator)#define VIDIOC_S_MODULATOR     _IOW('V', 55, struct v4l2_modulator)#define VIDIOC_G_FREQUENCY    _IOWR('V', 56, struct v4l2_frequency)#define VIDIOC_S_FREQUENCY     _IOW('V', 57, struct v4l2_frequency)#define VIDIOC_CROPCAP        _IOWR('V', 58, struct v4l2_cropcap)#define VIDIOC_G_CROP        _IOWR('V', 59, struct v4l2_crop)#define VIDIOC_S_CROP         _IOW('V', 60, struct v4l2_crop)#define VIDIOC_G_JPEGCOMP     _IOR('V', 61, struct v4l2_jpegcompression)#define VIDIOC_S_JPEGCOMP     _IOW('V', 62, struct v4l2_jpegcompression)#define VIDIOC_QUERYSTD           _IOR('V', 63, v4l2_std_id)#define VIDIOC_TRY_FMT          _IOWR('V', 64, struct v4l2_format)#define VIDIOC_ENUMAUDIO    _IOWR('V', 65, struct v4l2_audio)#define VIDIOC_ENUMAUDOUT    _IOWR('V', 66, struct v4l2_audioout)#define VIDIOC_G_PRIORITY        _IOR('V', 67, enum v4l2_priority)#define VIDIOC_S_PRIORITY        _IOW('V', 68, enum v4l2_priority)#define VIDIOC_G_SLICED_VBI_CAP _IOWR('V', 69, struct v4l2_sliced_vbi_cap)#define VIDIOC_LOG_STATUS         _IO('V', 70)#define VIDIOC_G_EXT_CTRLS    _IOWR('V', 71, struct v4l2_ext_controls)#define VIDIOC_S_EXT_CTRLS    _IOWR('V', 72, struct v4l2_ext_controls)#define VIDIOC_TRY_EXT_CTRLS    _IOWR('V', 73, struct v4l2_ext_controls)#if 1#define VIDIOC_ENUM_FRAMESIZES    _IOWR('V', 74, struct v4l2_frmsizeenum)#define VIDIOC_ENUM_FRAMEINTERVALS _IOWR('V', 75, struct v4l2_frmivalenum)#define VIDIOC_G_ENC_INDEX       _IOR('V', 76, struct v4l2_enc_idx)#define VIDIOC_ENCODER_CMD      _IOWR('V', 77, struct v4l2_encoder_cmd)#define VIDIOC_TRY_ENCODER_CMD  _IOWR('V', 78, struct v4l2_encoder_cmd)#endif#if 1/* Experimental, meant for debugging, testing and internal use.   Only implemented if CONFIG_VIDEO_ADV_DEBUG is defined.   You must be root to use these ioctls. Never use these in applications! */#define    VIDIOC_DBG_S_REGISTER      _IOW('V', 79, struct v4l2_dbg_register)#define    VIDIOC_DBG_G_REGISTER     _IOWR('V', 80, struct v4l2_dbg_register)/* Experimental, meant for debugging, testing and internal use.   Never use this ioctl in applications! */#define VIDIOC_DBG_G_CHIP_IDENT _IOWR('V', 81, struct v4l2_dbg_chip_ident)#endif#define VIDIOC_S_HW_FREQ_SEEK     _IOW('V', 82, struct v4l2_hw_freq_seek)#define    VIDIOC_ENUM_DV_PRESETS    _IOWR('V', 83, struct v4l2_dv_enum_preset)#define    VIDIOC_S_DV_PRESET    _IOWR('V', 84, struct v4l2_dv_preset)#define    VIDIOC_G_DV_PRESET    _IOWR('V', 85, struct v4l2_dv_preset)#define    VIDIOC_QUERY_DV_PRESET    _IOR('V',  86, struct v4l2_dv_preset)#define    VIDIOC_S_DV_TIMINGS    _IOWR('V', 87, struct v4l2_dv_timings)#define    VIDIOC_G_DV_TIMINGS    _IOWR('V', 88, struct v4l2_dv_timings)#define    VIDIOC_DQEVENT         _IOR('V', 89, struct v4l2_event)#define    VIDIOC_SUBSCRIBE_EVENT     _IOW('V', 90, struct v4l2_event_subscription)#define    VIDIOC_UNSUBSCRIBE_EVENT _IOW('V', 91, struct v4l2_event_subscription)/* Reminder: when adding new ioctls please add support for them to   drivers/media/video/v4l2-compat-ioctl32.c as well! */#define BASE_VIDIOC_PRIVATE    192        /* 192-255 are private *//* For ingenic xburst jz4780 */#define VIDIOC_SET_TLB_BASE         _IOW('V', BASE_VIDIOC_PRIVATE, unsigned int)#endif /* __LINUX_VIDEODEV2_H */
复制代码

  从__video_do_ioctl可以很容易看到,如何调用我们注册的ioctl_ios即ovisp_v4l2_ioctl_ops。关于这些IOCTL的功能在代码中也有简单的注释,这些都比较容易理解。关于每个IOCTL的细节,在以后看到重要的我们在进行详述。