转自: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的细节,在以后看到重要的我们在进行详述。