虚拟视频驱动程序vivi.c源码分析
 
 以下先把上一篇文章中的最后一段,放在这里利于程序源码的分析:
 
 
 vivi.c 虚拟视频驱动程序----- 此代码模拟一个真正的视频设备V4L2 API (位于drivers/media/video目录下)
 
 
 入口:+int __init vivi_init(void)
 
 
                + vivi_create_instance(i) /*创建设备*//**/。
 
 
                        + 分配一个vivi_dev的结构体 /*它嵌套这结构体v4l2_device 和video_device*/
 
 
                        + v4l2_device_register(NULL, &dev->v4l2_dev);/*注册vivi_dev中的V4l2_device*/
 
 
                        + 初始化视频的DMA队列
 
 
                        + 初始化锁
 
 
                        + video_device_alloc(); 动态分配video_device结构体
 
 
                        + 构建一个video_device结构体 vivi_template 并赋给上面分配的video_device
 
 
                               static struct video_device vivi_template = {
 
 
                                         .name        = "vivi",
 
 
                                         .fops           = &vivi_fops,
 
 
                                         .ioctl_ops     = &vivi_ioctl_ops,
 
 
                                         .minor        = -1,
 
 
                                         .release    = video_device_release,
 
 
                                         .tvnorms              = V4L2_STD_525_60,
 
 
                                         .current_norm         = V4L2_STD_NTSC_M,
 
 
                                };
 
 
                      + video_set_drvdata(vfd, dev);设置驱动程序专有数据
 
 
                      + 所有控件设置为其默认值
 
 
                      + list_add_tail(&dev->vivi_devlist, &vivi_devlist);添加到设备列表
 
 
         + 构建 v4l2_file_operations 结构体vivi_fops 并实现.open .release .read .poll .mmap函数----- .ioctl 用标准的v4l2控制处理程序
 
 
         + 构建 v4l2_ioctl_ops结构体 vivi_ioctl_ops
 
 
                            static const struct v4l2_ioctl_ops vivi_ioctl_ops = {
 
 
                                       .vidioc_querycap      = vidioc_querycap,
 
 
                                       .vidioc_enum_fmt_vid_cap  = vidioc_enum_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_queryctrl     = vidioc_queryctrl,
 
 
                                       .vidioc_g_ctrl        = vidioc_g_ctrl,
 
 
                                       .vidioc_s_ctrl        = vidioc_s_ctrl,
 
 
                                       .vidioc_streamon      = vidioc_streamon,
 
 
                                       .vidioc_streamoff     = vidioc_streamoff,
 
 
                            #ifdef CONFIG_VIDEO_V4L1_COMPAT
 
 
                                       .vidiocgmbuf          = vidiocgmbuf,
 
 
                            #endif
 
 
                      };
 
 
          + int vivi_open(struct file *file)
 
 
                    + vivi_dev *dev = video_drvdata(file);  访问驱动程序专用数据
 
 
                    + 分配+初始化句柄(vivi_fh)数据
 
 
                    + 重置帧计数器
 
 
                    + videobuf_queue_vmalloc_init(); 初始化视频缓冲队列
 
 
                    + 开启一个新线程用于开始和暂停
 
 
          + 实现自定义的v4l2_ioctl_ops 函数
 
 
 
 
 现在开始分析程序源码,利于之后对V4L2驱动的开发,学习
 
 
 首先就行驱动的入口开始:
 
 
 
- static int __init vivi_init(void)
 
- {
 
-     const struct font_desc *font = find_font("VGA8x16");
 
-     int ret = 0, i;
 
 
-     if (font == NULL) {
 
-         printk(KERN_ERR "vivi: could not find font\n");
 
-         return -ENODEV;
 
-     }
 
-     font8x16 = font->data;
 
 
-     if (n_devs <= 0)
 
-         n_devs = 1;
 
 
-     for (i = 0; i < n_devs; i++) {
 
-         //Here is the most important
 
-         ret = vivi_create_instance(i);
 
-         if (ret) {
 
-             /* If some instantiations succeeded, keep driver */
 
-             if (i)
 
-                 ret = 0;
 
-             break;
 
-         }
 
-     }
 
 
-     if (ret < 0) {
 
-         printk(KERN_ERR "vivi: error %d while loading driver\n", ret);
 
-         return ret;
 
-     }
 
 
-     printk(KERN_INFO "Video Technology Magazine Virtual Video "
 
-             "Capture Board ver %u.%u.%u successfully loaded.\n",
 
-             (VIVI_VERSION >> 16) & 0xFF, (VIVI_VERSION >> 8) & 0xFF,
 
-             VIVI_VERSION & 0xFF);
 
 
-     /* n_devs will reflect the actual number of allocated devices */
 
-     n_devs = i;
 
 
-     return ret;
 
- }
 
 
- static void __exit vivi_exit(void)
 
- {
 
-     vivi_release();
 
- }
 
 
- module_init(vivi_init);
 
- module_exit(vivi_exit);
 
 这其实最重要的就是上面标注备份,下面重点分析
 vivi_create_instance
 方法:
 
 
 
- static int __init vivi_create_instance(int inst)
 
