虚拟视频驱动程序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驱动的开发,学习
首先就行驱动的入口开始:

  1. static int __init vivi_init(void)
  2. {
  3. const struct font_desc *font = find_font("VGA8x16");
  4. int ret = 0, i;
  5. if (font == NULL) {
  6. printk(KERN_ERR "vivi: could not find font\n");
  7. return -ENODEV;
  8. }
  9. font8x16 = font->data;
  10. if (n_devs <= 0)
  11. n_devs = 1;
  12. for (i = 0; i < n_devs; i++) {
  13. //Here is the most important
  14. ret = vivi_create_instance(i);
  15. if (ret) {
  16. /* If some instantiations succeeded, keep driver */
  17. if (i)
  18. ret = 0;
  19. break;
  20. }
  21. }
  22. if (ret < 0) {
  23. printk(KERN_ERR "vivi: error %d while loading driver\n", ret);
  24. return ret;
  25. }
  26. printk(KERN_INFO "Video Technology Magazine Virtual Video "
  27. "Capture Board ver %u.%u.%u successfully loaded.\n",
  28. (VIVI_VERSION >> 16) & 0xFF, (VIVI_VERSION >> 8) & 0xFF,
  29. VIVI_VERSION & 0xFF);
  30. /* n_devs will reflect the actual number of allocated devices */
  31. n_devs = i;
  32. return ret;
  33. }
  34. static void __exit vivi_exit(void)
  35. {
  36. vivi_release();
  37. }
  38. module_init(vivi_init);
  39. module_exit(vivi_exit);

