虛擬視訊驅動程式vivi.c原始碼分析

虛擬視訊驅動程式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.  
  6.     if (font == NULL) {
  7.         printk(KERN_ERR "vivi: could not find font\n");
  8.         return -ENODEV;
  9.     }
  10.     font8x16 = font->data;
  11.  
  12.     if (n_devs <= 0)
  13.         n_devs = 1;
  14.  
  15.     for (= 0; i < n_devs; i++) {
  16.         //Here is the most important
  17.         ret = vivi_create_instance(i);
  18.         if (ret) {
  19.             /* If some instantiations succeeded, keep driver */
  20.             if (i)
  21.                 ret = 0;
  22.             break;
  23.         }
  24.     }
  25.  
  26.     if (ret < 0) {
  27.         printk(KERN_ERR "vivi: error %d while loading driver\n", ret);
  28.         return ret;
  29.     }
  30.  
  31.     printk(KERN_INFO "Video Technology Magazine Virtual Video "
  32.             "Capture Board ver %u.%u.%u successfully loaded.\n",
  33.             (VIVI_VERSION >> 16) & 0xFF, (VIVI_VERSION >> 8) & 0xFF,
  34.             VIVI_VERSION & 0xFF);
  35.  
  36.     /* n_devs will reflect the actual number of allocated devices */
  37.     n_devs = i;
  38.  
  39.     return ret;
  40. }
  41.  
  42. static void __exit vivi_exit(void)
  43. {
  44.     vivi_release();
  45. }
  46.  
  47. module_init(vivi_init);
  48. 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.  
  9.     dev = kzalloc(sizeof(*dev), GFP_KERNEL);
  10.     if (!dev)
  11.         return -ENOMEM;
  12.  
  13.     // set the v4l2_device(the name)
  14.     snprintf(dev->v4l2_dev.name, sizeof(dev->v4l2_dev.name),
  15.             "%s-%03d", VIVI_MODULE_NAME, inst);
  16.     
  17.     /* 
  18.     * register the v4l2_device, but you should pay attention here
  19.     * the "dev == NULL" it means v4l2_device.dev == NULL
  20.     * You did'set the v4l2_device.dev, you will set it later
  21.     */
  22.     ret = v4l2_device_register(NULL, &dev->v4l2_dev);
  23.     if (ret)
  24.         goto free_dev;
  25.  
  26.     /* init the handle, learn it later */
  27.     dev->fmt = &formats[0];
  28.     dev->width = 640;
  29.     dev->height = 480;
  30.     hdl = &dev->ctrl_handler;
  31.     v4l2_ctrl_handler_init(hdl, 11);
  32.     dev->volume = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops,
  33.             V4L2_CID_AUDIO_VOLUME, 0, 255, 1, 200);
  34.     dev->brightness = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops,
  35.             V4L2_CID_BRIGHTNESS, 0, 255, 1, 127);
  36.     dev->contrast = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops,
  37.             V4L2_CID_CONTRAST, 0, 255, 1, 16);
  38.     dev->saturation = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops,
  39.             V4L2_CID_SATURATION, 0, 255, 1, 127);
  40.     dev->hue = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops,
  41.             V4L2_CID_HUE, -128, 127, 1, 0);
  42.     dev->button = v4l2_ctrl_new_custom(hdl, &vivi_ctrl_button, NULL);
  43.     dev->int32 = v4l2_ctrl_new_custom(hdl, &vivi_ctrl_int32, NULL);
  44.     dev->int64 = v4l2_ctrl_new_custom(hdl, &vivi_ctrl_int64, NULL);
  45.     dev->boolean = v4l2_ctrl_new_custom(hdl, &vivi_ctrl_boolean, NULL);
  46.     dev->menu = v4l2_ctrl_new_custom(hdl, &vivi_ctrl_menu, NULL);
  47.     dev->string = v4l2_ctrl_new_custom(hdl, &vivi_ctrl_string, NULL);
  48.     if (hdl->error) {
  49.         ret = hdl->error;
  50.         goto unreg_dev;
  51.     }
  52.     dev->v4l2_dev.ctrl_handler = hdl;
  53.  
  54.     /* initialize locks */
  55.     spin_lock_init(&dev->slock);
  56.  
  57.     /* initialize queue, learn it later */
  58.     q = &dev->vb_vidq;
  59.     memset(q, 0, sizeof(dev->vb_vidq));
  60.     q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
  61.     q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_READ;
  62.     q->drv_priv = dev;
  63.     q->buf_struct_size = sizeof(struct vivi_buffer);
  64.     q->ops = &vivi_video_qops;
  65.     q->mem_ops = &vb2_vmalloc_memops;
  66.  
  67.     vb2_queue_init(q);
  68.  
  69.     mutex_init(&dev->mutex);
  70.  
  71.     /* init video dma queues */
  72.     INIT_LIST_HEAD(&dev->vidq.active);
  73.     init_waitqueue_head(&dev->vidq.wq);
  74.  
  75.     /* before register the video_device, init the video_device data*/
  76.     ret = -ENOMEM;
  77.     vfd = video_device_alloc();
  78.     if (!vfd)
  79.         goto unreg_dev;
  80.  
  81.     *vfd = vivi_template;/* the most important struct */
  82.     vfd->debug = debug;
  83.     vfd->v4l2_dev = &dev->v4l2_dev; /* here set the v4l2_device, you have already registered it */
  84.     set_bit(V4L2_FL_USE_FH_PRIO, &vfd->flags);
  85.  
  86.     /*
  87.      * Provide a mutex to v4l2 core. It will be used to protect
  88.      * all fops and v4l2 ioctls.
  89.      */
  90.     vfd->lock = &dev->mutex;
  91.  
  92.     ret = video_register_device(vfd, VFL_TYPE_GRABBER, video_nr);
  93.     if (ret < 0)
  94.         goto rel_vdev;
  95.  
  96.     /*
  97.     * You should pay attention to this method
  98.     * here you set the vivi_dev into the vedio_device for the later use in fops
  99.     * When you want to use the vivi_dev, you use vedio_get_drvdata() to get
  100.     */
  101.     video_set_drvdata(vfd, dev);
  102.  
  103.     /* Now that everything is fine, let's add it to device list */
  104.     list_add_tail(&dev->vivi_devlist, &vivi_devlist);
  105.  
  106.     if (video_nr != -1)
  107.         video_nr++;
  108.  
  109.     dev->vfd = vfd;
  110.  
  111.     /* the debug message*/
  112.     v4l2_info(&dev->v4l2_dev, "V4L2 device registered as %s\n",
  113.          video_device_node_name(vfd));
  114.     return 0;
  115.  
  116. rel_vdev:
  117.     video_device_release(vfd);
  118. unreg_dev:
  119.     v4l2_ctrl_handler_free(hdl);
  120.     v4l2_device_unregister(&dev->v4l2_dev);
  121. free_dev:
  122.     kfree(dev);
  123.     return ret;
  124. }

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結構setvideo_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.  
  6.     INIT_LIST_HEAD(&v4l2_dev->subdevs);
  7.     spin_lock_init(&v4l2_dev->lock);
  8.     mutex_init(&v4l2_dev->ioctl_lock);
  9.  
  10.     /* initial the global priotity*/
  11.     v4l2_prio_init(&v4l2_dev->prio);
  12.     kref_init(&v4l2_dev->ref);
  13.     v4l2_dev->dev = dev;
  14.     if (dev == NULL) {
  15.         /* If dev == NULL, then name must be filled in by the caller */
  16.         WARN_ON(!v4l2_dev->name[0]);
  17.         /* Here give the caller a WARN, tell the caller to set the dev*/
  18.         return 0;
  19.     }
  20.  
  21.     /* Set name to driver name + device name if it is empty. */
  22.     if (!v4l2_dev->name[0])
  23.         snprintf(v4l2_dev->name, sizeof(v4l2_dev->name), "%s %s",
  24.             dev->driver->name, dev_name(dev));
  25.  
  26.     /* Here is also very important, you can get v4l2_device by use dev_get_drvdata*/
  27.     if (!dev_get_drvdata(dev))
  28.         dev_set_drvdata(dev, v4l2_dev);
  29.     return 0;
  30. }
  31. 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_devicename,並且將v4l2_device結構setdev中,方便後面獲取使用

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.  
  31.     const struct vb2_ops        *ops;
  32.     const struct vb2_mem_ops    *mem_ops;
  33.     void                *drv_priv;
  34.     unsigned int            buf_struct_size;
  35.  
  36. /* private: internal use only */
  37.     enum v4l2_memory        memory;
  38.     struct vb2_buffer        *bufs[VIDEO_MAX_FRAME];
  39.     unsigned int            num_buffers;
  40.  
  41.     struct list_head        queued_list;
  42.  
  43.     atomic_t            queued_count;
  44.     struct list_head        done_list;
  45.     spinlock_t            done_lock;
  46.     wait_queue_head_t        done_wq;
  47.  
  48.     void                *alloc_ctx[VIDEO_MAX_PLANES];
  49.  
  50.     unsigned int            streaming:1;
  51.  
  52.     struct vb2_fileio_data        *fileio;
  53. };