- {
 
-     struct vivi_dev *dev;
 
-     struct video_device *vfd;
 
-     struct v4l2_ctrl_handler *hdl;
 
-     struct vb2_queue *q;
 
-     int ret;
 
 
-     dev = kzalloc(sizeof(*dev), GFP_KERNEL);
 
-     if (!dev)
 
-         return -ENOMEM;
 
 
-     // set the v4l2_device(the name)
 
-     snprintf(dev->v4l2_dev.name, sizeof(dev->v4l2_dev.name),
 
-             "%s-%03d", VIVI_MODULE_NAME, inst);
 
-     
 
-     /* 
 
-     * register the v4l2_device, but you should pay attention here
 
-     * the "dev == NULL" it means v4l2_device.dev == NULL
 
-     * You did't set the v4l2_device.dev, you will set it later
 
-     */
 
-     ret = v4l2_device_register(NULL, &dev->v4l2_dev);
 
-     if (ret)
 
-         goto free_dev;
 
 
-     /* init the handle, learn it later */
 
-     dev->fmt = &formats[0];
 
-     dev->width = 640;
 
-     dev->height = 480;
 
-     hdl = &dev->ctrl_handler;
 
-     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->brightness = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops,
 
-             V4L2_CID_BRIGHTNESS, 0, 255, 1, 127);
 
-     dev->contrast = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops,
 
-             V4L2_CID_CONTRAST, 0, 255, 1, 16);
 
-     dev->saturation = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops,
 
-             V4L2_CID_SATURATION, 0, 255, 1, 127);
 
-     dev->hue = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops,
 
-             V4L2_CID_HUE, -128, 127, 1, 0);
 
-     dev->button = v4l2_ctrl_new_custom(hdl, &vivi_ctrl_button, NULL);
 
-     dev->int32 = v4l2_ctrl_new_custom(hdl, &vivi_ctrl_int32, NULL);
 
-     dev->int64 = v4l2_ctrl_new_custom(hdl, &vivi_ctrl_int64, NULL);
 
-     dev->boolean = v4l2_ctrl_new_custom(hdl, &vivi_ctrl_boolean, NULL);
 
-     dev->menu = v4l2_ctrl_new_custom(hdl, &vivi_ctrl_menu, NULL);
 
-     dev->string = v4l2_ctrl_new_custom(hdl, &vivi_ctrl_string, NULL);
 
-     if (hdl->error) {
 
-         ret = hdl->error;
 
-         goto unreg_dev;
 
-     }
 
-     dev->v4l2_dev.ctrl_handler = hdl;
 
 
-     /* initialize locks */
 
-     spin_lock_init(&dev->slock);
 
 
-     /* initialize queue, learn it later */
 
-     q = &dev->vb_vidq;
 
-     memset(q, 0, sizeof(dev->vb_vidq));
 
-     q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
 
-     q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_READ;
 
-     q->drv_priv = dev;
 
-     q->buf_struct_size = sizeof(struct vivi_buffer);
 
-     q->ops = &vivi_video_qops;
 
-     q->mem_ops = &vb2_vmalloc_memops;
 
 
-     vb2_queue_init(q);
 
 
-     mutex_init(&dev->mutex);
 
 
-     /* init video dma queues */
 
-     INIT_LIST_HEAD(&dev->vidq.active);
 
-     init_waitqueue_head(&dev->vidq.wq);
 
 
-     /* before register the video_device, init the video_device data*/
 
-     ret = -ENOMEM;
 
-     vfd = video_device_alloc();
 
-     if (!vfd)
 
-         goto unreg_dev;
 
 
-     *vfd = vivi_template;/* the most important struct */
 
-     vfd->debug = debug;
 
-     vfd->v4l2_dev = &dev->v4l2_dev; /* here set the v4l2_device, you have already registered it */
 
-     set_bit(V4L2_FL_USE_FH_PRIO, &vfd->flags);
 
 
-     /*
 
-      * Provide a mutex to v4l2 core. It will be used to protect
 
-      * all fops and v4l2 ioctls.
 
-      */
 
-     vfd->lock = &dev->mutex;
 
 
-     ret = video_register_device(vfd, VFL_TYPE_GRABBER, video_nr);
 
-     if (ret < 0)
 
-         goto rel_vdev;
 
 
-     /*
 
-     * You should pay attention to this method
 
-     * here you set the vivi_dev into the vedio_device for the later use in fops
 
-     * When you want to use the vivi_dev, you use vedio_get_drvdata() to get
 
-     */
 
-     video_set_drvdata(vfd, dev);
 
 
-     /* Now that everything is fine, let's add it to device list */
 
-     list_add_tail(&dev->vivi_devlist, &vivi_devlist);
 
 
-     if (video_nr != -1)
 
-         video_nr++;
 
 
-     dev->vfd = vfd;
 
 
-     /* the debug message*/
 
-     v4l2_info(&dev->v4l2_dev, "V4L2 device registered as %s\n",
 
-          video_device_node_name(vfd));
 
-     return 0;
 
 
- rel_vdev:
 
-     video_device_release(vfd);
 
- unreg_dev:
 
-     v4l2_ctrl_handler_free(hdl);
 
-     v4l2_device_unregister(&dev->v4l2_dev);
 
- free_dev:
 
-     kfree(dev);
 
