虚拟视频驱动程序vivi.c源码分析-笔记

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;
     }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值