这其实最重要的就是上面标注备份,下面重点分析 vivi_create_instance 方法:

  1. static int __init vivi_create_instance(int inst)
  2. {
  3. struct vivi_dev *dev;
  4. struct video_device *vfd;
  5. struct v4l2_ctrl_handler *hdl;
  6. struct vb2_queue *q;
  7. int ret;
  8. dev = kzalloc(sizeof(*dev), GFP_KERNEL);
  9. if (!dev)
  10. return -ENOMEM;
  11. // set the v4l2_device(the name)
  12. snprintf(dev->v4l2_dev.name, sizeof(dev->v4l2_dev.name),
  13. "%s-%03d", VIVI_MODULE_NAME, inst);
  14. /*
  15. * register the v4l2_device, but you should pay attention here
  16. * the "dev == NULL" it means v4l2_device.dev == NULL
  17. * You did't set the v4l2_device.dev, you will set it later
  18. */
  19. ret = v4l2_device_register(NULL, &dev->v4l2_dev);
  20. if (ret)
  21. goto free_dev;
  22. /* init the handle, learn it later */
  23. dev->fmt = &formats[0];
  24. dev->width = 640;
  25. dev->height = 480;
  26. hdl = &dev->ctrl_handler;
  27. v4l2_ctrl_handler_init(hdl, 11);
  28. dev->volume = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops,
  29. V4L2_CID_AUDIO_VOLUME, 0, 255, 1, 200);
  30. dev->brightness = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops,
  31. V4L2_CID_BRIGHTNESS, 0, 255, 1, 127);
  32. dev->contrast = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops,
  33. V4L2_CID_CONTRAST, 0, 255, 1, 16);
  34. dev->saturation = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops,
  35. V4L2_CID_SATURATION, 0, 255, 1, 127);
  36. dev->hue = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops,
  37. V4L2_CID_HUE, -128, 127, 1, 0);
  38. dev->button = v4l2_ctrl_new_custom(hdl, &vivi_ctrl_button, NULL);
  39. dev->int32 = v4l2_ctrl_new_custom(hdl, &vivi_ctrl_int32, NULL);
  40. dev->int64 = v4l2_ctrl_new_custom(hdl, &vivi_ctrl_int64, NULL);
  41. dev->boolean = v4l2_ctrl_new_custom(hdl, &vivi_ctrl_boolean, NULL);
  42. dev->menu = v4l2_ctrl_new_custom(hdl, &vivi_ctrl_menu, NULL);
  43. dev->string = v4l2_ctrl_new_custom(hdl, &vivi_ctrl_string, NULL);
  44. if (hdl->error) {
  45. ret = hdl->error;
  46. goto unreg_dev;
  47. }
  48. dev->v4l2_dev.ctrl_handler = hdl;
  49. /* initialize locks */
  50. spin_lock_init(&dev->slock);
  51. /* initialize queue, learn it later */
  52. q = &dev->vb_vidq;
  53. memset(q, 0, sizeof(dev->vb_vidq));
  54. q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
  55. q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_READ;
  56. q->drv_priv = dev;
  57. q->buf_struct_size = sizeof(struct vivi_buffer);
  58. q->ops = &vivi_video_qops;
  59. q->mem_ops = &vb2_vmalloc_memops;
  60. vb2_queue_init(q);
  61. mutex_init(&dev->mutex);
  62. /* init video dma queues */
  63. INIT_LIST_HEAD(&dev->vidq.active);
  64. init_waitqueue_head(&dev->vidq.wq);
  65. /* before register the video_device, init the video_device data*/
  66. ret = -ENOMEM;
  67. vfd = video_device_alloc();
  68. if (!vfd)
  69. goto unreg_dev;
  70. *vfd = vivi_template;/* the most important struct */
  71. vfd->debug = debug;
  72. vfd->v4l2_dev = &dev->v4l2_dev; /* here set the v4l2_device, you have already registered it */
  73. set_bit(V4L2_FL_USE_FH_PRIO, &vfd->flags);
  74. /*
  75. * Provide a mutex to v4l2 core. It will be used to protect
  76. * all fops and v4l2 ioctls.
  77. */
  78. vfd->lock = &dev->mutex;
  79. ret = video_register_device(vfd, VFL_TYPE_GRABBER, video_nr);
  80. if (ret < 0)
  81. goto rel_vdev;
  82. /*
  83. * You should pay attention to this method
  84. * here you set the vivi_dev into the vedio_device for the later use in fops
  85. * When you want to use the vivi_dev, you use vedio_get_drvdata() to get
  86. */
  87. video_set_drvdata(vfd, dev);
  88. /* Now that everything is fine, let's add it to device list */
  89. list_add_tail(&dev->vivi_devlist, &vivi_devlist);
  90. if (video_nr != -1)
  91. video_nr++;
  92. dev->vfd = vfd;
  93. /* the debug message*/
  94. v4l2_info(&dev->v4l2_dev, "V4L2 device registered as %s\n",
  95. video_device_node_name(vfd));
  96. return 0;
  97. rel_vdev:
  98. video_device_release(vfd);
  99. unreg_dev:
  100. v4l2_ctrl_handler_free(hdl);
  101. v4l2_device_unregister(&dev->v4l2_dev);
  102. free_dev:
  103. kfree(dev);
  104. return ret;
  105. }

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

  1. int v4l2_device_register(struct device *dev, struct v4l2_device *v4l2_dev)
  2. {
  3. if (v4l2_dev == NULL)
  4. return -EINVAL;
  5. INIT_LIST_HEAD(&v4l2_dev->subdevs);
  6. spin_lock_init(&v4l2_dev->lock);
  7. mutex_init(&v4l2_dev->ioctl_lock);
  8. /* initial the global priotity*/
  9. v4l2_prio_init(&v4l2_dev->prio);
  10. kref_init(&v4l2_dev->ref);
  11. v4l2_dev->dev = dev;
  12. if (dev == NULL) {
  13. /* If dev == NULL, then name must be filled in by the caller */
  14. WARN_ON(!v4l2_dev->name[0]);
  15. /* Here give the caller a WARN, tell the caller to set the dev*/
  16. return 0;
  17. }
  18. /* Set name to driver name + device name if it is empty. */
  19. if (!v4l2_dev->name[0])
  20. snprintf(v4l2_dev->name, sizeof(v4l2_dev->name), "%s %s",
  21. dev->driver->name, dev_name(dev));
  22. /* Here is also very important, you can get v4l2_device by use dev_get_drvdata*/
  23. if (!dev_get_drvdata(dev))
  24. dev_set_drvdata(dev, v4l2_dev);
  25. return 0;
  26. }
  27. 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初始化

  1. /* Initialize the handler */
  2. int v4l2_ctrl_handler_init(struct v4l2_ctrl_handler *hdl,
  3. unsigned nr_of_controls_hint)
  4. {
  5. mutex_init(&hdl->lock);
  6. INIT_LIST_HEAD(&hdl->ctrls);
  7. INIT_LIST_HEAD(&hdl->ctrl_refs);
  8. hdl->nr_of_buckets = 1 + nr_of_controls_hint / 8;
  9. hdl->buckets = kzalloc(sizeof(hdl->buckets[0]) * hdl->nr_of_buckets,
  10. GFP_KERNEL);
  11. hdl->error = hdl->buckets ? 0 : -ENOMEM;
  12. return hdl->error;
  13. }

这里还是很有必要了解一下v4l2_ctrl_handler这个结构体到底是干什么用的

  1. /** struct v4l2_ctrl_handler - The control handler keeps track of all the
  2. * controls: both the controls owned by the handler and those inherited
  3. * from other handlers.
  4. * @lock:    Lock to control access to this handler and its controls.
  5. * @ctrls:    The list of controls owned by this handler.
  6. * @ctrl_refs:    The list of control references.
  7. * @cached:    The last found control reference. It is common that the same
  8. *        control is needed multiple times, so this is a simple
  9. *        optimization.
  10. * @buckets:    Buckets for the hashing. Allows for quick control lookup.
  11. * @nr_of_buckets: Total number of buckets in the array.
  12. * @error:    The error code of the first failed control addition.
  13. */
  14. struct v4l2_ctrl_handler {
  15. struct mutex lock;
  16. struct list_head ctrls;
  17. struct list_head ctrl_refs;
  18. struct v4l2_ctrl_ref *cached;
  19. struct v4l2_ctrl_ref **buckets;
  20. u16 nr_of_buckets;
  21. int error;
  22. };