- return ret;
- }
 
 vivi_create_instance
 方法中主要完成以下工作;
 
 
 1.首先通过v4l2_device_register() 方法注册 v4l2_device
 
 
 2.ctrl_handler初始化
 
 
 3.互斥锁,自旋锁等初始化
 
 
 4.vb2_quene初始化
 
 
 5.init video dma queues
 
 
 6.
 填充video_device,并且调用
 
 video_register_device注册video_device
 
 
 7.把vivi_dev结构set进video_device中,
 方便之后使用,使用
 video_set_drvdata
 (
 vfd
 ,
  dev
 )
 ;
 
 
 
 8.设备信息加入的链表结构
 
 
 
 
 下面针对以上步骤做详细分析:
 
 
 1.首先通过v4l2_device_register() 方法注册 v4l2_device
 
 
- int v4l2_device_register(struct device *dev, struct v4l2_device *v4l2_dev)
 
- {
 
-     if (v4l2_dev == NULL)
 
-         return -EINVAL;
 
 
-     INIT_LIST_HEAD(&v4l2_dev->subdevs);
 
-     spin_lock_init(&v4l2_dev->lock);
 
-     mutex_init(&v4l2_dev->ioctl_lock);
 
 
-     /* initial the global priotity*/
 
-     v4l2_prio_init(&v4l2_dev->prio);
 
-     kref_init(&v4l2_dev->ref);
 
-     v4l2_dev->dev = dev;
 
-     if (dev == NULL) {
 
-         /* If dev == NULL, then name must be filled in by the caller */
 
-         WARN_ON(!v4l2_dev->name[0]);
 
-         /* Here give the caller a WARN, tell the caller to set the dev*/
 
-         return 0;
 
-     }
 
 
-     /* Set name to driver name + device name if it is empty. */
 
-     if (!v4l2_dev->name[0])
 
-         snprintf(v4l2_dev->name, sizeof(v4l2_dev->name), "%s %s",
 
-             dev->driver->name, dev_name(dev));
 
 
-     /* Here is also very important, you can get v4l2_device by use dev_get_drvdata*/
 
-     if (!dev_get_drvdata(dev))
 
-         dev_set_drvdata(dev, v4l2_dev);
 
-     return 0;
 
- }
 
- EXPORT_SYMBOL_GPL(v4l2_device_register);
 在
 v4l2_device_register方法中,进行v4l2设备优先级的初始化,这里把v4l2_device中记录优先级状态的v4l2_prio_state结构变量
 prio清空,
 
 另外这里说一下kref结构定义的ref变量,这个变量保存打开设备的计数,这里第一次注册设备,初始化ref为1,若之后重复注册则会先检查ref这个变量,
 
 如果ref为1,则表示已经注册过了,避免重复注册v4l2_device,这个初始化在kref_init方法中实现,这是我的理解,
 可是我暂时还没有找到检查ref的地方,暂且跳过
 
 
 最后一步根据dev 结构体决定,如果dev不为空,则这里setv4l2_device的name,并且将v4l2_device结构set进dev中,方便后面获取使用
 
 
 
 
 2.
 ctrl_handler初始化
 
 
- /* Initialize the handler */
 
- int v4l2_ctrl_handler_init(struct v4l2_ctrl_handler *hdl,
 
-              unsigned nr_of_controls_hint)
 
- {
 
-     mutex_init(&hdl->lock);
 
-     INIT_LIST_HEAD(&hdl->ctrls);
 
-     INIT_LIST_HEAD(&hdl->ctrl_refs);
 
-     hdl->nr_of_buckets = 1 + nr_of_controls_hint / 8;
 
-     hdl->buckets = kzalloc(sizeof(hdl->buckets[0]) * hdl->nr_of_buckets,
 
-                                 GFP_KERNEL);
 
-     hdl->error = hdl->buckets ? 0 : -ENOMEM;
 
-     return hdl->error;
 
- }
 这里还是很有必要了解一下v4l2_ctrl_handler这个结构体到底是干什么用的
 
 
 
- /** struct v4l2_ctrl_handler - The control handler keeps track of all the
 
-   * controls: both the controls owned by the handler and those inherited
 
-   * from other handlers.
 
-   * @lock:    Lock to control access to this handler and its controls.
 
-   * @ctrls:    The list of controls owned by this handler.
 
-   * @ctrl_refs:    The list of control references.
 
-   * @cached:    The last found control reference. It is common that the same
 
-   *        control is needed multiple times, so this is a simple
 
-   *        optimization.
 
-   * @buckets:    Buckets for the hashing. Allows for quick control lookup.
 
-   * @nr_of_buckets: Total number of buckets in the array.
 
-   * @error:    The error code of the first failed control addition.
 
-   */
 
- struct v4l2_ctrl_handler {
 
-     struct mutex lock;
 
-     struct list_head ctrls;
 
-     struct list_head ctrl_refs;
 
-     struct v4l2_ctrl_ref *cached;
 
-     struct v4l2_ctrl_ref **buckets;
 
-     u16 nr_of_buckets;
 
-     int error;
 
- };
 在v4l2_ctrl_handler_init方法中,主要通过nr_of_controls_hint变量的大小,计算nr_of_buckets,并为buckets申请空间,并将申请结果保存在error变量中,我感觉可以是用于以后方便check的
 
 dev->v4l2_dev.ctrl_handler = hdl;最后,关联vivi_dev
 
 
 问题点:
 
 
 1.
 v4l2_ctrl_new_std
 
 
 2.
 v4l2_ctrl_new_custom
 
 
 以上两个方法不是很理解,待以后研究
 
 
 
 
 
 
 3.互斥锁,自旋锁等
 初始化
 这个比较简单,就不在做说明了
 
 
 
 4.vb2_quene初始化
 首先还是很有必要看一下这个结构体
 
 
