v4l2_device v4l2_device在v4l2框架中充当所有v4l2_subdev的父设备,管理着注册在其下的子设备。 可以看出v4l2_device的主要作用是管理注册在其下的子设备,方便系统查找引用到。 V4l2_subdev V4l2_subdev代表子设备,包含了子设备的相关属性和操作。 每个子设备驱动都需要实现一个v4l2_subdev结构体,v4l2_subdev可以内嵌到其它结构体中,也可以独立使用。结构体中包含了对子设备操作的成员v4l2_subdev_ops和v4l2_subdev_internal_ops video_device video_device结构体用于在/dev目录下生成设备节点文件,把操作设备的接口暴露给用户空间。 ===================================================================================== vivi_create_instance(i);/*创建设备*/ { struct vivi_dev *dev;/*它嵌套这结构体v4l2_device 和video_device*/ dev = kzalloc(sizeof(*dev), GFP_KERNEL);//分配vivi_dev空间 snprintf(dev->v4l2_dev.name, sizeof(dev->v4l2_dev.name), "%s-%03d", VIVI_MODULE_NAME, inst);//设备名称(不知道这个名字用来干嘛) v4l2_device_register(NULL, &dev->v4l2_dev);/*注册vivi_dev中的V4l2_device*/ v4l2_ctrl_handler_init(hdl, 11); dev->volume = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops, V4L2_CID_AUDIO_VOLUME, 0, 255, 1, 200); dev->string = v4l2_ctrl_new_custom(hdl, &vivi_ctrl_string, NULL); .. dev->v4l2_dev.ctrl_handler = hdl;//关联vivi_dev q->ops = &vivi_video_qops; q->mem_ops = &vb2_vmalloc_memops; //在v4l2_ctrl_handler_init方法中,首先对vb2_quene其中的重要数据进行填充,最最重要的就是 //q->ops = &vivi_video_qops; //q->mem_ops = &vb2_vmalloc_memops; //这两条简单的复制语句责任重大(还不清楚为什么重要) vb2_queue_init(q);// 最后调用vb2_queue_init方法,进行初始化 } ------------------------------------------------------------------------- vfd = video_device_alloc();// struct video_device申请空间 *vfd = vivi_template; vfd->v4l2_dev = &dev->v4l2_dev; video_register_device(vfd, VFL_TYPE_GRABBER, video_nr);//在/dev目录下生成设备节点文件 { vdev->vfl_type = type; vdev->parent = vdev->v4l2_dev->dev;//这里说明vdev和保存在v4l2_device的dev具有共同的parent,对之后sys 接口那里有用 vdev->ctrl_handler = vdev->v4l2_dev->ctrl_handler; //上面初始化的ctrl_handler在这里要派上用处了,而且同时指向video_device和v4l2_device的化ctrl_handler vdev->prio = &vdev->v4l2_dev->prio; //上面初始化的prio在这里要派上用处了,而且同时指向video_device和v4l2_device的化prio /*上面的方法获取到了那个合适的设备号,现在要开始注册我们的字符设备了 */ vdev->cdev = cdev_alloc(); vdev->cdev->ops = &v4l2_fops;//most important part,应用程序操作设备的通道 vdev->cdev->owner = owner; ret = cdev_add(vdev->cdev, MKDEV(VIDEO_MAJOR, vdev->minor), 1); dev_set_name(&vdev->dev, "%s%d", name_base, vdev->num);//设置/dev设备名字 ret = device_register(&vdev->dev); vdev->dev.release = v4l2_device_release; video_device[vdev->minor] = vdev; //保存注册成功标记,并将注册成功的video_device保存到全局数组video_device中,大功告成 } --------------------------------------------------------------------------------------- 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, }; static int v4l2_open(struct inode *inode, struct file *filp) { vdev = video_devdata(filp); video_get(vdev);//这里是用来计数的 ret = vdev->fops->open(filp); //static const struct v4l2_file_operations vivi_fops = { // .open = v4l2_fh_open, } int v4l2_fh_open(struct file *filp) { struct video_device *vdev = video_devdata (filp); struct v4l2_fh *fh = kzalloc(sizeof(*fh), GFP_KERNEL); filp->private_data = fh; //问题就在这里了,我们真正在read,write中需要传递的想要使用的是vivi_dev其实, //而不是v4l2_fh这个数据结构 if (fh == NULL) return -ENOMEM; v4l2_fh_init(fh, vdev); v4l2_fh_add(fh); return 0; } static ssize_t vivi_read(struct file *file, char __user *data, size_t count, loff_t *ppos) { struct vivi_dev *dev = video_drvdata(file); dprintk(dev, 1, "read called\n"); return vb2_read(&dev->vb_vidq, data, count, ppos, file->f_flags & O_NONBLOCK); } //vivi_read方法获取到vivi_dev这个数据结构,并传递到vb2_read这个方法中,这个方法时驱动中数据传输的关键,而这个vivi_dev是通过video_drvdata方法获得的,这就是为什么在上面我强调的要用 //video_set_drvdata(vfd, dev);把vivi_dev装载到video_device中 --------------------------------------------------------------------------------- static struct video_device vivi_template = { .name = "vivi", .fops = &vivi_fops, .ioctl_ops = &vivi_ioctl_ops, .release = video_device_release, .tvnorms = V4L2_STD_525_60, .current_norm = V4L2_STD_NTSC_M, }; static const struct v4l2_file_operations vivi_fops = { .owner = THIS_MODULE, .open = v4l2_fh_open, .release = vivi_close, .read = vivi_read, .poll = vivi_poll, .unlocked_ioctl = video_ioctl2, /* V4L2 ioctl handler */ .mmap = vivi_mmap, }; static const struct v4l2_ioctl_ops vivi_ioctl_ops = { .vidioc_querycap = vidioc_querycap, .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap, .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap, .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, .vidioc_reqbufs = vidioc_reqbufs, .vidioc_querybuf = vidioc_querybuf, .vidioc_qbuf = vidioc_qbuf, .vidioc_dqbuf = vidioc_dqbuf, .vidioc_s_std = vidioc_s_std, .vidioc_enum_input = vidioc_enum_input, .vidioc_g_input = vidioc_g_input, .vidioc_s_input = vidioc_s_input, .vidioc_streamon = vidioc_streamon, .vidioc_streamoff = vidioc_streamoff, .vidioc_log_status = v4l2_ctrl_log_status, .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, .vidioc_unsubscribe_event = v4l2_event_unsubscribe, }; struct V4l2_device{ /* DEV-> driver_data指向这个结构。 注:DEV可能是空的,如果没有父设备是如同ISA设备。 */ struct device *dev; /* 用于跟踪注册的subdevs */ struct list_head subdevs; /*锁定此结构体;可以使用的驱动程序以及如果这个结构嵌入到一个更大的结构。 */ spinlock_t lock; /* 独特的设备名称,默认情况下,驱动程序姓名+总线ID */ char name[V4L2_DEVICE_NAME_SIZE]; /*报告由一些子设备调用的回调函数。 */ void (*notify)(struct v4l2_subdev *sd, unsigned int notification, void *arg); };
v4l2_device注册和注销
v4l2_device_register(struct device *dev, struct v4l2_device *v4l2_dev);
第一个参数‘dev’通常是一个pci_dev的struct device的指针,但它是ISA设备或一个设备创建多个PCI设备时这是罕见的DEV为NULL,因此makingit不可能联想到一个特定的父母 v4l2_dev。 您也可以提供一个notify()回调子设备,可以通过调用通知你的事件。取决于你是否需要设置子设备。一个子设备支持的任何通知必须在头文件中定义 .
注册时将初始化 v4l2_device 结构体. 如果 dev->driver_data字段是空, 它将连接到 v4l2_dev.
v4l2_device_unregister(struct v4l2_device *v4l2_dev);
注销也将自动注销设备所有子设备。
2.video_device (进行视频编程时v4l的最重要也是最常用功能)
在/dev目录下的设备节点使用的 struct video_device(v4l2_dev.h)创建。
struct video_device { /*设备操作函数 */ const struct v4l2_file_operations *fops; /* 虚拟文件系统 */ struct device dev; /* v4l 设备 */ struct cdev *cdev; /* 字符设备 */ struct device *parent; /*父设备 */ struct v4l2_device *v4l2_dev; /* v4l2_device parent */ /* 设备信息 */ char name[32]; int vfl_type; /* 'minor' is set to -1 if the registration failed */ int minor; u16 num; /* use bitops to set/clear/test flags */ unsigned long flags; /*属性来区分一个物理设备上的多个索引 */ int index; /* V4L2 文件句柄 */ spinlock_t fh_lock; /*锁定所有的 v4l2_fhs */ struct list_head fh_list; /* List of struct v4l2_fh */ int debug; /* Activates debug level*/ /* Video standard vars */ v4l2_std_id tvnorms; /* Supported tv norms */ v4l2_std_id current_norm; /* Current tvnorm */ /* 释放的回调函数 */ void (*release)(struct video_device *vdev); /* 控制的回调函数 */ const struct v4l2_ioctl_ops *ioctl_ops; }