在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初始化
首先还是很有必要看一下这个结构体

  1. /**
  2. * struct vb2_queue - a videobuf queue
  3. *
  4. * @type:    queue type (see V4L2_BUF_TYPE_* in linux/videodev2.h
  5. * @io_modes:    supported io methods (see vb2_io_modes enum)
  6. * @io_flags:    additional io flags (see vb2_fileio_flags enum)
  7. * @ops:    driver-specific callbacks
  8. * @mem_ops:    memory allocator specific callbacks
  9. * @drv_priv:    driver private data
  10. * @buf_struct_size: size of the driver-specific buffer structure;
  11. *        "0" indicates the driver doesn't want to use a custom buffer
  12. *        structure type, so sizeof(struct vb2_buffer) will is used
  13. *
  14. * @memory:    current memory type used
  15. * @bufs:    videobuf buffer structures
  16. * @num_buffers: number of allocated/used buffers
  17. * @queued_list: list of buffers currently queued from userspace
  18. * @queued_count: number of buffers owned by the driver
  19. * @done_list:    list of buffers ready to be dequeued to userspace
  20. * @done_lock:    lock to protect done_list list
  21. * @done_wq:    waitqueue for processes waiting for buffers ready to be dequeued
  22. * @alloc_ctx:    memory type/allocator-specific contexts for each plane
  23. * @streaming:    current streaming state
  24. * @fileio:    file io emulator internal data, used only if emulator is active
  25. */
  26. struct vb2_queue {
  27. enum v4l2_buf_type        type;
  28. unsigned int            io_modes;
  29. unsigned int            io_flags;
  30. const struct vb2_ops        *ops;
  31. const struct vb2_mem_ops    *mem_ops;
  32. void                *drv_priv;
  33. unsigned int            buf_struct_size;
  34. /* private: internal use only */
  35. enum v4l2_memory        memory;
  36. struct vb2_buffer        *bufs[VIDEO_MAX_FRAME];
  37. unsigned int            num_buffers;
  38. struct list_head        queued_list;
  39. atomic_t            queued_count;
  40. struct list_head        done_list;
  41. spinlock_t            done_lock;
  42. wait_queue_head_t        done_wq;
  43. void                *alloc_ctx[VIDEO_MAX_PLANES];
  44. unsigned int            streaming:1;
  45. struct vb2_fileio_data        *fileio;
  46. };