- /**
 
-  * struct vb2_queue - a videobuf queue
 
-  *
 
-  * @type:    queue type (see V4L2_BUF_TYPE_* in linux/videodev2.h
 
-  * @io_modes:    supported io methods (see vb2_io_modes enum)
 
-  * @io_flags:    additional io flags (see vb2_fileio_flags enum)
 
-  * @ops:    driver-specific callbacks
 
-  * @mem_ops:    memory allocator specific callbacks
 
-  * @drv_priv:    driver private data
 
-  * @buf_struct_size: size of the driver-specific buffer structure;
 
-  *        "0" indicates the driver doesn't want to use a custom buffer
 
-  *        structure type, so sizeof(struct vb2_buffer) will is used
 
-  *
 
-  * @memory:    current memory type used
 
-  * @bufs:    videobuf buffer structures
 
-  * @num_buffers: number of allocated/used buffers
 
-  * @queued_list: list of buffers currently queued from userspace
 
-  * @queued_count: number of buffers owned by the driver
 
-  * @done_list:    list of buffers ready to be dequeued to userspace
 
-  * @done_lock:    lock to protect done_list list
 
-  * @done_wq:    waitqueue for processes waiting for buffers ready to be dequeued
 
-  * @alloc_ctx:    memory type/allocator-specific contexts for each plane
 
-  * @streaming:    current streaming state
 
-  * @fileio:    file io emulator internal data, used only if emulator is active
 
-  */
 
- struct vb2_queue {
 
-     enum v4l2_buf_type        type;
 
-     unsigned int            io_modes;
 
-     unsigned int            io_flags;
 
 
-     const struct vb2_ops        *ops;
 
-     const struct vb2_mem_ops    *mem_ops;
 
-     void                *drv_priv;
 
-     unsigned int            buf_struct_size;
 
 
- /* private: internal use only */
 
-     enum v4l2_memory        memory;
 
-     struct vb2_buffer        *bufs[VIDEO_MAX_FRAME];
 
-     unsigned int            num_buffers;
 
 
-     struct list_head        queued_list;
 
 
-     atomic_t            queued_count;
 
-     struct list_head        done_list;
 
-     spinlock_t            done_lock;
 
-     wait_queue_head_t        done_wq;
 
 
-     void                *alloc_ctx[VIDEO_MAX_PLANES];
 
 
-     unsigned int            streaming:1;
 
 
-     struct vb2_fileio_data        *fileio;
 
- };
 
 在
 v4l2_ctrl_handler_init方法中,首先对
 vb2_quene其中的重要数据进行填充,最最重要的就是
 
 q->ops = &vivi_video_qops;
 
 q->mem_ops = &vb2_vmalloc_memops;
 
 
 这两条简单的复制语句责任重大啊,
 这里先知道有这么一回事,后面补充
 
 
 
- /**
 
-  * struct vb2_ops - driver-specific callbacks
 
-  *
 
-  * @queue_setup:    called from a VIDIOC_REQBUFS handler, before
 
-  *            memory allocation; driver should return the required
 
-  *            number of buffers in num_buffers, the required number
 
-  *            of planes per buffer in num_planes; the size of each
 
-  *            plane should be set in the sizes[] array and optional
 
-  *            per-plane allocator specific context in alloc_ctxs[]
 
-  *            array
 
-  * @wait_prepare:    release any locks taken while calling vb2 functions;
 
-  *            it is called before an ioctl needs to wait for a new
 
-  *            buffer to arrive; required to avoid a deadlock in
 
-  *            blocking access type
 
-  * @wait_finish:    reacquire all locks released in the previous callback;
 
-  *            required to continue operation after sleeping while
 
-  *            waiting for a new buffer to arrive
 
-  * @buf_init:        called once after allocating a buffer (in MMAP case)
 
-  *            or after acquiring a new USERPTR buffer; drivers may
 
-  *            perform additional buffer-related initialization;
 
-  *            initialization failure (return != 0) will prevent
 
-  *            queue setup from completing successfully; optional
 
-  * @buf_prepare:    called every time the buffer is queued from userspace;
 
-  *            drivers may perform any initialization required before
 
-  *            each hardware operation in this callback;
 
-  *            if an error is returned, the buffer will not be queued
 
-  *            in driver; optional
 
-  * @buf_finish:        called before every dequeue of the buffer back to
 
-  *            userspace; drivers may perform any operations required
 
-  *            before userspace accesses the buffer; optional
 
-  * @buf_cleanup:    called once before the buffer is freed; drivers may
 
-  *            perform any additional cleanup; optional
 
-  * @start_streaming:    called once before entering 'streaming' state; enables
 
-  *            driver to receive buffers over buf_queue() callback
 
-  * @stop_streaming:    called when 'streaming' state must be disabled; driver
 
-  *            should stop any DMA transactions or wait until they
 
-  *            finish and give back all buffers it got from buf_queue()
 
-  *            callback; may use vb2_wait_for_all_buffers() function
 
-  * @buf_queue:        passes buffer vb to the driver; driver may start
 
-  *            hardware operation on this buffer; driver should give
 
-  *            the buffer back by calling vb2_buffer_done() function
 
-  */
 
