一、V4L2应用层调用流程
二、V4L2设备注册
三、video设备初始化
四、V4L2 control结构框架图
五、v4l2 ctrl 函数初始化—增加标准接口v4l2_ctrl_new_std
六、v4l2 ctrl 函数初始化—增加自定义接口v4l2_ctrl_new_custom
七、V4L2 ioctl 标准接口 调用流程
八、V4L2 ioctl 控制接口 调用流程
V4L2 ioctl调用流程
当video节点注册完成之后,用户层就会通过 该节点的字符设备接口接入到内核中去
在 三、video设备初始化中就讲解了video的初始化,vdev->cdev->ops = &v4l2_fops; 代表着用户层的ioctl会调用到v4l2_fops的unlocked_ioctl = v4l2_ioctl;
结构体 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,
#ifdef CONFIG_COMPAT
.compat_ioctl = v4l2_compat_ioctl32,
#endif
.release = v4l2_release,
.poll = v4l2_poll,
.llseek = no_llseek,
};
v4l2_ioctl
static long v4l2_ioctl(struct file filp, unsigned int cmd, unsigned long arg)
{
struct video_device *vdev = video_devdata(flip);
int ret = -ENODEV;
if (vdev->fops->unlocked_ioctl) {
if (video_is_registered(vdev))
//这里从kernel的ioctl 就调用到具体video设备的ioctl上去了
ret = vdev->fops->unlocked_ioctl(flip, cmd, arg)
else
ret = -ENOTTY;
}
return ret;
}
接下来看下具体设备的fops的结构体
static const struct v4l2_file_operations xxx_v4l2_fops = {
.owner = THIS_MODULE,
.open = xxx_v4l2_fop_open,
.release = xxx_v4l2_fop_close,
.read = xxx_v4l2_fop_read,
.poll = xxx_v4l2_fop_poll,
.unlocked_ioctl = video_ioctl2,
.mmap = xxx_v4l2_fop_mmap,
};
从用户层的ioctl的调用直接调用到了 具体设备的unlocked_ioctl
unlocked_ioctl 又调用到了kernel的video_ioctl2
video_ioctl2
long video_ioctl2(struct file *file,
unsigned int cmd, unsigned long arg)
{
return video_usercopy(file, cmd, arg, __video_do_ioctl);
}
__video_do_ioctl
static long __video_do_ioctl(struct file *file, unsigned int cmd, void *arg)
{
struct video_device *vfd = video_devdata(file);
struct mutex *req_queue_lock = NULL;
struct mutex *lock;
const struct v4l2_ioctl_ops *ops = vfd->ioctl_ops;
struct write_only = false;
struct v4l2_ioctl_info default_info;
const struct v4l2_ioctl_info *info;
void *fh = file->private_data;
struct v4l2_fh *vfh = NULL;
int dev_debug = vfd->dev_debug;
long ret = -ENOTTY;
if (ops == NULL) {
pr_warn("%s: has no ioctl_ops.\n",
video_device_node_name(vfd));
return ret;
}
if (test_bit(V4L2_FL_USES_V4L2_FH, &vfd->flags))
vfh = file->private_data;
....
/*
假如执行的是
ioctl(fd, VIDIOC_QUERYCAP, &cap)
info = v4l2_ioctls[0]组的地址
*/
if (v4l2_is_know_ioctl(cmd)) {
info = &v4l2_ioctls[_IOC_NR(cmd)];
}
ret = info->func(ops, file, fh, arg);
}
v4l2_is_known_ioctl
static bool v4l2_is_known_ioctl(unsigned int cmd)
{
if (_IOC_NR(cmd) >= V4L2_IOCTLS)
return false;
return v4l2_ioctls[_IOC_NR(cmd)].ioctl == cmd;
}
v4l2_ioctl_info 结构体
struct v4l2_ioctl_info {
unsigned int ioctl;
u32 flags;
const char * const name;
int (*func)(const struct v4l2_ioctl_ops *ops, struct file *file,
void *fh, void *p);
void (*debug)(const void *arg, bool write_only);
};
static const struct v4l2_ioctl_info v4l2_ioctls[] = {
IOCTL_INFO(VIDIOC_QUERYCAP, v4l_querycap, v4l_print_querycap, 0),
}
#define IOCTL_INFO(_ioctl, _func, _debug, _flags) \
[_IOC_NR(_ioctl)] = { \
.ioctl = _ioctl, \
.flags = _flags, \
.name = #_ioctl, \
.func = _func, \
.debug = _debug, \
}
info->func最后就指向了v4l_querycap
在前面的初始化中 const struct v4l2_ioctl_ops *ops = vfd->ioctl_ops;
static int v4l_querycap(const struct v4l2_ioctl_ops *ops,
struct file *file, void *fh, void *arg)
{
struct v4l2_capability *cap = (struct v4l2_capability *)arg;
struct video_device *vfd = video_devdata(file);
cap->version = LINUX_VERSION_CODE;
cap->device_caps = vfd->device_caps;
cap->capabilities = vfd->device_caps | V4L2_CAP_DEVICE_CAPS;
ret = ops->vidioc_querycap(file, fh, cap); //从这里调用到了 平台自定义的驱动中
cap->capabilities |= V4L2_CAP_EXT_PIX_FORMAT;
}