在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.  
  48.     void (*wait_prepare)(struct vb2_queue *q);
  49.     void (*wait_finish)(struct vb2_queue *q);
  50.  
  51.     int (*buf_init)(struct vb2_buffer *vb);
  52.     int (*buf_prepare)(struct vb2_buffer *vb);
  53.     int (*buf_finish)(struct vb2_buffer *vb);
  54.     void (*buf_cleanup)(struct vb2_buffer *vb);
  55.  
  56.     int (*start_streaming)(struct vb2_queue *q);
  57.     int (*stop_streaming)(struct vb2_queue *q);
  58.  
  59.     void (*buf_queue)(struct vb2_buffer *vb);
  60. };

 

  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.  
  42.     void        *(*get_userptr)(void *alloc_ctx, unsigned long vaddr,
  43.                     unsigned long size, int write);
  44.     void        (*put_userptr)(void *buf_priv);
  45.  
  46.     void        *(*vaddr)(void *buf_priv);
  47.     void        *(*cookie)(void *buf_priv);
  48.  
  49.     unsigned int    (*num_users)(void *buf_priv);
  50.  
  51.     int        (*mmap)(void *buf_priv, struct vm_area_struct *vma);
  52. };

最後調用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.  
  20.     BUG_ON(!q->ops->queue_setup);
  21.     BUG_ON(!q->ops->buf_queue);
  22.  
  23.     INIT_LIST_HEAD(&q->queued_list);
  24.     INIT_LIST_HEAD(&q->done_list);
  25.     spin_lock_init(&q->done_lock);
  26.     init_waitqueue_head(&q->done_wq);
  27.  
  28.     if (q->buf_struct_size == 0)
  29.         q->buf_struct_size = sizeof(struct vb2_buffer);
  30.  
  31.     return 0;
  32. }
  33. 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_devicevideo_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.  
  10.     /* A minor value of -1 marks this video device as never
  11.      having been registered */
  12.     vdev->minor = -1;
  13.  
  14.     /* the release callback MUST be present */
  15.     WARN_ON(!vdev->release);
  16.     if (!vdev->release)
  17.         return -EINVAL;
  18.  
  19.     /* v4l2_fh support */
  20.     spin_lock_init(&vdev->fh_lock);
  21.     INIT_LIST_HEAD(&vdev->fh_list);
  22.  
  23.     /* Part 1: check device type */
  24.     /* after here, you can see videx ...the char device in /dev */
  25.     //這裏還是單獨說一下吧,最終你在/dev目錄下看到的video0就是在這裏決定的,大家可以知道,可不是一定名字叫video的
  26.     switch (type) {
  27.     case VFL_TYPE_GRABBER:
  28.         name_base = "video";
  29.         break;
  30.     case VFL_TYPE_VBI:
  31.         name_base = "vbi";
  32.         break;
  33.     case VFL_TYPE_RADIO:
  34.         name_base = "radio";
  35.         break;
  36.     case VFL_TYPE_SUBDEV:
  37.         name_base = "v4l-subdev";
  38.         break;
  39.     default:
  40.         printk(KERN_ERR "%s called with unknown type: %d\n",
  41.          __func__, type);
  42.         return -EINVAL;
  43.     }
  44.  
  45.     vdev->vfl_type = type;
  46.     vdev->cdev = NULL;
  47.     if (vdev->v4l2_dev) {
  48.         if (vdev->v4l2_dev->dev)
  49.             //這裏說明vdev和保存在v4l2_device的dev具有共同的parent,對之後sys 介面那裏有用

  50.             vdev->parent = vdev->v4l2_dev->dev;
  51.         if (vdev->ctrl_handler == NULL)
  52.             //上面初始化的ctrl_handler在這裏要派上用處了,而且同時指向video_device和v4l2_device的化ctrl_handler

  53.             vdev->ctrl_handler = vdev->v4l2_dev->ctrl_handler;
  54.         /* If the prio state pointer is NULL, then use the v4l2_device
  55.          prio state. */
  56.         if (vdev->prio == NULL)
  57.             //上面初始化的prio在這裏要派上用處了,而且同時指向video_device和v4l2_device的化prio

  58.             vdev->prio = &vdev->v4l2_dev->prio;
  59.     }

      //從這裏往下挺長一段程式碼是在為要申請的字元設備尋找一個合適的設備號,這裏不去深入追究了,有時間可以可慮回來看看

  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.  
  28.     /* Pick a device node number */
  29.     mutex_lock(&videodev_lock);
  30.     nr = devnode_find(vdev, nr == -? 0 : nr, minor_cnt);
  31.     if (nr == minor_cnt)
  32.         nr = devnode_find(vdev, 0, minor_cnt);
  33.     if (nr == minor_cnt) {
  34.         printk(KERN_ERR "could not get a free device node number\n");
  35.         mutex_unlock(&videodev_lock);
  36.         return -ENFILE;
  37.     }
  38. #ifdef CONFIG_VIDEO_FIXED_MINOR_RANGES
  39.     /* 1-on-1 mapping of device node number to minor number */
  40.     i = nr;
  41. #else
  42.     /* The device node number and minor numbers are independent, so
  43.      we just find the first free minor number. */
  44.     for (= 0; i < VIDEO_NUM_DEVICES; i++)
  45.         if (video_device[i] == NULL)
  46.             break;
  47.     if (== VIDEO_NUM_DEVICES) {
  48.         mutex_unlock(&videodev_lock);
  49.         printk(KERN_ERR "could not get a free minor\n");
  50.         return -ENFILE;
  51.     }
  52. #endif
  53.     vdev->minor = i + minor_offset;
  54.     vdev->num = nr;
  55.     devnode_set(vdev);
  56.  
  57.     /* Should not happen since we thought this minor was free */
  58.     WARN_ON(video_device[vdev->minor] != NULL);
  59.     vdev->index = get_index(vdev);
  60.     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.  
  16.     if (nr != -&& nr != vdev->num && warn_if_nr_in_use)
  17.         printk(KERN_WARNING "%s: requested %s%d, got %s\n", __func__,
  18.             name_base, nr, video_device_node_name(vdev));
  19.  
  20.     /* Increase v4l2_device refcount */
  21.     if (vdev->v4l2_dev)
  22.         v4l2_device_get(vdev->v4l2_dev);
  23.  
  24. #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.  
  24.     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結構setvideo_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.  
  8.     /* Now that everything is fine, let's add it to device list */
  9.     list_add_tail(&dev->vivi_devlist, &vivi_devlist);//添加的device list當中
  10.  
  11.     if (video_nr != -1)
  12.         video_nr++;//用於計數,找到設備
  13.  
  14.     dev->vfd = vfd;關聯video_devicevivi_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.  
  7.     /* Check if the video device is available */
  8.     mutex_lock(&videodev_lock);
  9.     vdev = video_devdata(filp);
  10.     /* return ENODEV if the video device has already been removed. */
  11.     if (vdev == NULL || !video_is_registered(vdev)) {
  12.         mutex_unlock(&videodev_lock);
  13.         return -ENODEV;
  14.     }
  15.     /* and increase the device refcount */
  16.     video_get(vdev);//這裏是用來計數的
  17.     mutex_unlock(&videodev_lock);
  18.  
  19.     /* 
  20.     * Here using the API you get the method you get the open() method write
  21.     * The other methods in fops use the same method to use you own code 
  22.     */
  23.     if (vdev->fops->open) {
  24.         if (vdev->lock && mutex_lock_interruptible(vdev->lock)) {
  25.             ret = -ERESTARTSYS;
  26.             goto err;
  27.         }
  28.         if (video_is_registered(vdev))
  29.             ret = vdev->fops->open(filp);
  30.         else
  31.             ret = -ENODEV;
  32.         if (vdev->lock)
  33.             mutex_unlock(vdev->lock);
  34.     }
  35.  
  36. err:
  37.     /* decrease the refcount in case of an error */
  38.     if (ret)
  39.         video_put(vdev);
  40.     return ret;
  41. }