- struct vb2_ops {
 
-     int (*queue_setup)(struct vb2_queue *q, unsigned int *num_buffers,
 
-              unsigned int *num_planes, unsigned long sizes[],
 
-              void *alloc_ctxs[]);
 
 
-     void (*wait_prepare)(struct vb2_queue *q);
 
-     void (*wait_finish)(struct vb2_queue *q);
 
 
-     int (*buf_init)(struct vb2_buffer *vb);
 
-     int (*buf_prepare)(struct vb2_buffer *vb);
 
-     int (*buf_finish)(struct vb2_buffer *vb);
 
-     void (*buf_cleanup)(struct vb2_buffer *vb);
 
 
-     int (*start_streaming)(struct vb2_queue *q);
 
-     int (*stop_streaming)(struct vb2_queue *q);
 
 
-     void (*buf_queue)(struct vb2_buffer *vb);
 
- };
- /**
 
-  * struct vb2_mem_ops - memory handling/memory allocator operations
 
-  * @alloc:    allocate video memory and, optionally, allocator private data,
 
-  *        return NULL on failure or a pointer to allocator private,
 
-  *        per-buffer data on success; the returned private structure
 
-  *        will then be passed as buf_priv argument to other ops in this
 
-  *        structure
 
-  * @put:    inform the allocator that the buffer will no longer be used;
 
-  *        usually will result in the allocator freeing the buffer (if
 
-  *        no other users of this buffer are present); the buf_priv
 
-  *        argument is the allocator private per-buffer structure
 
-  *        previously returned from the alloc callback
 
-  * @get_userptr: acquire userspace memory for a hardware operation; used for
 
-  *         USERPTR memory types; vaddr is the address passed to the
 
-  *         videobuf layer when queuing a video buffer of USERPTR type;
 
-  *         should return an allocator private per-buffer structure
 
-  *         associated with the buffer on success, NULL on failure;
 
-  *         the returned private structure will then be passed as buf_priv
 
-  *         argument to other ops in this structure
 
-  * @put_userptr: inform the allocator that a USERPTR buffer will no longer
 
-  *         be used
 
-  * @vaddr:    return a kernel virtual address to a given memory buffer
 
-  *        associated with the passed private structure or NULL if no
 
-  *        such mapping exists
 
-  * @cookie:    return allocator specific cookie for a given memory buffer
 
-  *        associated with the passed private structure or NULL if not
 
-  *        available
 
-  * @num_users:    return the current number of users of a memory buffer;
 
-  *        return 1 if the videobuf layer (or actually the driver using
 
-  *        it) is the only user
 
-  * @mmap:    setup a userspace mapping for a given memory buffer under
 
-  *        the provided virtual memory region
 
-  *
 
-  * Required ops for USERPTR types: get_userptr, put_userptr.
 
-  * Required ops for MMAP types: alloc, put, num_users, mmap.
 
-  * Required ops for read/write access types: alloc, put, num_users, vaddr
 
-  */
 
- struct vb2_mem_ops {
 
-     void        *(*alloc)(void *alloc_ctx, unsigned long size);
 
-     void        (*put)(void *buf_priv);
 
 
-     void        *(*get_userptr)(void *alloc_ctx, unsigned long vaddr,
 
-                     unsigned long size, int write);
 
-     void        (*put_userptr)(void *buf_priv);
 
 
-     void        *(*vaddr)(void *buf_priv);
 
-     void        *(*cookie)(void *buf_priv);
 
 
-     unsigned int    (*num_users)(void *buf_priv);
 
 
-     int        (*mmap)(void *buf_priv, struct vm_area_struct *vma);
 
- };
 
 最后调用
 vb2_queue_init
 方法,进行初始化
 
 
 
- /**
 
-  * vb2_queue_init() - initialize a videobuf2 queue
 
-  * @q:        videobuf2 queue; this structure should be allocated in driver
 
-  *
 
-  * The vb2_queue structure should be allocated by the driver. The driver is
 
-  * responsible of clearing it's content and setting initial values for some
 
-  * required entries before calling this function.
 
-  * q->ops, q->mem_ops, q->type and q->io_modes are mandatory. Please refer
 
-  * to the struct vb2_queue description in include/media/videobuf2-core.h
 
-  * for more information.
 
-  */
 
- int vb2_queue_init(struct vb2_queue *q)
 
- {
 
-     BUG_ON(!q);
 
-     BUG_ON(!q->ops);
 
-     BUG_ON(!q->mem_ops);
 
-     BUG_ON(!q->type);
 
-     BUG_ON(!q->io_modes);
 
 
-     BUG_ON(!q->ops->queue_setup);
 
-     BUG_ON(!q->ops->buf_queue);
 
 
-     INIT_LIST_HEAD(&q->queued_list);
 
-     INIT_LIST_HEAD(&q->done_list);
 
-     spin_lock_init(&q->done_lock);
 
-     init_waitqueue_head(&q->done_wq);
 
 
-     if (q->buf_struct_size == 0)
 
-         q->buf_struct_size = sizeof(struct vb2_buffer);
 
 
-     return 0;
 
- }
 
