zoukankan      html  css  js  c++  java
  • 虚拟视频驱动程序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.     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 (= 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.     /* 
    16.     * register the v4l2_device, but you should pay attention here
    17.     * the "dev == NULL" it means v4l2_device.dev == NULL
    18.     * You did'set the v4l2_device.dev, you will set it later
    19.     */
    20.     ret = v4l2_device_register(NULL, &dev->v4l2_dev);
    21.     if (ret)
    22.         goto free_dev;

    23.     /* init the handle, learn it later */
    24.     dev->fmt = &formats[0];
    25.     dev->width = 640;
    26.     dev->height = 480;
    27.     hdl = &dev->ctrl_handler;
    28.     v4l2_ctrl_handler_init(hdl, 11);
    29.     dev->volume = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops,
    30.             V4L2_CID_AUDIO_VOLUME, 0, 255, 1, 200);
    31.     dev->brightness = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops,
    32.             V4L2_CID_BRIGHTNESS, 0, 255, 1, 127);
    33.     dev->contrast = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops,
    34.             V4L2_CID_CONTRAST, 0, 255, 1, 16);
    35.     dev->saturation = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops,
    36.             V4L2_CID_SATURATION, 0, 255, 1, 127);
    37.     dev->hue = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops,
    38.             V4L2_CID_HUE, -128, 127, 1, 0);
    39.     dev->button = v4l2_ctrl_new_custom(hdl, &vivi_ctrl_button, NULL);
    40.     dev->int32 = v4l2_ctrl_new_custom(hdl, &vivi_ctrl_int32, NULL);
    41.     dev->int64 = v4l2_ctrl_new_custom(hdl, &vivi_ctrl_int64, NULL);
    42.     dev->boolean = v4l2_ctrl_new_custom(hdl, &vivi_ctrl_boolean, NULL);
    43.     dev->menu = v4l2_ctrl_new_custom(hdl, &vivi_ctrl_menu, NULL);
    44.     dev->string = v4l2_ctrl_new_custom(hdl, &vivi_ctrl_string, NULL);
    45.     if (hdl->error) {
    46.         ret = hdl->error;
    47.         goto unreg_dev;
    48.     }
    49.     dev->v4l2_dev.ctrl_handler = hdl;

    50.     /* initialize locks */
    51.     spin_lock_init(&dev->slock);

    52.     /* initialize queue, learn it later */
    53.     q = &dev->vb_vidq;
    54.     memset(q, 0, sizeof(dev->vb_vidq));
    55.     q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    56.     q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_READ;
    57.     q->drv_priv = dev;
    58.     q->buf_struct_size = sizeof(struct vivi_buffer);
    59.     q->ops = &vivi_video_qops;
    60.     q->mem_ops = &vb2_vmalloc_memops;

    61.     vb2_queue_init(q);

    62.     mutex_init(&dev->mutex);

    63.     /* init video dma queues */
    64.     INIT_LIST_HEAD(&dev->vidq.active);
    65.     init_waitqueue_head(&dev->vidq.wq);

    66.     /* before register the video_device, init the video_device data*/
    67.     ret = -ENOMEM;
    68.     vfd = video_device_alloc();
    69.     if (!vfd)
    70.         goto unreg_dev;

    71.     *vfd = vivi_template;/* the most important struct */
    72.     vfd->debug = debug;
    73.     vfd->v4l2_dev = &dev->v4l2_dev; /* here set the v4l2_device, you have already registered it */
    74.     set_bit(V4L2_FL_USE_FH_PRIO, &vfd->flags);

    75.     /*
    76.      * Provide a mutex to v4l2 core. It will be used to protect
    77.      * all fops and v4l2 ioctls.
    78.      */
    79.     vfd->lock = &dev->mutex;

    80.     ret = video_register_device(vfd, VFL_TYPE_GRABBER, video_nr);
    81.     if (ret < 0)
    82.         goto rel_vdev;

    83.     /*
    84.     * You should pay attention to this method
    85.     * here you set the vivi_dev into the vedio_device for the later use in fops
    86.     * When you want to use the vivi_dev, you use vedio_get_drvdata() to get
    87.     */
    88.     video_set_drvdata(vfd, dev);

    89.     /* Now that everything is fine, let's add it to device list */
    90.     list_add_tail(&dev->vivi_devlist, &vivi_devlist);

    91.     if (video_nr != -1)
    92.         video_nr++;

    93.     dev->vfd = vfd;

    94.     /* the debug message*/
    95.     v4l2_info(&dev->v4l2_dev, "V4L2 device registered as %s\n",
    96.          video_device_node_name(vfd));
    97.     return 0;

    98. rel_vdev:
    99.     video_device_release(vfd);
    100. unreg_dev:
    101.     v4l2_ctrl_handler_free(hdl);
    102.     v4l2_device_unregister(&dev->v4l2_dev);
    103. free_dev:
    104.     kfree(dev);
    105.     return ret;
    106. }

    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 == -? 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 (= 0; i < VIDEO_NUM_DEVICES; i++)
    44.         if (video_device[i] == NULL)
    45.             break;
    46.     if (== 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 != -&& 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中

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

  • 相关阅读:
    如何开始DDD(续)
    如何开始DDD
    ThinkNet终于见面了
    [Umbraco] umbraco中如何分页
    ETL 工具下载全集 包括 Informatica Datastage Cognos( 持续更新)
    js时间对比-转化为几天前,几小时前,几分钟前
    原生JS实现返回顶部和滚动锚点
    JSONP原理及简单实现 可做简单插件使用
    CSS3 transition效果 360度旋转 旋转放大 放大 移动
    js获取url的常用方法
  • 原文地址:https://www.cnblogs.com/jiangu66/p/3037421.html
Copyright © 2011-2022 走看看