只有最下面的那個標準才是重點,經過那麼多的check,最後走的了最後這一步,ret = vdev->fops->open(filp);
這個open方法在哪里呢?那就沿著箭頭方向找吧,是video_device內部的fops中的open方法,這個方法不是有在哪里呢?
接著找,在video_device_register方法之前
*vfd = vivi_template;/* the most important struct */
我還特意在這裏寫了最重要的結構體
所以最終調用的是vivi_templatefops中定義的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.  
  11. static const struct v4l2_ioctl_ops vivi_ioctl_ops = {
  12.     .vidioc_querycap = vidioc_querycap,
  13.     .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
  14.     .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap,
  15.     .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap,
  16.     .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap,
  17.     .vidioc_reqbufs = vidioc_reqbufs,
  18.     .vidioc_querybuf = vidioc_querybuf,
  19.     .vidioc_qbuf = vidioc_qbuf,
  20.     .vidioc_dqbuf = vidioc_dqbuf,
  21.     .vidioc_s_std = vidioc_s_std,
  22.     .vidioc_enum_input = vidioc_enum_input,
  23.     .vidioc_g_input = vidioc_g_input,
  24.     .vidioc_s_input = vidioc_s_input,
  25.     .vidioc_streamon = vidioc_streamon,
  26.     .vidioc_streamoff = vidioc_streamoff,
  27. };
  28.  
  29. static struct video_device vivi_template = {
  30.     .name        = "vivi",
  31.     .fops = &vivi_fops,
  32.     .ioctl_ops     = &vivi_ioctl_ops,
  33.     .release    = video_device_release,
  34.  
  35.     .tvnorms = V4L2_STD_525_60,
  36.     .current_norm = V4L2_STD_NTSC_M,
  37. };