- EXPORT_SYMBOL_GPL(vb2_queue_init);
 
 这里方法很简单,只是进行check,然后最最简单的初始化,这里不再多说了
 
 
 
 
 5.
 
 init video dma queues
 只有两条语句进行初始化
 INIT_LIST_HEAD(&dev->vidq.active);
 init_waitqueue_head(&dev->vidq.wq);
 
 
 
 6.
 填充video_device,并且调用
 
 video_register_device注册video_device
 这里终于到了重点了,很重要,开始了
 开始简单,申请内存空间并进行填充,然后才真正调用用video_register_device这个方法,开始了
 但是在调用之前的这条语句你必须关注vfd->v4l2_dev = &dev->v4l2_dev;从这里也可以知道v4l2_device和video_device的注册顺序
 
 
- int __video_register_device(struct video_device *vdev, int type, int nr,
 
-         int warn_if_nr_in_use, struct module *owner)
 
- {
 
-     int i = 0;
 
-     int ret;
 
-     int minor_offset = 0;
 
-     int minor_cnt = VIDEO_NUM_DEVICES;
 
-     const char *name_base;
 
 
-     /* A minor value of -1 marks this video device as never
 
-      having been registered */
 
-     vdev->minor = -1;
 
 
-     /* the release callback MUST be present */
 
-     WARN_ON(!vdev->release);
 
-     if (!vdev->release)
 
-         return -EINVAL;
 
 
-     /* v4l2_fh support */
 
-     spin_lock_init(&vdev->fh_lock);
 
-     INIT_LIST_HEAD(&vdev->fh_list);
 
 
-     /* Part 1: check device type */
 
-     /* after here, you can see videx ...the char device in /dev */
 
- //这里还是单独说一下吧,最终你在/dev目录下看到的video0就是在这里决定的,大家可以知道,可不是一定名字叫video的
-     switch (type) {
 
-     case VFL_TYPE_GRABBER:
 
-         name_base = "video";
 
-         break;
 
-     case VFL_TYPE_VBI:
 
-         name_base = "vbi";
 
-         break;
 
-     case VFL_TYPE_RADIO:
 
-         name_base = "radio";
 
-         break;
 
-     case VFL_TYPE_SUBDEV:
 
-         name_base = "v4l-subdev";
 
-         break;
 
-     default:
 
-         printk(KERN_ERR "%s called with unknown type: %d\n",
 
-          __func__, type);
 
-         return -EINVAL;
 
-     }
 
 
-     vdev->vfl_type = type;
 
- vdev->cdev = NULL;
-     if (vdev->v4l2_dev) {
 
-         if (vdev->v4l2_dev->dev)
 
-             //这里说明vdev和保存在v4l2_device的dev具有共同的parent,对之后sys 接口那里有用
 
-             vdev->parent = vdev->v4l2_dev->dev;
 
-         if (vdev->ctrl_handler == NULL)
 
-             //上面初始化的ctrl_handler在这里要派上用处了,而且同时指向video_device和v4l2_device的化ctrl_handler
 
-             vdev->ctrl_handler = vdev->v4l2_dev->ctrl_handler;
 
-         /* If the prio state pointer is NULL, then use the v4l2_device
 
-          prio state. */
 
-         if (vdev->prio == NULL)
 
-             //上面初始化的prio在这里要派上用处了,而且同时指向video_device和v4l2_device的化prio
 
-             vdev->prio = &vdev->v4l2_dev->prio;
 
-     }
 
-     /* Part 2: find a free minor, device node number and device index. */
 
- #ifdef CONFIG_VIDEO_FIXED_MINOR_RANGES
 
-     /* Keep the ranges for the first four types for historical
 
-      * reasons.
 
-      * Newer devices (not yet in place) should use the range
 
-      * of 128-191 and just pick the first free minor there
 
-      * (new style). */
 
-     switch (type) {
 
-     case VFL_TYPE_GRABBER:
 
-         minor_offset = 0;
 
-         minor_cnt = 64;
 
-         break;
 
-     case VFL_TYPE_RADIO:
 
-         minor_offset = 64;
 
-         minor_cnt = 64;
 
-         break;
 
-     case VFL_TYPE_VBI:
 
-         minor_offset = 224;
 
-         minor_cnt = 32;
 
-         break;
 
-     default:
 
-         minor_offset = 128;
 
-         minor_cnt = 64;
 
-         break;
 
-     }
 
- #endif
 
 
-     /* Pick a device node number */
 
-     mutex_lock(&videodev_lock);
 
-     nr = devnode_find(vdev, nr == -1 ? 0 : nr, minor_cnt);
 
-     if (nr == minor_cnt)
 
-         nr = devnode_find(vdev, 0, minor_cnt);
 
-     if (nr == minor_cnt) {
 
-         printk(KERN_ERR "could not get a free device node number\n");
 
-         mutex_unlock(&videodev_lock);
 
-         return -ENFILE;
 
-     }
 
- #ifdef CONFIG_VIDEO_FIXED_MINOR_RANGES
 
-     /* 1-on-1 mapping of device node number to minor number */
 
-     i = nr;
 
- #else
 
-     /* The device node number and minor numbers are independent, so
 
-      we just find the first free minor number. */
 
-     for (i = 0; i < VIDEO_NUM_DEVICES; i++)
 
-         if (video_device[i] == NULL)
 
-             break;
 
-     if (i == VIDEO_NUM_DEVICES) {
 
-         mutex_unlock(&videodev_lock);
 
-         printk(KERN_ERR "could not get a free minor\n");
 
-         return -ENFILE;
 
-     }
 
- #endif
 
-     vdev->minor = i + minor_offset;
 
-     vdev->num = nr;
 
-     devnode_set(vdev);
 
 
-     /* Should not happen since we thought this minor was free */
 
-     WARN_ON(video_device[vdev->minor] != NULL);
 
-     vdev->index = get_index(vdev);
 
-     mutex_unlock(&videodev_lock);
 
 
-     /* Part 3: Initialize the character device */
 
-     vdev->cdev = cdev_alloc();
 
-     if (vdev->cdev == NULL) {
 
-         ret = -ENOMEM;
 
-         goto cleanup;
 
-     }
 
-     vdev->cdev->ops = &v4l2_fops;//most important part,操作设备的通道
 
-     vdev->cdev->owner = owner;
 
-     ret = cdev_add(vdev->cdev, MKDEV(VIDEO_MAJOR, vdev->minor), 1);
 
-     if (ret < 0) {
 
-         printk(KERN_ERR "%s: cdev_add failed\n", __func__);
 
-         kfree(vdev->cdev);
 
-         vdev->cdev = NULL;
 
-         goto cleanup;
 
-     }
 
 
-     /* Part 4: register the device with sysfs */
 
-     vdev->dev.class = &video_class;
 
-     vdev->dev.devt = MKDEV(VIDEO_MAJOR, vdev->minor);
 
-     if (vdev->parent)
 
-         vdev->dev.parent = vdev->parent;
 
-     dev_set_name(&vdev->dev, "%s%d", name_base, vdev->num);
 
-     ret = device_register(&vdev->dev);
 
-     if (ret < 0) {
 
-         printk(KERN_ERR "%s: device_register failed\n", __func__);
 
-         goto cleanup;
 
-     }
 
-     /* Register the release callback that will be called when the last
 
-      reference to the device goes away. */
 
-     vdev->dev.release = v4l2_device_release;
 
 
-     if (nr != -1 && nr != vdev->num && warn_if_nr_in_use)
 
-         printk(KERN_WARNING "%s: requested %s%d, got %s\n", __func__,
 
-             name_base, nr, video_device_node_name(vdev));
 
 
-     /* Increase v4l2_device refcount */
 
-     if (vdev->v4l2_dev)
 
-         v4l2_device_get(vdev->v4l2_dev);
 
 
- #if defined(CONFIG_MEDIA_CONTROLLER)
 
//意思就是如果这个驱动中需要用到media controler的时候就需要在这里注册media_device
//这里同样先不做深入研究,media_device和media_entity这两个重要结构体之后还要研究
-     /* Part 5: Register the entity. */
 
-     if (vdev->v4l2_dev && vdev->v4l2_dev->mdev &&
 
-      vdev->vfl_type != VFL_TYPE_SUBDEV) {
 
-         vdev->entity.type = MEDIA_ENT_T_DEVNODE_V4L;
 
-         vdev->entity.name = vdev->name;
 
-         vdev->entity.v4l.major = VIDEO_MAJOR;
 
-         vdev->entity.v4l.minor = vdev->minor;
 
-         ret = media_device_register_entity(vdev->v4l2_dev->mdev,
 
-             &vdev->entity);
 
-         if (ret < 0)
 
-             printk(KERN_WARNING
 
-              "%s: media_device_register_entity failed\n",
 
-              __func__);
 
-     }
 
- #endif
 
-     //保存注册成功标记,并将注册成功的video_device保存到全局数组video_device中,大功告成
 
-     /* Part 6: Activate this minor. The char device can now be used. */
 
-     set_bit(V4L2_FL_REGISTERED, &vdev->flags);//设置标志位,之后还会遇到test_bit方法用来check flags的第nr位是否为1.
 
-     //这里还是多说一点,另外还有两中标志位需要知道:V4L2_FL_USES_V4L2_FH, V4L2_FL_USE_FH_PRIO
 
-     mutex_lock(&videodev_lock);
 
-     video_device[vdev->minor] = vdev;
 
-     mutex_unlock(&videodev_lock);
 
 
-     return 0;
 
- cleanup:
 
-     mutex_lock(&videodev_lock);
 
-     if (vdev->cdev)
 
-         cdev_del(vdev->cdev);
 
-     devnode_clear(vdev);
 
-     mutex_unlock(&videodev_lock);
 
-     /* Mark this video device as never having been registered. */
 
-     vdev->minor = -1;
 
-     return ret;
 
- }
 
