一、V4L2应用层调用流程
二、V4L2设备注册
三、video设备初始化
四、V4L2 control结构框架图
五、v4l2 ctrl 函数初始化—增加标准接口v4l2_ctrl_new_std
六、v4l2 ctrl 函数初始化—增加自定义接口v4l2_ctrl_new_custom
七、V4L2 ioctl 标准接口 调用流程
八、V4L2 ioctl 控制接口 调用流程
video_device结构体
struct video_device
{
#if defined (CONFIG_MEDIA_CONTROLLER)
struct media_entity entity;
struct media_intf_devnode *intf_devnode;
struct media_pipeline pipe;
#endif
const struct v4l2_file_operations *fops;
u32 device_caps;
/* sysfs */
struct device dev;
struct cdev *cdev;
struct v4l2_device *v4l2_dev;
struct device *dev_parent;
struct v4l2_ctrl_handler *ctrl_handler;
struct vb2_queue *queue;
struct v4l2_prio_state *prio;
/* device info */
char name[32];
int vfl_type;
int vfl_dir;
int minor;
u16 num;
unsigned long flags;
int index;
/* V4L2 file handles */
spinlock_t fh_lock;
struct list_head fh_list;
int dev_debug;
v4l2_std_id tvnorms;
/* callbacks */
void (*release)(struct video_device *vdev);
const struct v4l2_ioctl_ops *ioctl_ops;
DECLARE_BITMAP(valid_ioctls, BASE_VIDIOC_PRIVATE);
DECLARE_BITMAP(disable_locking, BASE_VIDIOC_PRIVATE);
struct mutex *lock;
};
video设备也是一个字符设备,具备字符设备的open、close、read、wirte、ioctl功能
v4l2_file_operations结构体初始化定义
struct v4l2_file_operations xxx_fops = {
.owner = THIS_MODULE,
.open = xxx_open,
.release = xxx_close,
.read = xxx_read,
.poll = xxx_p_poll,
.unlocked_ioctl = video_ioctl2,
.mmap = xxx_mmap,
};
v4l2_ioctl_ops结构体初始化定义
struct v4l2_ioctl_ops xxx_v4l2_ioctl_ops = {
.vidioc_querycap = xxx_v4l2_querycap,
/* Per-stream config operations */
.vidioc_g_fmt_vid_cap_mplane = xxx_v4l2_g_fmt_vid_cap,
.vidioc_enum_fmt_vid_cap = xxx_v4l2_enum_fmt_vid_cap,
.vidioc_try_fmt_vid_cap_mplane = xxx_v4l2_try_fmt_vid_cap,
.vidioc_s_fmt_vid_cap_mplane = xxx_v4l2_s_fmt_vid_cap,
.vidioc_g_fmt_vid_cap = xxx_v4l2_g_fmt_vid_cap,
.vidioc_enum_fmt_vid_cap = xxx_v4l2_enum_fmt_vid_cap,
.vidioc_try_fmt_vid_cap = xxx_v4l2_try_fmt_vid_cap,
.vidioc_s_fmt_vid_cap = xxx_v4l2_s_fmt_vid_cap,
.vidioc_enum_framesizes = xxx_v4l2_enum_framesizes,
/* Per-stream control operations */
.vidioc_streamon = xxx_v4l2_streamon,
.vidioc_streamoff = xxx_v4l2_streamoff,
/* input control */
.vidioc_enum_input = xxx_v4l2_enum_input,
.vidioc_g_input = xxx_v4l2_g_input,
.vidioc_s_input = xxx_v4l2_s_input,
/* vb2 customization for multi-stream support */
.vidioc_reqbufs = xxx_v4l2_reqbufs,
.vidioc_querybuf = xxx_v4l2_querybuf,
.vidioc_qbuf = xxx_v4l2_qbuf,
.vidioc_dqbuf = xxx_v4l2_dqbuf,
/* v4l2 event ioctls */
.vidioc_log_status = xxx_ctrl_log_status,
.vidioc_subscribe_event = xxx_ctrl_subscribe_event,
.vidioc_unsubscribe_event = xxx_event_unsubscribe,
/* crop ioctls */
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0))
.vidioc_g_selection = xxx_v4l2_g_selection,
.vidioc_s_selection = xxx_v4l2_s_selection,
.vidioc_g_pixelaspect = xxx_v4l2_g_pixelaspect,
#else
.vidioc_cropcap = xxx_v4l2_cropcap,
.vidioc_g_crop = xxx_v4l2_g_crop,
.vidioc_s_crop = xxx_v4l2_s_crop,
#endif
.vidioc_expbuf = xxx_v4l2_expbuf,
};
以上的xxx函数都是各个平台最终实现的函数功能,左边的由用户程序调用到kernel驱动最终调用到平台函数
video_register_device注册
struct video_device *vdev;
vdev= &dev->video_dev;
strlcpy( vfd->name, "xxx", sizeof( vfd->name ) );
vdev->fops = &xxx_v4l2_fops;
vdev->ioctl_ops = &xxx_v4l2_ioctl_ops;
vdev->release = video_device_release_empty;
vdev->v4l2_dev = dev->v4l2_dev;
vdev->queue = NULL; // queue will be customized in file handle
vdev->tvnorms = tvnorms_cap;
vdev->vfl_type = VFL_TYPE_GRABBER;
vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING | V4L2_CAP_READWRITE;
//VFL_TYPE_GRABBER代表注册的是video节点
//0代表的是注册video0节点
video_register_device( vdev, VFL_TYPE_GRABBER, 0);//代表创建注册video0节点
static inline int __must_check video_register_device(struct video_device *vdev, int type, int nr)
{
return __video_register_device(vdev, type, nr, 1, vdev->fops->owner);
}
int __video_register_device(struct video_device *vdev, int type, int nr, int warn_if_nr_use, struct module *owner)
{
...
/* v4l2_fh support */
spin_lock_init(&vdev->fh_lock);
INIT_LIST_HEAD(&vdev->fh_list);
switch(type)
{
case VFL_TYPE_GRABBER:
name_base = "video";
break;
}
...
vdev->vfl_type = type;
vdev->cdev = NULL;
if (vdev->dev_parent == NULL)
vdev->dev_parent = vdev->v4l2_dev->dev;
if (vdev->ctrl_handler == NULL)
vdev->ctrl_handler = vdev->vdev_dev->ctrl_handler;
if (vdev->prio == NULL)
vdev->prio = &vdev->v4l2_dev->prio;
switch (type) {
case VFL_TYPE_GRABBER:
minor_offset = 0;
minor_cnt = 64;
break;
}
...
vdev->minor = i + minor_offset;
vdev->num = nr;
devnode_set(vdev);
video_device[vdev->minor] = vdev;//把vdev保存到了 video_device[x]
...
vdev->cdev->ops = &v4l2_fops;
ret = cdev_add(vdev->cdev, MKDEV(VIDEO_MAJOR, vdev->minor), 1);
vdev->dev.class = &video_class;
vdev->dev.devt = MKDEV(VIDEO_MAJOR, vdev->minor);
vdev->dev.parent = vdev->dev_parent;
dev_set_name(&vdev->dev, "%s%d", name_base, vdev->num);
ret = device_register(&vdev->dev);
}