在 v4l2_ctrl_handler_init方法中,首先对 vb2_quene其中的重要数据进行填充,最最重要的就是
q->ops = &vivi_video_qops;
q->mem_ops = &vb2_vmalloc_memops;
这两条简单的复制语句责任重大啊, 这里先知道有这么一回事,后面补充

  1. /**
  2. * struct vb2_ops - driver-specific callbacks
  3. *
  4. * @queue_setup:    called from a VIDIOC_REQBUFS handler, before
  5. *            memory allocation; driver should return the required
  6. *            number of buffers in num_buffers, the required number
  7. *            of planes per buffer in num_planes; the size of each
  8. *            plane should be set in the sizes[] array and optional
  9. *            per-plane allocator specific context in alloc_ctxs[]
  10. *            array
  11. * @wait_prepare:    release any locks taken while calling vb2 functions;
  12. *            it is called before an ioctl needs to wait for a new
  13. *            buffer to arrive; required to avoid a deadlock in
  14. *            blocking access type
  15. * @wait_finish:    reacquire all locks released in the previous callback;
  16. *            required to continue operation after sleeping while
  17. *            waiting for a new buffer to arrive
  18. * @buf_init:        called once after allocating a buffer (in MMAP case)
  19. *            or after acquiring a new USERPTR buffer; drivers may
  20. *            perform additional buffer-related initialization;
  21. *            initialization failure (return != 0) will prevent
  22. *            queue setup from completing successfully; optional
  23. * @buf_prepare:    called every time the buffer is queued from userspace;
  24. *            drivers may perform any initialization required before
  25. *            each hardware operation in this callback;
  26. *            if an error is returned, the buffer will not be queued
  27. *            in driver; optional
  28. * @buf_finish:        called before every dequeue of the buffer back to
  29. *            userspace; drivers may perform any operations required
  30. *            before userspace accesses the buffer; optional
  31. * @buf_cleanup:    called once before the buffer is freed; drivers may
  32. *            perform any additional cleanup; optional
  33. * @start_streaming:    called once before entering 'streaming' state; enables
  34. *            driver to receive buffers over buf_queue() callback
  35. * @stop_streaming:    called when 'streaming' state must be disabled; driver
  36. *            should stop any DMA transactions or wait until they
  37. *            finish and give back all buffers it got from buf_queue()
  38. *            callback; may use vb2_wait_for_all_buffers() function
  39. * @buf_queue:        passes buffer vb to the driver; driver may start
  40. *            hardware operation on this buffer; driver should give
  41. *            the buffer back by calling vb2_buffer_done() function
  42. */
  43. struct vb2_ops {
  44. int (*queue_setup)(struct vb2_queue *q, unsigned int *num_buffers,
  45. unsigned int *num_planes, unsigned long sizes[],
  46. void *alloc_ctxs[]);
  47. void (*wait_prepare)(struct vb2_queue *q);
  48. void (*wait_finish)(struct vb2_queue *q);
  49. int (*buf_init)(struct vb2_buffer *vb);
  50. int (*buf_prepare)(struct vb2_buffer *vb);
  51. int (*buf_finish)(struct vb2_buffer *vb);
  52. void (*buf_cleanup)(struct vb2_buffer *vb);
  53. int (*start_streaming)(struct vb2_queue *q);
  54. int (*stop_streaming)(struct vb2_queue *q);
  55. void (*buf_queue)(struct vb2_buffer *vb);
  56. };
  1. /**
  2. * struct vb2_mem_ops - memory handling/memory allocator operations
  3. * @alloc:    allocate video memory and, optionally, allocator private data,
  4. *        return NULL on failure or a pointer to allocator private,
  5. *        per-buffer data on success; the returned private structure
  6. *        will then be passed as buf_priv argument to other ops in this
  7. *        structure
  8. * @put:    inform the allocator that the buffer will no longer be used;
  9. *        usually will result in the allocator freeing the buffer (if
  10. *        no other users of this buffer are present); the buf_priv
  11. *        argument is the allocator private per-buffer structure
  12. *        previously returned from the alloc callback
  13. * @get_userptr: acquire userspace memory for a hardware operation; used for
  14. *         USERPTR memory types; vaddr is the address passed to the
  15. *         videobuf layer when queuing a video buffer of USERPTR type;
  16. *         should return an allocator private per-buffer structure
  17. *         associated with the buffer on success, NULL on failure;
  18. *         the returned private structure will then be passed as buf_priv
  19. *         argument to other ops in this structure
  20. * @put_userptr: inform the allocator that a USERPTR buffer will no longer
  21. *         be used
  22. * @vaddr:    return a kernel virtual address to a given memory buffer
  23. *        associated with the passed private structure or NULL if no
  24. *        such mapping exists
  25. * @cookie:    return allocator specific cookie for a given memory buffer
  26. *        associated with the passed private structure or NULL if not
  27. *        available
  28. * @num_users:    return the current number of users of a memory buffer;
  29. *        return 1 if the videobuf layer (or actually the driver using
  30. *        it) is the only user
  31. * @mmap:    setup a userspace mapping for a given memory buffer under
  32. *        the provided virtual memory region
  33. *
  34. * Required ops for USERPTR types: get_userptr, put_userptr.
  35. * Required ops for MMAP types: alloc, put, num_users, mmap.
  36. * Required ops for read/write access types: alloc, put, num_users, vaddr
  37. */
  38. struct vb2_mem_ops {
  39. void        *(*alloc)(void *alloc_ctx, unsigned long size);
  40. void        (*put)(void *buf_priv);
  41. void        *(*get_userptr)(void *alloc_ctx, unsigned long vaddr,
  42. unsigned long size, int write);
  43. void        (*put_userptr)(void *buf_priv);
  44. void        *(*vaddr)(void *buf_priv);
  45. void        *(*cookie)(void *buf_priv);
  46. unsigned int    (*num_users)(void *buf_priv);
  47. int        (*mmap)(void *buf_priv, struct vm_area_struct *vma);
  48. };