- EXPORT_SYMBOL(__video_register_device);
 
 
 
 7.把vivi_dev结构set进video_device中,
 方便之后使用,
 设备信息加入的链表结构
 
 
 最后结尾的这段代码这里我决定单独放在下面分析,也算妥善收尾吧
 
 
 
-     /*
 
-     * You should pay attention to this method
 
-     * here you set the vivi_dev into the vedio_device for the later use in fops
 
-     * When you want to use the vivi_dev, you use vedio_get_drvdata() to get
 
-     */
 
-     video_set_drvdata(vfd, dev);
 
 
-     /* Now that everything is fine, let's add it to device list */
 
-     list_add_tail(&dev->vivi_devlist, &vivi_devlist);//添加的device list当中
 
 
-     if (video_nr != -1)
 
-         video_nr++;//用于计数,找到设备
 
 
- dev->vfd = vfd;关联video_device和vivi_dev
 
 短短的几条语句,但我看来,个个都短小精悍
 
 
 首先说说这个方法,他有什么用处呢?其实用处可大了,就行我上面注释的那样,这里把vivi_dev设置到vedio_device中,是为了之后字符设备访问接口中使用
 
 
 这里多说一点,也算顺便说一下用户空间操作设备的流程了
 
 
 首先当时用户空间访问设备了,这个做驱动的不懂那可糗大了,用户空间open时,也就是启动了上面video_device_register方法中的很重要的下面结构中的open方法
 
 
 
