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;
}
本文深入探讨V4L2框架中的核心组件v4l2_device、v4l2_subdev及video_device的作用与实现细节,揭示它们之间的关联及其在视频编程中的应用。
555

被折叠的 条评论
为什么被折叠?