最后调用 vb2_queue_init 方法,进行初始化

  1. /**
  2. * vb2_queue_init() - initialize a videobuf2 queue
  3. * @q:        videobuf2 queue; this structure should be allocated in driver
  4. *
  5. * The vb2_queue structure should be allocated by the driver. The driver is
  6. * responsible of clearing it's content and setting initial values for some
  7. * required entries before calling this function.
  8. * q->ops, q->mem_ops, q->type and q->io_modes are mandatory. Please refer
  9. * to the struct vb2_queue description in include/media/videobuf2-core.h
  10. * for more information.
  11. */
  12. int vb2_queue_init(struct vb2_queue *q)
  13. {
  14. BUG_ON(!q);
  15. BUG_ON(!q->ops);
  16. BUG_ON(!q->mem_ops);
  17. BUG_ON(!q->type);
  18. BUG_ON(!q->io_modes);
  19. BUG_ON(!q->ops->queue_setup);
  20. BUG_ON(!q->ops->buf_queue);
  21. INIT_LIST_HEAD(&q->queued_list);
  22. INIT_LIST_HEAD(&q->done_list);
  23. spin_lock_init(&q->done_lock);
  24. init_waitqueue_head(&q->done_wq);
  25. if (q->buf_struct_size == 0)
  26. q->buf_struct_size = sizeof(struct vb2_buffer);
  27. return 0;
  28. }
  29. 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的注册顺序

  1. int __video_register_device(struct video_device *vdev, int type, int nr,
  2. int warn_if_nr_in_use, struct module *owner)
  3. {
  4. int i = 0;
  5. int ret;
  6. int minor_offset = 0;
  7. int minor_cnt = VIDEO_NUM_DEVICES;
  8. const char *name_base;
  9. /* A minor value of -1 marks this video device as never
  10. having been registered */
  11. vdev->minor = -1;
  12. /* the release callback MUST be present */
  13. WARN_ON(!vdev->release);
  14. if (!vdev->release)
  15. return -EINVAL;
  16. /* v4l2_fh support */
  17. spin_lock_init(&vdev->fh_lock);
  18. INIT_LIST_HEAD(&vdev->fh_list);
  19. /* Part 1: check device type */
  20. /* after here, you can see videx ...the char device in /dev */
  21. //这里还是单独说一下吧,最终你在/dev目录下看到的video0就是在这里决定的,大家可以知道,可不是一定名字叫video的
  22. switch (type) {
  23. case VFL_TYPE_GRABBER:
  24. name_base = "video";
  25. break;
  26. case VFL_TYPE_VBI:
  27. name_base = "vbi";
  28. break;
  29. case VFL_TYPE_RADIO:
  30. name_base = "radio";
  31. break;
  32. case VFL_TYPE_SUBDEV:
  33. name_base = "v4l-subdev";
  34. break;
  35. default:
  36. printk(KERN_ERR "%s called with unknown type: %d\n",
  37. __func__, type);
  38. return -EINVAL;
  39. }
  40. vdev->vfl_type = type;
  41. vdev->cdev = NULL;
  42. if (vdev->v4l2_dev) {
  43. if (vdev->v4l2_dev->dev)
  44. //这里说明vdev和保存在v4l2_device的dev具有共同的parent,对之后sys 接口那里有用
  45. vdev->parent = vdev->v4l2_dev->dev;
  46. if (vdev->ctrl_handler == NULL)
  47. //上面初始化的ctrl_handler在这里要派上用处了,而且同时指向video_device和v4l2_device的化ctrl_handler
  48. vdev->ctrl_handler = vdev->v4l2_dev->ctrl_handler;
  49. /* If the prio state pointer is NULL, then use the v4l2_device
  50. prio state. */
  51. if (vdev->prio == NULL)
  52. //上面初始化的prio在这里要派上用处了,而且同时指向video_device和v4l2_device的化prio
  53. vdev->prio = &vdev->v4l2_dev->prio;
  54. }
      //从这里往下挺长一段代码是在为要申请的字符设备寻找一个合适的设备号,这里不去深入追究了,有时间可以可虑回来看看
  1. /* Part 2: find a free minor, device node number and device index. */
  2. #ifdef CONFIG_VIDEO_FIXED_MINOR_RANGES
  3. /* Keep the ranges for the first four types for historical
  4. * reasons.
  5. * Newer devices (not yet in place) should use the range
  6. * of 128-191 and just pick the first free minor there
  7. * (new style). */
  8. switch (type) {
  9. case VFL_TYPE_GRABBER:
  10. minor_offset = 0;
  11. minor_cnt = 64;
  12. break;
  13. case VFL_TYPE_RADIO:
  14. minor_offset = 64;
  15. minor_cnt = 64;
  16. break;
  17. case VFL_TYPE_VBI:
  18. minor_offset = 224;
  19. minor_cnt = 32;
  20. break;
  21. default:
  22. minor_offset = 128;
  23. minor_cnt = 64;
  24. break;
  25. }
  26. #endif
  27. /* Pick a device node number */
  28. mutex_lock(&videodev_lock);
  29. nr = devnode_find(vdev, nr == -1 ? 0 : nr, minor_cnt);
  30. if (nr == minor_cnt)
  31. nr = devnode_find(vdev, 0, minor_cnt);
  32. if (nr == minor_cnt) {
  33. printk(KERN_ERR "could not get a free device node number\n");
  34. mutex_unlock(&videodev_lock);
  35. return -ENFILE;
  36. }
  37. #ifdef CONFIG_VIDEO_FIXED_MINOR_RANGES
  38. /* 1-on-1 mapping of device node number to minor number */
  39. i = nr;
  40. #else
  41. /* The device node number and minor numbers are independent, so
  42. we just find the first free minor number. */
  43. for (i = 0; i < VIDEO_NUM_DEVICES; i++)
  44. if (video_device[i] == NULL)
  45. break;
  46. if (i == VIDEO_NUM_DEVICES) {
  47. mutex_unlock(&videodev_lock);
  48. printk(KERN_ERR "could not get a free minor\n");
  49. return -ENFILE;
  50. }
  51. #endif
  52. vdev->minor = i + minor_offset;
  53. vdev->num = nr;
  54. devnode_set(vdev);
  55. /* Should not happen since we thought this minor was free */
  56. WARN_ON(video_device[vdev->minor] != NULL);
  57. vdev->index = get_index(vdev);
  58. mutex_unlock(&videodev_lock);
     //上面的方法获取到了那个合适的设备号,现在要开始注册我们的字符设备了
  1. /* Part 3: Initialize the character device */
  2. vdev->cdev = cdev_alloc();
  3. if (vdev->cdev == NULL) {
  4. ret = -ENOMEM;
  5. goto cleanup;
  6. }
  7. vdev->cdev->ops = &v4l2_fops;//most important part,操作设备的通道
  8. vdev->cdev->owner = owner;
  9. ret = cdev_add(vdev->cdev, MKDEV(VIDEO_MAJOR, vdev->minor), 1);
  10. if (ret < 0) {
  11. printk(KERN_ERR "%s: cdev_add failed\n", __func__);
  12. kfree(vdev->cdev);
  13. vdev->cdev = NULL;
  14. goto cleanup;
  15. }
     //这里我们也大可先不用关注,主要是在sysfs的一些设备添加等等
  1. /* Part 4: register the device with sysfs */
  2. vdev->dev.class = &video_class;
  3. vdev->dev.devt = MKDEV(VIDEO_MAJOR, vdev->minor);
  4. if (vdev->parent)
  5. vdev->dev.parent = vdev->parent;
  6. dev_set_name(&vdev->dev, "%s%d", name_base, vdev->num);
  7. ret = device_register(&vdev->dev);
  8. if (ret < 0) {
  9. printk(KERN_ERR "%s: device_register failed\n", __func__);
  10. goto cleanup;
  11. }
  12. /* Register the release callback that will be called when the last
  13. reference to the device goes away. */
  14. vdev->dev.release = v4l2_device_release;
  15. if (nr != -1 && nr != vdev->num && warn_if_nr_in_use)
  16. printk(KERN_WARNING "%s: requested %s%d, got %s\n", __func__,
  17. name_base, nr, video_device_node_name(vdev));
  18. /* Increase v4l2_device refcount */
  19. if (vdev->v4l2_dev)
  20. v4l2_device_get(vdev->v4l2_dev);
  21. #if defined(CONFIG_MEDIA_CONTROLLER)
     //这里其实还是比较重要的,不过不是所以的驱动都要添加这一个步骤,这也是为什么有一个if define 的原因了
     //意思就是如果这个驱动中需要用到media controler的时候就需要在这里注册media_device
     //这里同样先不做深入研究,media_device和media_entity这两个重要结构体之后还要研究
  1. /* Part 5: Register the entity. */
  2. if (vdev->v4l2_dev && vdev->v4l2_dev->mdev &&
  3. vdev->vfl_type != VFL_TYPE_SUBDEV) {
  4. vdev->entity.type = MEDIA_ENT_T_DEVNODE_V4L;
  5. vdev->entity.name = vdev->name;
  6. vdev->entity.v4l.major = VIDEO_MAJOR;
  7. vdev->entity.v4l.minor = vdev->minor;
  8. ret = media_device_register_entity(vdev->v4l2_dev->mdev,
  9. &vdev->entity);
  10. if (ret < 0)
  11. printk(KERN_WARNING
  12. "%s: media_device_register_entity failed\n",
  13. __func__);
  14. }
  15. #endif
  16. //保存注册成功标记,并将注册成功的video_device保存到全局数组video_device中,大功告成
  17. /* Part 6: Activate this minor. The char device can now be used. */
  18. set_bit(V4L2_FL_REGISTERED, &vdev->flags);//设置标志位,之后还会遇到test_bit方法用来check flags的第nr位是否为1.
  19. //这里还是多说一点,另外还有两中标志位需要知道:V4L2_FL_USES_V4L2_FH, V4L2_FL_USE_FH_PRIO
  20. mutex_lock(&videodev_lock);
  21. video_device[vdev->minor] = vdev;
  22. mutex_unlock(&videodev_lock);
  23. return 0;
 //这里是出错处理函数
  1. cleanup:
  2. mutex_lock(&videodev_lock);
  3. if (vdev->cdev)
  4. cdev_del(vdev->cdev);
  5. devnode_clear(vdev);
  6. mutex_unlock(&videodev_lock);
  7. /* Mark this video device as never having been registered. */
  8. vdev->minor = -1;
  9. return ret;
  10. }
  11. EXPORT_SYMBOL(__video_register_device);