- 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,
 
- };
 
 我们来看一下这个open方法
 
 
 
- /* Override for the open function */
 
- static int v4l2_open(struct inode *inode, struct file *filp)
 
- {
 
-     struct video_device *vdev;
 
-     int ret = 0;
 
 
-     /* Check if the video device is available */
 
-     mutex_lock(&videodev_lock);
 
-     vdev = video_devdata(filp);
 
-     /* return ENODEV if the video device has already been removed. */
 
-     if (vdev == NULL || !video_is_registered(vdev)) {
 
-         mutex_unlock(&videodev_lock);
 
-         return -ENODEV;
 
-     }
 
-     /* and increase the device refcount */
 
-     video_get(vdev);//这里是用来计数的
 
-     mutex_unlock(&videodev_lock);
 
 
-     /* 
 
-     * Here using the API you get the method you get the open() method write
 
-     * The other methods in fops use the same method to use you own code 
 
-     */
 
-     if (vdev->fops->open) {
 
-         if (vdev->lock && mutex_lock_interruptible(vdev->lock)) {
 
-             ret = -ERESTARTSYS;
 
-             goto err;
 
-         }
 
-         if (video_is_registered(vdev))
 
-             ret = vdev->fops->open(filp);
 
-         else
 
-             ret = -ENODEV;
 
-         if (vdev->lock)
 
-             mutex_unlock(vdev->lock);
 
-     }
 
 
- err:
 
-     /* decrease the refcount in case of an error */
 
-     if (ret)
 
-         video_put(vdev);
 
-     return ret;
 
- }
 
 只有最下面的那个标准才是重点,经过那么多的check,最后走的了最后这一步,
 ret 
 =
  vdev
 -
 >
 fops
 -
 >
 open
 (
 filp
 )
 ;
 
 
 这个open方法在哪里呢?那就沿着箭头方向找吧,是video_device内部的fops中的open方法,这个方法不是有在哪里呢?
 
 
 接着找,在
 video_device_register方法之前
 
 
 *vfd = vivi_template;/* the most important struct */我还特意在这里写了最重要的结构体
 
 
 
 所以最终调用的是
 vivi_template
 中fops中定义的open方法
 
 
 
- 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,
 
- };
 
 
- 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,
 
- };
 
 我们找到了,就是
 v4l2_fh_open
 这个方法,这个方法与我们之前写字符驱动时遇到的情况很是不同,他的open方法其实是有内核API写好的,我们先看看
 
 
 
- int v4l2_fh_open(struct file *filp)
 
- {
 
-     struct video_device *vdev = video_devdata(filp);
 
-     struct v4l2_fh *fh = kzalloc(sizeof(*fh), GFP_KERNEL);
 
 
-     /*
 
-     * IN the open method, do only one job
 
-     * set v4l2_fh into filp->private_data for later use, and initial v4l2_fh
 
-     */
 
-     filp->private_data = fh;
 
-     if (fh == NULL)
 
-         return -ENOMEM;
 
-     v4l2_fh_init(fh, vdev);
 
-     v4l2_fh_add(fh);
 
-     return 0;
 
- }
 
- EXPORT_SYMBOL_GPL(v4l2_fh_open);
 
 这个open方法将v4l2_fh 这个同样很重要的结构体保存到filp->private中,并做一些初始化,具体过程暂且不说
 
 
 这里fops中的其他接口的实现比起open,方法是一样的,而且更简单
 
 
 
 
 但是,这里我真正想表达的东西还没有出现,大家看到这里的
 filp
 -
 >
 private_data 
 =
  fh
 ;//问题就在这里了,我们真正在read,write中需要传递的想要使用的是vivi_dev其实,
 而不是v4l2_fh这个数据结构,我们还是直接看看
 vivi_template
 中fops中
 定义的read方法吧
 
 
 
- 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_dev这个数据结构,并传递到vb2_read这个方法中,这个方法时驱动中数据传输的关键,之后再慢慢研究,这里同样不深究
 
 
 而这个vivi_dev是通过video_drvdata方法获得的,这就是为什么在上面我强调的要用
 
 
 
 video_set_drvdata
 (
 vfd
 ,
  dev
 )
 ;
 把vivi_dev装载到video_device中
 
 
 
 
 到这里基本的过程都整理完了,其实大头还在后面呢,待续。。。。。。 
 
                    
                     
                    
                 
                    
                
 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号