我們找到了,就是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.     /*
  7.     * IN the open method, do only one job
  8.     * set v4l2_fh into filp->private_data for later use, and initial v4l2_fh
  9.     */
  10.     filp->private_data = fh;
  11.     if (fh == NULL)
  12.         return -ENOMEM;
  13.     v4l2_fh_init(fh, vdev);
  14.     v4l2_fh_add(fh);
  15.     return 0;
  16. }
  17. EXPORT_SYMBOL_GPL(v4l2_fh_open);

這個open方法將v4l2_fh 這個同樣很重要的結構體保存到filp->private中,並做一些初始化,具體過程暫且不說
這裏fops中的其他介面的實現比起open,方法是一樣的,而且更簡單

但是,這裏我真正想表達的東西還沒有出現,大家看到這裏的filp->private_data = fh;//問題就在這裏了,我們真正在readwrite中需要傳遞的想要使用的是vivi_dev其實,
而不是v4l2_fh這個資料結構,我們還是直接看看vivi_templatefops中定義的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.  
  6.     dprintk(dev, 1, "read called\n");
  7.     return vb2_read(&dev->vb_vidq, data, count, ppos,
  8.          file->f_flags & O_NONBLOCK);
  9. }

這裏大家看到了,方法獲取到vivi_dev這個資料結構,並傳遞到vb2_read這個方法中,這個方法時驅動中資料傳輸的關鍵,之後再慢慢研究,這裏同樣不深究
而這個vivi_dev是通過video_drvdata方法獲得的,這就是為什麼在上面我強調的要用
video_set_drvdata(vfd, dev);vivi_dev裝載到video_device

 

原文網址

http://www.csdn123.com/html/blogs/20130422/5306.htm

立你斯 發表在 痞客邦 留言(0) 人氣()