7.把vivi_dev结构set进video_device中, 方便之后使用, 设备信息加入的链表结构
最后结尾的这段代码这里我决定单独放在下面分析,也算妥善收尾吧

  1. /*
  2. * You should pay attention to this method
  3. * here you set the vivi_dev into the vedio_device for the later use in fops
  4. * When you want to use the vivi_dev, you use vedio_get_drvdata() to get
  5. */
  6. video_set_drvdata(vfd, dev);
  7. /* Now that everything is fine, let's add it to device list */
  8. list_add_tail(&dev->vivi_devlist, &vivi_devlist);//添加的device list当中
  9. if (video_nr != -1)
  10. video_nr++;//用于计数,找到设备
  11. dev->vfd = vfd;关联video_device和vivi_dev

短短的几条语句,但我看来,个个都短小精悍
首先说说这个方法,他有什么用处呢?其实用处可大了,就行我上面注释的那样,这里把vivi_dev设置到vedio_device中,是为了之后字符设备访问接口中使用
这里多说一点,也算顺便说一下用户空间操作设备的流程了
首先当时用户空间访问设备了,这个做驱动的不懂那可糗大了,用户空间open时,也就是启动了上面video_device_register方法中的很重要的下面结构中的open方法

  1. static const struct file_operations v4l2_fops = {
  2. .owner = THIS_MODULE,
  3. .read = v4l2_read,
  4. .write = v4l2_write,
  5. .open = v4l2_open,
  6. .get_unmapped_area = v4l2_get_unmapped_area,
  7. .mmap = v4l2_mmap,
  8. .unlocked_ioctl = v4l2_ioctl,
  9. #ifdef CONFIG_COMPAT
  10. .compat_ioctl = v4l2_compat_ioctl32,
  11. #endif
  12. .release = v4l2_release,
  13. .poll = v4l2_poll,
  14. .llseek = no_llseek,
  15. };

我们来看一下这个open方法

  1. /* Override for the open function */
  2. static int v4l2_open(struct inode *inode, struct file *filp)
  3. {
  4. struct video_device *vdev;
  5. int ret = 0;
  6. /* Check if the video device is available */
  7. mutex_lock(&videodev_lock);
  8. vdev = video_devdata(filp);
  9. /* return ENODEV if the video device has already been removed. */
  10. if (vdev == NULL || !video_is_registered(vdev)) {
  11. mutex_unlock(&videodev_lock);
  12. return -ENODEV;
  13. }
  14. /* and increase the device refcount */
  15. video_get(vdev);//这里是用来计数的
  16. mutex_unlock(&videodev_lock);
  17. /*
  18. * Here using the API you get the method you get the open() method write
  19. * The other methods in fops use the same method to use you own code
  20. */
  21. if (vdev->fops->open) {
  22. if (vdev->lock && mutex_lock_interruptible(vdev->lock)) {
  23. ret = -ERESTARTSYS;
  24. goto err;
  25. }
  26. if (video_is_registered(vdev))
  27. ret = vdev->fops->open(filp);
  28. else
  29. ret = -ENODEV;
  30. if (vdev->lock)
  31. mutex_unlock(vdev->lock);
  32. }
  33. err:
  34. /* decrease the refcount in case of an error */
  35. if (ret)
  36. video_put(vdev);
  37. return ret;
  38. }

只有最下面的那个标准才是重点,经过那么多的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方法

  1. static const struct v4l2_file_operations vivi_fops = {
  2. .owner        = THIS_MODULE,
  3. .open        = v4l2_fh_open,
  4. .release = vivi_close,
  5. .read = vivi_read,
  6. .poll        = vivi_poll,
  7. .unlocked_ioctl = video_ioctl2, /* V4L2 ioctl handler */
  8. .mmap = vivi_mmap,
  9. };
  10. static const struct v4l2_ioctl_ops vivi_ioctl_ops = {
  11. .vidioc_querycap = vidioc_querycap,
  12. .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
  13. .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap,
  14. .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap,
  15. .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap,
  16. .vidioc_reqbufs = vidioc_reqbufs,
  17. .vidioc_querybuf = vidioc_querybuf,
  18. .vidioc_qbuf = vidioc_qbuf,
  19. .vidioc_dqbuf = vidioc_dqbuf,
  20. .vidioc_s_std = vidioc_s_std,
  21. .vidioc_enum_input = vidioc_enum_input,
  22. .vidioc_g_input = vidioc_g_input,
  23. .vidioc_s_input = vidioc_s_input,
  24. .vidioc_streamon = vidioc_streamon,
  25. .vidioc_streamoff = vidioc_streamoff,
  26. };
  27. static struct video_device vivi_template = {
  28. .name        = "vivi",
  29. .fops = &vivi_fops,
  30. .ioctl_ops     = &vivi_ioctl_ops,
  31. .release    = video_device_release,
  32. .tvnorms = V4L2_STD_525_60,
  33. .current_norm = V4L2_STD_NTSC_M,
  34. };

我们找到了,就是 v4l2_fh_open 这个方法,这个方法与我们之前写字符驱动时遇到的情况很是不同,他的open方法其实是有内核API写好的,我们先看看

  1. int v4l2_fh_open(struct file *filp)
  2. {
  3. struct video_device *vdev = video_devdata(filp);
  4. struct v4l2_fh *fh = kzalloc(sizeof(*fh), GFP_KERNEL);
  5. /*
  6. * IN the open method, do only one job
  7. * set v4l2_fh into filp->private_data for later use, and initial v4l2_fh
  8. */
  9. filp->private_data = fh;
  10. if (fh == NULL)
  11. return -ENOMEM;
  12. v4l2_fh_init(fh, vdev);
  13. v4l2_fh_add(fh);
  14. return 0;
  15. }
  16. 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方法吧

  1. static ssize_t
  2. vivi_read(struct file *file, char __user *data, size_t count, loff_t *ppos)
  3. {
  4. struct vivi_dev *dev = video_drvdata(file);
  5. dprintk(dev, 1, "read called\n");
  6. return vb2_read(&dev->vb_vidq, data, count, ppos,
  7. file->f_flags & O_NONBLOCK);
  8. }

这里大家看到了,方法获取到vivi_dev这个数据结构,并传递到vb2_read这个方法中,这个方法时驱动中数据传输的关键,之后再慢慢研究,这里同样不深究
而这个vivi_dev是通过video_drvdata方法获得的,这就是为什么在上面我强调的要用
video_set_drvdata ( vfd ,  dev ) ; 把vivi_dev装载到video_device中

到这里基本的过程都整理完了,其实大头还在后面呢,待续。。。。。。

转载于:https://www.cnblogs.com/jiangu66/archive/2013/04/23/3037421.html

虚拟视频驱动程序vivi.c源码分析相关推荐

  1. Android 7.0 虚拟按键(NavigationBar)源码分析 之 点击事件的实现流程

    第二部分: Let's go!!! [点击事件的实现流程] 1.初始化 虚拟按键点击效果的实现和实体按键相似,也是通过上报一个keyCode值,来判断哪个按钮被点击.不同的是,实体按键的keyCode ...

  2. 音视频技术之ffplay源码分析-音视频同步

    音视频同步的目的是为了使播放的声音和显示的画面保持一致.视频按帧播放,图像显示设备每次显示一帧画面,视频播放速度由帧率确定,帧率指示每秒显示多少帧:音频按采样点播放,声音播放设备每次播放一个采样点,声 ...

  3. [Linux 基础] -- V4L2 实例分析 —— vivi.c 源码详解(深度好文)

    本文主要是通过对虚拟视频驱动程序 vivi.c 源码分析,加深对 V4L2 框架的理解.转载于:https://blog.csdn.net/yanbixing123/article/details/5 ...

  4. FFmpeg简述,源码分析,录制/压缩/水印/剪切/旋转/滤镜/美颜/上传视频等(CPU软编码和解码)

    > ffmpeg源码分析 ffmpeg源码简析(一)结构总览- https://blog.csdn.net/Louis_815/article/details/79621056 FFmpeg的库 ...

  5. ffplay源码分析4-音视频同步

    ffplay是FFmpeg工程自带的简单播放器,使用FFmpeg提供的解码器和SDL库进行视频播放.本文基于FFmpeg工程4.1版本进行分析,其中ffplay源码清单如下: https://gith ...

  6. Linux内核源码分析方法—程序员进阶必备

    一.内核源码之我见 Linux内核代码的庞大令不少人"望而生畏",也正因为如此,使得人们对Linux的了解仅处于泛泛的层次.如果想透析Linux,深入操作系统的本质,阅读内核源码是 ...

  7. 【原创】【专栏】《Linux设备驱动程序》--- LDD3源码目录结构和源码分析经典链接

    http://blog.csdn.net/geng823/article/details/37567557 [原创][专栏]<Linux设备驱动程序>--- LDD3源码目录结构和源码分析 ...

  8. uCOS2源码分析1-BSP部分-第4季第2部分视频课程-朱有鹏-专题视频课程

    uCOS2源码分析1-BSP部分-第4季第2部分视频课程-988人已学习 课程介绍         本课程是<朱有鹏老师单片机完全学习系列课程>第4季第2个课程,本课程我们开始分析uCOS ...

  9. uCOS2源码分析3-RTOS核心代码视频课程-第4季第4部分-朱有鹏-专题视频课程

    uCOS2源码分析3-RTOS核心代码视频课程-第4季第4部分-1077人已学习 课程介绍         本课程是<朱有鹏老师单片机完全学习系列课程>第4季第4个课程,本课程我们重点分析 ...

最新文章

  1. 射极跟随器实验报告数据处理_射极跟随器实验报告模式
  2. java Date.getTime()返回负数异常情况分析
  3. 射影几何教程: 1 射影几何介绍
  4. 左值和左值引用、右值和右值引用
  5. 使用了SAP Spartacus的一个在线网站:乐高Storefront
  6. 走过小公司的坑之入职一周
  7. 【BZOJ5213】[ZJOI2018]迷宫(神仙题)
  8. 重新初始化_关窗,也有大学问!宝马车窗初始化设置步骤方法...
  9. 想入职阿里的Java开发者必看,阿里巴巴面试官实战经验分享!
  10. vs.net 2005中引用webservice的简单方法
  11. 一文弄懂数据挖掘的十大算法,数据挖掘算法原理讲解
  12. 小米用户画像实战,48页PPT下载
  13. 小米商城网页制作(附源码)
  14. 阿法狗之后的围棋世界
  15. Docker安全工具Clair/Anchore/DockerScan对比测试
  16. 我和Double Lift的故事(三)——应用篇
  17. java语言基础知识笔记
  18. vim etc mysql my.cnf_初始化配置文件的使用:/etc/my.cnf
  19. java用队列实现栈、用栈实现对队列
  20. 苹果cms模板文件不存在解决方法

热门文章

  1. php cachelock,巧用lock解决缓存击穿的解决方案
  2. win10 联想键盘快捷键关闭_这些Win10键盘快捷键你必须掌握,系统高手必备技能...
  3. oracle安装时配饰失败了,【求助】急!!!!oracle客户端安装时创建实例失败
  4. 鸿蒙系统新手教程,鸿蒙灭神决新手入门全流程图文攻略
  5. php中数组生成下拉选项,php数组生成html下拉列表的方法
  6. 求数列1/3到1/n之和
  7. python如何把一张图像的所有像素点的值都显示出来_情人节,教你用 Python 向女神表白...
  8. 八进制转换成十进制c语言程序,C语言程序 十进制、八进制、十六进制的相互转化...
  9. Python小白的数学建模课-03.线性规划
  10. 什么是大数据,大数据到底应该如何学?