日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

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

發布時間:2025/3/15 编程问答 51 豆豆
生活随笔 收集整理的這篇文章主要介紹了 虚拟视频驱动程序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驅動的開發,學習
首先就行驅動的入口開始:

  • static?int?__init vivi_init(void)
  • {
  • ????const?struct font_desc?*font?=?find_font("VGA8x16");
  • ????int?ret?=?0,?i;

  • ????if?(font?==?NULL)?{
  • ????????printk(KERN_ERR?"vivi: could not find font\n");
  • ????????return?-ENODEV;
  • ????}
  • ????font8x16?=?font->data;

  • ????if?(n_devs?<=?0)
  • ????????n_devs?=?1;

  • ????for?(i?=?0;?i?<?n_devs;?i++)?{
  • ????????//Here?is?the most important
  • ????????ret?=?vivi_create_instance(i);
  • ????????if?(ret)?{
  • ????????????/*?If?some instantiations succeeded,?keep driver?*/
  • ????????????if?(i)
  • ????????????????ret?=?0;
  • ????????????break;
  • ????????}
  • ????}

  • ????if?(ret?<?0)?{
  • ????????printk(KERN_ERR?"vivi: error %d while loading driver\n",?ret);
  • ????????return ret;
  • ????}

  • ????printk(KERN_INFO?"Video Technology Magazine Virtual Video "
  • ????????????"Capture Board ver %u.%u.%u successfully loaded.\n",
  • ????????????(VIVI_VERSION?>>?16)?&?0xFF,?(VIVI_VERSION?>>?8)?&?0xFF,
  • ????????????VIVI_VERSION?&?0xFF);

  • ????/*?n_devs will reflect the actual number of allocated devices?*/
  • ????n_devs?=?i;

  • ????return ret;
  • }

  • static void __exit vivi_exit(void)
  • {
  • ????vivi_release();
  • }

  • module_init(vivi_init);
  • module_exit(vivi_exit);
  • 這其實最重要的就是上面標注備份,下面重點分析 vivi_create_instance 方法:

  • static?int?__init vivi_create_instance(int?inst)
  • {
  • ????struct vivi_dev?*dev;
  • ????struct video_device?*vfd;
  • ????struct v4l2_ctrl_handler?*hdl;
  • ????struct vb2_queue?*q;
  • ????int?ret;

  • ????dev?=?kzalloc(sizeof(*dev),?GFP_KERNEL);
  • ????if?(!dev)
  • ????????return?-ENOMEM;

  • ????//?set?the v4l2_device(the name)
  • ????snprintf(dev->v4l2_dev.name,?sizeof(dev->v4l2_dev.name),
  • ????????????"%s-%03d",?VIVI_MODULE_NAME,?inst);
  • ????
  • ????/*?
  • ????*?register the v4l2_device,?but you should pay attention here
  • ????*?the?"dev == NULL"?it means v4l2_device.dev?==?NULL
  • ????*?You did't?set?the v4l2_device.dev,?you will?set?it later
  • ????*/
  • ????ret?=?v4l2_device_register(NULL,?&dev->v4l2_dev);
  • ????if?(ret)
  • ????????goto free_dev;

  • ????/*?init the handle,?learn it later?*/
  • ????dev->fmt?=?&formats[0];
  • ????dev->width?=?640;
  • ????dev->height?=?480;
  • ????hdl?=?&dev->ctrl_handler;
  • ????v4l2_ctrl_handler_init(hdl,?11);
  • ????dev->volume?=?v4l2_ctrl_new_std(hdl,?&vivi_ctrl_ops,
  • ????????????V4L2_CID_AUDIO_VOLUME,?0,?255,?1,?200);
  • ????dev->brightness?=?v4l2_ctrl_new_std(hdl,?&vivi_ctrl_ops,
  • ????????????V4L2_CID_BRIGHTNESS,?0,?255,?1,?127);
  • ????dev->contrast?=?v4l2_ctrl_new_std(hdl,?&vivi_ctrl_ops,
  • ????????????V4L2_CID_CONTRAST,?0,?255,?1,?16);
  • ????dev->saturation?=?v4l2_ctrl_new_std(hdl,?&vivi_ctrl_ops,
  • ????????????V4L2_CID_SATURATION,?0,?255,?1,?127);
  • ????dev->hue?=?v4l2_ctrl_new_std(hdl,?&vivi_ctrl_ops,
  • ????????????V4L2_CID_HUE,?-128,?127,?1,?0);
  • ????dev->button?=?v4l2_ctrl_new_custom(hdl,?&vivi_ctrl_button,?NULL);
  • ????dev->int32?=?v4l2_ctrl_new_custom(hdl,?&vivi_ctrl_int32,?NULL);
  • ????dev->int64?=?v4l2_ctrl_new_custom(hdl,?&vivi_ctrl_int64,?NULL);
  • ????dev->boolean?=?v4l2_ctrl_new_custom(hdl,?&vivi_ctrl_boolean,?NULL);
  • ????dev->menu?=?v4l2_ctrl_new_custom(hdl,?&vivi_ctrl_menu,?NULL);
  • ????dev->string?=?v4l2_ctrl_new_custom(hdl,?&vivi_ctrl_string,?NULL);
  • ????if?(hdl->error)?{
  • ????????ret?=?hdl->error;
  • ????????goto unreg_dev;
  • ????}
  • ????dev->v4l2_dev.ctrl_handler?=?hdl;

  • ????/*?initialize locks?*/
  • ????spin_lock_init(&dev->slock);

  • ????/*?initialize queue,?learn it later?*/
  • ????q?=?&dev->vb_vidq;
  • ????memset(q,?0,?sizeof(dev->vb_vidq));
  • ????q->type?=?V4L2_BUF_TYPE_VIDEO_CAPTURE;
  • ????q->io_modes?=?VB2_MMAP?|?VB2_USERPTR?|?VB2_READ;
  • ????q->drv_priv?=?dev;
  • ????q->buf_struct_size?=?sizeof(struct vivi_buffer);
  • ????q->ops?=?&vivi_video_qops;
  • ????q->mem_ops?=?&vb2_vmalloc_memops;

  • ????vb2_queue_init(q);

  • ????mutex_init(&dev->mutex);

  • ????/*?init video dma queues?*/
  • ????INIT_LIST_HEAD(&dev->vidq.active);
  • ????init_waitqueue_head(&dev->vidq.wq);

  • ????/*?before register the video_device,?init the video_device data*/
  • ????ret?=?-ENOMEM;
  • ????vfd?=?video_device_alloc();
  • ????if?(!vfd)
  • ????????goto unreg_dev;

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

  • ????/*
  • ?????*?Provide a mutex?to?v4l2 core.?It will be used?to?protect
  • ?????*?all fops?and?v4l2 ioctls.
  • ?????*/
  • ????vfd->lock?=?&dev->mutex;

  • ????ret?=?video_register_device(vfd,?VFL_TYPE_GRABBER,?video_nr);
  • ????if?(ret?<?0)
  • ????????goto rel_vdev;

  • ????/*
  • ????*?You should pay attention?to?this method
  • ????*?here you?set?the vivi_dev into the vedio_device?for?the later use?in?fops
  • ????*?When you want?to?use the vivi_dev,?you use vedio_get_drvdata()?to?get
  • ????*/
  • ????video_set_drvdata(vfd,?dev);

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

  • ????if?(video_nr?!=?-1)
  • ????????video_nr++;

  • ????dev->vfd?=?vfd;

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

  • rel_vdev:
  • ????video_device_release(vfd);
  • unreg_dev:
  • ????v4l2_ctrl_handler_free(hdl);
  • ????v4l2_device_unregister(&dev->v4l2_dev);
  • free_dev:
  • ????kfree(dev);
  • ????return ret;
  • }
  • vivi_create_instance 方法中主要完成以下工作;
    1.首先通過v4l2_device_register() 方法注冊 v4l2_device
    2.ctrl_handler初始化
    3.互斥鎖,自旋鎖等初始化
    4.vb2_quene初始化
    5.init video dma queues
    6. 填充video_device,并且調用 video_register_device注冊video_device
    7.把vivi_dev結構set進video_device中, 方便之后使用,使用 video_set_drvdata ( vfd , ?dev ) ;
    8.設備信息加入的鏈表結構

    下面針對以上步驟做詳細分析:
    1.首先通過v4l2_device_register() 方法注冊 v4l2_device

  • int?v4l2_device_register(struct device?*dev,?struct v4l2_device?*v4l2_dev)
  • {
  • ????if?(v4l2_dev?==?NULL)
  • ????????return?-EINVAL;

  • ????INIT_LIST_HEAD(&v4l2_dev->subdevs);
  • ????spin_lock_init(&v4l2_dev->lock);
  • ????mutex_init(&v4l2_dev->ioctl_lock);

  • ????/*?initial the global priotity*/
  • ????v4l2_prio_init(&v4l2_dev->prio);
  • ????kref_init(&v4l2_dev->ref);
  • ????v4l2_dev->dev?=?dev;
  • ????if?(dev?==?NULL)?{
  • ????????/*?If?dev?==?NULL,?then?name must be filled?in?by the caller?*/
  • ????????WARN_ON(!v4l2_dev->name[0]);
  • ????????/*?Here give the caller a WARN,?tell the caller?to?set?the dev*/
  • ????????return 0;
  • ????}

  • ????/*?Set?name?to?driver name?+?device name?if?it?is?empty.?*/
  • ????if?(!v4l2_dev->name[0])
  • ????????snprintf(v4l2_dev->name,?sizeof(v4l2_dev->name),?"%s %s",
  • ????????????dev->driver->name,?dev_name(dev));

  • ????/*?Here?is?also very important,?you can?get?v4l2_device by use dev_get_drvdata*/
  • ????if?(!dev_get_drvdata(dev))
  • ????????dev_set_drvdata(dev,?v4l2_dev);
  • ????return 0;
  • }
  • EXPORT_SYMBOL_GPL(v4l2_device_register);
  • 在 v4l2_device_register方法中,進行v4l2設備優先級的初始化,這里把v4l2_device中記錄優先級狀態的v4l2_prio_state結構變量 prio清空,
    另外這里說一下kref結構定義的ref變量,這個變量保存打開設備的計數,這里第一次注冊設備,初始化ref為1,若之后重復注冊則會先檢查ref這個變量,
    如果ref為1,則表示已經注冊過了,避免重復注冊v4l2_device,這個初始化在kref_init方法中實現,這是我的理解, 可是我暫時還沒有找到檢查ref的地方,暫且跳過
    最后一步根據dev 結構體決定,如果dev不為空,則這里setv4l2_device的name,并且將v4l2_device結構set進dev中,方便后面獲取使用

    2. ctrl_handler初始化

  • /*?Initialize the handler?*/
  • int?v4l2_ctrl_handler_init(struct v4l2_ctrl_handler?*hdl,
  • ???????????? unsigned nr_of_controls_hint)
  • {
  • ????mutex_init(&hdl->lock);
  • ????INIT_LIST_HEAD(&hdl->ctrls);
  • ????INIT_LIST_HEAD(&hdl->ctrl_refs);
  • ????hdl->nr_of_buckets?=?1?+?nr_of_controls_hint?/?8;
  • ????hdl->buckets?=?kzalloc(sizeof(hdl->buckets[0])?*?hdl->nr_of_buckets,
  • ????????????????????????????????GFP_KERNEL);
  • ????hdl->error?=?hdl->buckets???0?:?-ENOMEM;
  • ????return hdl->error;
  • }
  • 這里還是很有必要了解一下v4l2_ctrl_handler這個結構體到底是干什么用的

  • /**?struct v4l2_ctrl_handler?-?The control handler keeps track of all the
  • ??*?controls:?both the controls owned by the handler?and?those inherited
  • ??*?from other handlers.
  • ??*?@lock:????Lock?to?control access?to?this handler?and?its controls.
  • ??*?@ctrls:????The list of controls owned by this handler.
  • ??*?@ctrl_refs:????The list of control references.
  • ??*?@cached:????The last found control reference.?It?is?common that the same
  • ??*????????control?is?needed multiple times,?so this?is?a simple
  • ??*????????optimization.
  • ??*?@buckets:????Buckets?for?the hashing.?Allows?for?quick control lookup.
  • ??*?@nr_of_buckets:?Total number of buckets?in?the?array.
  • ??*?@error:????The?error?code of the first failed control addition.
  • ??*/
  • struct v4l2_ctrl_handler?{
  • ????struct mutex lock;
  • ????struct list_head ctrls;
  • ????struct list_head ctrl_refs;
  • ????struct v4l2_ctrl_ref?*cached;
  • ????struct v4l2_ctrl_ref?**buckets;
  • ????u16 nr_of_buckets;
  • ????int?error;
  • };
  • 在v4l2_ctrl_handler_init方法中,主要通過nr_of_controls_hint變量的大小,計算nr_of_buckets,并為buckets申請空間,并將申請結果保存在error變量中,我感覺可以是用于以后方便check的
    dev->v4l2_dev.ctrl_handler = hdl;最后,關聯vivi_dev
    問題點:
    1. v4l2_ctrl_new_std
    2. v4l2_ctrl_new_custom
    以上兩個方法不是很理解,待以后研究

    3.互斥鎖,自旋鎖等 初始化
    這個比較簡單,就不在做說明了

    4.vb2_quene初始化
    首先還是很有必要看一下這個結構體

  • /**
  • ?*?struct vb2_queue?-?a videobuf queue
  • ?*
  • ?*?@type:????queue type?(see V4L2_BUF_TYPE_*?in?linux/videodev2.h
  • ?*?@io_modes:????supported io methods?(see vb2_io_modes enum)
  • ?*?@io_flags:????additional io flags?(see vb2_fileio_flags enum)
  • ?*?@ops:????driver-specific callbacks
  • ?*?@mem_ops:????memory allocator specific callbacks
  • ?*?@drv_priv:????driver?private?data
  • ?*?@buf_struct_size:?size of the driver-specific buffer structure;
  • ?*????????"0"?indicates the driver doesn't want?to?use a custom buffer
  • ?*????????structure type,?so sizeof(struct vb2_buffer)?will?is?used
  • ?*
  • ?*?@memory:????current memory type used
  • ?*?@bufs:????videobuf buffer structures
  • ?*?@num_buffers:?number of allocated/used buffers
  • ?*?@queued_list:?list of buffers currently queued from userspace
  • ?*?@queued_count:?number of buffers owned by the driver
  • ?*?@done_list:????list of buffers ready?to?be dequeued?to?userspace
  • ?*?@done_lock:????lock?to?protect done_list list
  • ?*?@done_wq:????waitqueue?for?processes waiting?for?buffers ready?to?be dequeued
  • ?*?@alloc_ctx:????memory type/allocator-specific contexts?for?each?plane
  • ?*?@streaming:????current streaming state
  • ?*?@fileio:????file io emulator internal data,?used only?if?emulator?is?active
  • ?*/
  • struct vb2_queue?{
  • ????enum v4l2_buf_type????????type;
  • ????unsigned?int????????????io_modes;
  • ????unsigned?int????????????io_flags;

  • ????const?struct vb2_ops????????*ops;
  • ????const?struct vb2_mem_ops????*mem_ops;
  • ????void????????????????*drv_priv;
  • ????unsigned?int????????????buf_struct_size;

  • /*?private:?internal use only?*/
  • ????enum v4l2_memory????????memory;
  • ????struct vb2_buffer????????*bufs[VIDEO_MAX_FRAME];
  • ????unsigned?int????????????num_buffers;

  • ????struct list_head????????queued_list;

  • ????atomic_t????????????queued_count;
  • ????struct list_head????????done_list;
  • ????spinlock_t????????????done_lock;
  • ????wait_queue_head_t????????done_wq;

  • ????void????????????????*alloc_ctx[VIDEO_MAX_PLANES];

  • ????unsigned?int????????????streaming:1;

  • ????struct vb2_fileio_data????????*fileio;
  • };
  • 在 v4l2_ctrl_handler_init方法中,首先對 vb2_quene其中的重要數據進行填充,最最重要的就是
    q->ops = &vivi_video_qops;
    q->mem_ops = &vb2_vmalloc_memops;
    這兩條簡單的復制語句責任重大啊, 這里先知道有這么一回事,后面補充

  • /**
  • ?*?struct vb2_ops?-?driver-specific callbacks
  • ?*
  • ?*?@queue_setup:????called from a VIDIOC_REQBUFS handler,?before
  • ?*????????????memory allocation;?driver should return the required
  • ?*????????????number of buffers?in?num_buffers,?the required number
  • ?*????????????of planes per buffer?in?num_planes;?the size of?each
  • ?*????????????plane should be?set?in?the sizes[]?array?and?optional
  • ?*????????????per-plane allocator specific context?in?alloc_ctxs[]
  • ?*????????????array
  • ?*?@wait_prepare:????release any locks taken?while?calling vb2 functions;
  • ?*????????????it?is?called before an ioctl needs?to?wait?for?a new
  • ?*????????????buffer?to?arrive;?required?to?avoid a deadlock?in
  • ?*????????????blocking access type
  • ?*?@wait_finish:????reacquire all locks released?in?the previous callback;
  • ?*????????????required?to?continue operation after sleeping?while
  • ?*????????????waiting?for?a new buffer?to?arrive
  • ?*?@buf_init:????????called once after allocating a buffer?(in?MMAP?case)
  • ?*????????????or?after acquiring a new USERPTR buffer;?drivers may
  • ?*????????????perform additional buffer-related initialization;
  • ?*????????????initialization failure?(return?!=?0)?will prevent
  • ?*????????????queue setup from completing successfully;?optional
  • ?*?@buf_prepare:????called every?time?the buffer?is?queued from userspace;
  • ?*????????????drivers may perform any initialization required before
  • ?*????????????each?hardware operation?in?this callback;
  • ?*????????????if?an?error?is?returned,?the buffer will?not?be queued
  • ?*????????????in?driver;?optional
  • ?*?@buf_finish:????????called before every dequeue of the buffer back?to
  • ?*????????????userspace;?drivers may perform any operations required
  • ?*????????????before userspace accesses the buffer;?optional
  • ?*?@buf_cleanup:????called once before the buffer?is?freed;?drivers may
  • ?*????????????perform any additional cleanup;?optional
  • ?*?@start_streaming:????called once before entering?'streaming'?state;?enables
  • ?*????????????driver?to?receive buffers over buf_queue()?callback
  • ?*?@stop_streaming:????called when?'streaming'?state must be disabled;?driver
  • ?*????????????should stop any DMA transactions?or?wait?until?they
  • ?*????????????finish?and?give back all buffers it got from buf_queue()
  • ?*????????????callback;?may use vb2_wait_for_all_buffers()?function
  • ?*?@buf_queue:????????passes buffer vb?to?the driver;?driver may start
  • ?*????????????hardware operation?on?this buffer;?driver should give
  • ?*????????????the buffer back by calling vb2_buffer_done()?function
  • ?*/
  • struct vb2_ops?{
  • ????int?(*queue_setup)(struct vb2_queue?*q,?unsigned?int?*num_buffers,
  • ???????????? unsigned?int?*num_planes,?unsigned long sizes[],
  • ???????????? void?*alloc_ctxs[]);

  • ????void?(*wait_prepare)(struct vb2_queue?*q);
  • ????void?(*wait_finish)(struct vb2_queue?*q);

  • ????int?(*buf_init)(struct vb2_buffer?*vb);
  • ????int?(*buf_prepare)(struct vb2_buffer?*vb);
  • ????int?(*buf_finish)(struct vb2_buffer?*vb);
  • ????void?(*buf_cleanup)(struct vb2_buffer?*vb);

  • ????int?(*start_streaming)(struct vb2_queue?*q);
  • ????int?(*stop_streaming)(struct vb2_queue?*q);

  • ????void?(*buf_queue)(struct vb2_buffer?*vb);
  • };
  • ?

  • /**
  • ?*?struct vb2_mem_ops?-?memory handling/memory allocator operations
  • ?*?@alloc:????allocate video memory?and,?optionally,?allocator?private?data,
  • ?*????????return?NULL?on?failure?or?a pointer?to?allocator?private,
  • ?*????????per-buffer data?on?success;?the returned?private?structure
  • ?*????????will?then?be passed as buf_priv argument?to?other ops?in?this
  • ?*????????structure
  • ?*?@put:????inform the allocator that the buffer will no longer be used;
  • ?*????????usually will result?in?the allocator freeing the buffer?(if
  • ?*????????no other users of this buffer are present);?the buf_priv
  • ?*????????argument?is?the allocator?private?per-buffer structure
  • ?*????????previously returned from the alloc callback
  • ?*?@get_userptr:?acquire userspace memory?for?a hardware operation;?used?for
  • ?*???????? USERPTR memory types;?vaddr?is?the address passed?to?the
  • ?*???????? videobuf layer when queuing a video buffer of USERPTR type;
  • ?*???????? should return an allocator?private?per-buffer structure
  • ?*???????? associated with the buffer?on?success,?NULL?on?failure;
  • ?*???????? the returned?private?structure will?then?be passed as buf_priv
  • ?*???????? argument?to?other ops?in?this structure
  • ?*?@put_userptr:?inform the allocator that a USERPTR buffer will no longer
  • ?*???????? be used
  • ?*?@vaddr:????return a kernel virtual address?to?a given memory buffer
  • ?*????????associated with the passed?private?structure?or?NULL?if?no
  • ?*????????such mapping exists
  • ?*?@cookie:????return allocator specific cookie?for?a given memory buffer
  • ?*????????associated with the passed?private?structure?or?NULL?if?not
  • ?*????????available
  • ?*?@num_users:????return the current number of users of a memory buffer;
  • ?*????????return 1?if?the videobuf layer?(or?actually the driver using
  • ?*????????it)?is?the only user
  • ?*?@mmap:????setup a userspace mapping?for?a given memory buffer under
  • ?*????????the provided virtual memory region
  • ?*
  • ?*?Required ops?for?USERPTR types:?get_userptr,?put_userptr.
  • ?*?Required ops?for?MMAP types:?alloc,?put,?num_users,?mmap.
  • ?*?Required ops?for?read/write access types:?alloc,?put,?num_users,?vaddr
  • ?*/
  • struct vb2_mem_ops?{
  • ????void????????*(*alloc)(void?*alloc_ctx,?unsigned long size);
  • ????void????????(*put)(void?*buf_priv);

  • ????void????????*(*get_userptr)(void?*alloc_ctx,?unsigned long vaddr,
  • ????????????????????unsigned long size,?int?write);
  • ????void????????(*put_userptr)(void?*buf_priv);

  • ????void????????*(*vaddr)(void?*buf_priv);
  • ????void????????*(*cookie)(void?*buf_priv);

  • ????unsigned?int????(*num_users)(void?*buf_priv);

  • ????int????????(*mmap)(void?*buf_priv,?struct vm_area_struct?*vma);
  • };
  • 最后調用 vb2_queue_init 方法,進行初始化

  • /**
  • ?*?vb2_queue_init()?-?initialize a videobuf2 queue
  • ?*?@q:????????videobuf2 queue;?this structure should be allocated?in?driver
  • ?*
  • ?*?The vb2_queue structure should be allocated by the driver.?The driver?is
  • ?*?responsible of clearing it's content?and?setting initial values?for?some
  • ?*?required entries before calling this?function.
  • ?*?q->ops,?q->mem_ops,?q->type?and?q->io_modes are mandatory.?Please refer
  • ?*?to?the struct vb2_queue description?in?include/media/videobuf2-core.h
  • ?*?for?more information.
  • ?*/
  • int?vb2_queue_init(struct vb2_queue?*q)
  • {
  • ????BUG_ON(!q);
  • ????BUG_ON(!q->ops);
  • ????BUG_ON(!q->mem_ops);
  • ????BUG_ON(!q->type);
  • ????BUG_ON(!q->io_modes);

  • ????BUG_ON(!q->ops->queue_setup);
  • ????BUG_ON(!q->ops->buf_queue);

  • ????INIT_LIST_HEAD(&q->queued_list);
  • ????INIT_LIST_HEAD(&q->done_list);
  • ????spin_lock_init(&q->done_lock);
  • ????init_waitqueue_head(&q->done_wq);

  • ????if?(q->buf_struct_size?==?0)
  • ????????q->buf_struct_size?=?sizeof(struct vb2_buffer);

  • ????return 0;
  • }
  • EXPORT_SYMBOL_GPL(vb2_queue_init);
  • 這里方法很簡單,只是進行check,然后最最簡單的初始化,這里不再多說了

    5. init video dma queues
    只有兩條語句進行初始化
    INIT_LIST_HEAD(&dev->vidq.active);
    init_waitqueue_head(&dev->vidq.wq);

    6. 填充video_device,并且調用 video_register_device注冊video_device
    這里終于到了重點了,很重要,開始了
    開始簡單,申請內存空間并進行填充,然后才真正調用用video_register_device這個方法,開始了
    但是在調用之前的這條語句你必須關注vfd->v4l2_dev = &dev->v4l2_dev;從這里也可以知道v4l2_device和video_device的注冊順序

  • int?__video_register_device(struct video_device?*vdev,?int?type,?int?nr,
  • ????????int?warn_if_nr_in_use,?struct module?*owner)
  • {
  • ????int?i?=?0;
  • ????int?ret;
  • ????int?minor_offset?=?0;
  • ????int?minor_cnt?=?VIDEO_NUM_DEVICES;
  • ????const?char?*name_base;

  • ????/*?A minor value of?-1 marks this video device as never
  • ???? having been registered?*/
  • ????vdev->minor?=?-1;

  • ????/*?the release callback MUST be present?*/
  • ????WARN_ON(!vdev->release);
  • ????if?(!vdev->release)
  • ????????return?-EINVAL;

  • ????/*?v4l2_fh support?*/
  • ????spin_lock_init(&vdev->fh_lock);
  • ????INIT_LIST_HEAD(&vdev->fh_list);

  • ????/*?Part 1:?check device type?*/
  • ????/*?after here,?you can see videx?...the char device?in?/dev?*/
  • ? ??//這里還是單獨說一下吧,最終你在/dev目錄下看到的video0就是在這里決定的,大家可以知道,可不是一定名字叫video的
  • ????switch?(type)?{
  • ????case?VFL_TYPE_GRABBER:
  • ????????name_base?=?"video";
  • ????????break;
  • ????case?VFL_TYPE_VBI:
  • ????????name_base?=?"vbi";
  • ????????break;
  • ????case?VFL_TYPE_RADIO:
  • ????????name_base?=?"radio";
  • ????????break;
  • ????case?VFL_TYPE_SUBDEV:
  • ????????name_base?=?"v4l-subdev";
  • ????????break;
  • ????default:
  • ????????printk(KERN_ERR?"%s called with unknown type: %d\n",
  • ???????? __func__,?type);
  • ????????return?-EINVAL;
  • ????}

  • ????vdev->vfl_type?=?type;
  • ????vdev->cdev?=?NULL;
  • ????if?(vdev->v4l2_dev)?{
  • ????????if?(vdev->v4l2_dev->dev)
  • ????????? ??//這里說明vdev和保存在v4l2_device的dev具有共同的parent,對之后sys 接口那里有用
  • ????????????vdev->parent?=?vdev->v4l2_dev->dev;
  • ????????if?(vdev->ctrl_handler?==?NULL)
  • ????????? ??//上面初始化的ctrl_handler在這里要派上用處了,而且同時指向video_device和v4l2_device的ctrl_handler
  • ????????????vdev->ctrl_handler?=?vdev->v4l2_dev->ctrl_handler;
  • ????????/*?If?the prio state pointer?is?NULL,?then?use the v4l2_device
  • ???????? prio state.?*/
  • ????????if?(vdev->prio?==?NULL)
  • ????????????//上面初始化的prio在這里要派上用處了,而且同時指向video_device和v4l2_device的prio
  • ????????????vdev->prio?=?&vdev->v4l2_dev->prio;
  • ????}
  • ??????//從這里往下挺長一段代碼是在為要申請的字符設備尋找一個合適的設備號,這里不去深入追究了,有時間可以可慮回來看看
  • ????/*?Part 2:?find a free minor,?device node number?and?device index.?*/
  • #ifdef CONFIG_VIDEO_FIXED_MINOR_RANGES
  • ????/*?Keep the ranges?for?the first four types?for?historical
  • ?????*?reasons.
  • ?????*?Newer devices?(not?yet?in?place)?should use the range
  • ?????*?of 128-191?and?just pick the first free minor there
  • ?????*?(new style).?*/
  • ????switch?(type)?{
  • ????case?VFL_TYPE_GRABBER:
  • ????????minor_offset?=?0;
  • ????????minor_cnt?=?64;
  • ????????break;
  • ????case?VFL_TYPE_RADIO:
  • ????????minor_offset?=?64;
  • ????????minor_cnt?=?64;
  • ????????break;
  • ????case?VFL_TYPE_VBI:
  • ????????minor_offset?=?224;
  • ????????minor_cnt?=?32;
  • ????????break;
  • ????default:
  • ????????minor_offset?=?128;
  • ????????minor_cnt?=?64;
  • ????????break;
  • ????}
  • #endif

  • ????/*?Pick a device node number?*/
  • ????mutex_lock(&videodev_lock);
  • ????nr?=?devnode_find(vdev,?nr?==?-1???0?:?nr,?minor_cnt);
  • ????if?(nr?==?minor_cnt)
  • ????????nr?=?devnode_find(vdev,?0,?minor_cnt);
  • ????if?(nr?==?minor_cnt)?{
  • ????????printk(KERN_ERR?"could not get a free device node number\n");
  • ????????mutex_unlock(&videodev_lock);
  • ????????return?-ENFILE;
  • ????}
  • #ifdef CONFIG_VIDEO_FIXED_MINOR_RANGES
  • ????/*?1-on-1 mapping of device node number?to?minor number?*/
  • ????i?=?nr;
  • #else
  • ????/*?The device node number?and?minor numbers are independent,?so
  • ???? we just find the first free minor number.?*/
  • ????for?(i?=?0;?i?<?VIDEO_NUM_DEVICES;?i++)
  • ????????if?(video_device[i]?==?NULL)
  • ????????????break;
  • ????if?(i?==?VIDEO_NUM_DEVICES)?{
  • ????????mutex_unlock(&videodev_lock);
  • ????????printk(KERN_ERR?"could not get a free minor\n");
  • ????????return?-ENFILE;
  • ????}
  • #endif
  • ????vdev->minor?=?i?+?minor_offset;
  • ????vdev->num?=?nr;
  • ????devnode_set(vdev);

  • ????/*?Should?not?happen since we thought this minor was free?*/
  • ????WARN_ON(video_device[vdev->minor]?!=?NULL);
  • ????vdev->index?=?get_index(vdev);
  • ????mutex_unlock(&videodev_lock);

  • ?????//上面的方法獲取到了那個合適的設備號,現在要開始注冊我們的字符設備了
  • ????/*?Part 3:?Initialize the character device?*/
  • ????vdev->cdev?=?cdev_alloc();
  • ????if?(vdev->cdev?==?NULL)?{
  • ????????ret?=?-ENOMEM;
  • ????????goto cleanup;
  • ????}
  • ????vdev->cdev->ops?=?&v4l2_fops;//most important part,操作設備的通道
  • ????vdev->cdev->owner?=?owner;
  • ????ret?=?cdev_add(vdev->cdev,?MKDEV(VIDEO_MAJOR,?vdev->minor),?1);
  • ????if?(ret?<?0)?{
  • ????????printk(KERN_ERR?"%s: cdev_add failed\n",?__func__);
  • ????????kfree(vdev->cdev);
  • ????????vdev->cdev?=?NULL;
  • ????????goto cleanup;
  • ????}

  • ?????//這里我們也大可先不用關注,主要是在sysfs的一些設備添加等等
  • ????/*?Part 4:?register the device with sysfs?*/
  • ????vdev->dev.class?=?&video_class;
  • ????vdev->dev.devt?=?MKDEV(VIDEO_MAJOR,?vdev->minor);
  • ????if?(vdev->parent)
  • ????????vdev->dev.parent?=?vdev->parent;
  • ????dev_set_name(&vdev->dev,?"%s%d",?name_base,?vdev->num);
  • ????ret?=?device_register(&vdev->dev);
  • ????if?(ret?<?0)?{
  • ????????printk(KERN_ERR?"%s: device_register failed\n",?__func__);
  • ????????goto cleanup;
  • ????}
  • ????/*?Register the release callback that will be called when the last
  • ???? reference?to?the device goes away.?*/
  • ????vdev->dev.release?=?v4l2_device_release;

  • ????if?(nr?!=?-1?&&?nr?!=?vdev->num?&&?warn_if_nr_in_use)
  • ????????printk(KERN_WARNING?"%s: requested %s%d, got %s\n",?__func__,
  • ????????????name_base,?nr,?video_device_node_name(vdev));

  • ????/*?Increase v4l2_device refcount?*/
  • ????if?(vdev->v4l2_dev)
  • ????????v4l2_device_get(vdev->v4l2_dev);

  • #if?defined(CONFIG_MEDIA_CONTROLLER)
  • ? ? ?//這里其實還是比較重要的,不過不是所以的驅動都要添加這一個步驟,這也是為什么有一個if?define 的原因了
    ?????//意思就是如果這個驅動中需要用到media controler的時候就需要在這里注冊media_device
    ?????//這里同樣先不做深入研究,media_device和media_entity這兩個重要結構體之后還要研究
  • ????/*?Part 5:?Register the entity.?*/
  • ????if?(vdev->v4l2_dev?&&?vdev->v4l2_dev->mdev?&&
  • ???? vdev->vfl_type?!=?VFL_TYPE_SUBDEV)?{
  • ????????vdev->entity.type?=?MEDIA_ENT_T_DEVNODE_V4L;
  • ????????vdev->entity.name?=?vdev->name;
  • ????????vdev->entity.v4l.major?=?VIDEO_MAJOR;
  • ????????vdev->entity.v4l.minor?=?vdev->minor;
  • ????????ret?=?media_device_register_entity(vdev->v4l2_dev->mdev,
  • ????????????&vdev->entity);
  • ????????if?(ret?<?0)
  • ????????????printk(KERN_WARNING
  • ?????????????"%s: media_device_register_entity failed\n",
  • ???????????? __func__);
  • ????}
  • #endif
  • ? ??//保存注冊成功標記,并將注冊成功的video_device保存到全局數組video_device中,大功告成
  • ????/*?Part 6:?Activate this minor.?The char device can?now?be used.?*/
  • ????set_bit(V4L2_FL_REGISTERED,?&vdev->flags);//設置標志位,之后還會遇到test_bit方法用來check flags的第nr位是否為1.
  • ? ? //這里還是多說一點,另外還有兩中標志位需要知道:V4L2_FL_USES_V4L2_FH,?V4L2_FL_USE_FH_PRIO
  • ????mutex_lock(&videodev_lock);
  • ????video_device[vdev->minor]?=?vdev;
  • ????mutex_unlock(&videodev_lock);

  • ????return 0;
  • ?//這里是出錯處理函數
  • cleanup:
  • ????mutex_lock(&videodev_lock);
  • ????if?(vdev->cdev)
  • ????????cdev_del(vdev->cdev);
  • ????devnode_clear(vdev);
  • ????mutex_unlock(&videodev_lock);
  • ????/*?Mark this video device as never having been registered.?*/
  • ????vdev->minor?=?-1;
  • ????return ret;
  • }
  • EXPORT_SYMBOL(__video_register_device);

  • 7.把vivi_dev結構set進video_device中, 方便之后使用, 設備信息加入的鏈表結構
    最后結尾的這段代碼這里我決定單獨放在下面分析,也算妥善收尾吧

  • ????/*
  • ????*?You should pay attention?to?this method
  • ????*?here you?set?the vivi_dev into the vedio_device?for?the later use?in?fops
  • ????*?When you want?to?use the vivi_dev,?you use vedio_get_drvdata()?to?get
  • ????*/
  • ????video_set_drvdata(vfd,?dev);

  • ????/*?Now?that everything?is?fine,?let's add it?to?device list?*/
  • ????list_add_tail(&dev->vivi_devlist,?&vivi_devlist);//添加的device list當中

  • ????if?(video_nr?!=?-1)
  • ????????video_nr++;//用于計數,找到設備

  • ????dev->vfd?=?vfd;關聯video_device和vivi_dev
  • 短短的幾條語句,但我看來,個個都短小精悍
    首先說說這個方法,他有什么用處呢?其實用處可大了,就行我上面注釋的那樣,這里把vivi_dev設置到vedio_device中,是為了之后字符設備訪問接口中使用
    這里多說一點,也算順便說一下用戶空間操作設備的流程了
    首先當時用戶空間訪問設備了,這個做驅動的不懂那可糗大了,用戶空間open時,也就是啟動了上面video_device_register方法中的很重要的下面結構中的open方法

  • static?const?struct file_operations v4l2_fops?=?{
  • ????.owner?=?THIS_MODULE,
  • ????.read?=?v4l2_read,
  • ????.write?=?v4l2_write,
  • ????.open?=?v4l2_open,
  • ????.get_unmapped_area?=?v4l2_get_unmapped_area,
  • ????.mmap?=?v4l2_mmap,
  • ????.unlocked_ioctl?=?v4l2_ioctl,
  • #ifdef CONFIG_COMPAT
  • ????.compat_ioctl?=?v4l2_compat_ioctl32,
  • #endif
  • ????.release?=?v4l2_release,
  • ????.poll?=?v4l2_poll,
  • ????.llseek?=?no_llseek,
  • };
  • 我們來看一下這個open方法

  • /*?Override?for?the open?function?*/
  • static?int?v4l2_open(struct inode?*inode,?struct file?*filp)
  • {
  • ????struct video_device?*vdev;
  • ????int?ret?=?0;

  • ????/*?Check?if?the video device?is?available?*/
  • ????mutex_lock(&videodev_lock);
  • ????vdev?=?video_devdata(filp);
  • ????/*?return ENODEV?if?the video device has already been removed.?*/
  • ????if?(vdev?==?NULL?||?!video_is_registered(vdev))?{
  • ????????mutex_unlock(&videodev_lock);
  • ????????return?-ENODEV;
  • ????}
  • ????/*?and?increase the device refcount?*/
  • ????video_get(vdev);//這里是用來計數的
  • ????mutex_unlock(&videodev_lock);

  • ????/*?
  • ????*?Here using the API you?get?the method you?get?the open()?method write
  • ????*?The other methods?in?fops use the same method?to?use you own code?
  • ????*/
  • ????if?(vdev->fops->open)?{
  • ????????if?(vdev->lock?&&?mutex_lock_interruptible(vdev->lock))?{
  • ????????????ret?=?-ERESTARTSYS;
  • ????????????goto?err;
  • ????????}
  • ????????if?(video_is_registered(vdev))
  • ????????????ret?=?vdev->fops->open(filp);
  • ????????else
  • ????????????ret?=?-ENODEV;
  • ????????if?(vdev->lock)
  • ????????????mutex_unlock(vdev->lock);
  • ????}

  • err:
  • ????/*?decrease the refcount?in?case?of an?error?*/
  • ????if?(ret)
  • ????????video_put(vdev);
  • ????return ret;
  • }
  • 只有最下面的那個標準才是重點,經過那么多的check,最后走的了最后這一步, ret? = ?vdev - > fops - > open ( filp ) ;
    這個open方法在哪里呢?那就沿著箭頭方向找吧,是video_device內部的fops中的open方法,這個方法不是有在哪里呢?
    接著找,在 video_device_register方法之前
    *vfd = vivi_template;/* the most important struct */我還特意在這里寫了最重要的結構體
    所以最終調用的是 vivi_template 中fops中定義的open方法

  • static?const?struct v4l2_file_operations vivi_fops?=?{
  • ????.owner????????=?THIS_MODULE,
  • ????.open????????=?v4l2_fh_open,
  • ????.release?=?vivi_close,
  • ????.read?=?vivi_read,
  • ????.poll????????=?vivi_poll,
  • ????.unlocked_ioctl?=?video_ioctl2,?/*?V4L2 ioctl handler?*/
  • ????.mmap?=?vivi_mmap,
  • };

  • static?const?struct v4l2_ioctl_ops vivi_ioctl_ops?=?{
  • ????.vidioc_querycap?=?vidioc_querycap,
  • ????.vidioc_enum_fmt_vid_cap?=?vidioc_enum_fmt_vid_cap,
  • ????.vidioc_g_fmt_vid_cap?=?vidioc_g_fmt_vid_cap,
  • ????.vidioc_try_fmt_vid_cap?=?vidioc_try_fmt_vid_cap,
  • ????.vidioc_s_fmt_vid_cap?=?vidioc_s_fmt_vid_cap,
  • ????.vidioc_reqbufs?=?vidioc_reqbufs,
  • ????.vidioc_querybuf?=?vidioc_querybuf,
  • ????.vidioc_qbuf?=?vidioc_qbuf,
  • ????.vidioc_dqbuf?=?vidioc_dqbuf,
  • ????.vidioc_s_std?=?vidioc_s_std,
  • ????.vidioc_enum_input?=?vidioc_enum_input,
  • ????.vidioc_g_input?=?vidioc_g_input,
  • ????.vidioc_s_input?=?vidioc_s_input,
  • ????.vidioc_streamon?=?vidioc_streamon,
  • ????.vidioc_streamoff?=?vidioc_streamoff,
  • };

  • static struct video_device vivi_template?=?{
  • ????.name????????=?"vivi",
  • ????.fops?=?&vivi_fops,
  • ????.ioctl_ops ????=?&vivi_ioctl_ops,
  • ????.release????=?video_device_release,

  • ????.tvnorms?=?V4L2_STD_525_60,
  • ????.current_norm?=?V4L2_STD_NTSC_M,
  • };
  • 我們找到了,就是 v4l2_fh_open 這個方法,這個方法與我們之前寫字符驅動時遇到的情況很是不同,他的open方法其實是有內核API寫好的,我們先看看

  • int?v4l2_fh_open(struct file?*filp)
  • {
  • ????struct video_device?*vdev?=?video_devdata(filp);
  • ????struct v4l2_fh?*fh?=?kzalloc(sizeof(*fh),?GFP_KERNEL);

  • ????/*
  • ????*?IN?the open method,?do?only one job
  • ????*?set?v4l2_fh into filp->private_data?for?later use,?and?initial v4l2_fh
  • ????*/
  • ????filp->private_data?=?fh;
  • ????if?(fh?==?NULL)
  • ????????return?-ENOMEM;
  • ????v4l2_fh_init(fh,?vdev);
  • ????v4l2_fh_add(fh);
  • ????return 0;
  • }
  • EXPORT_SYMBOL_GPL(v4l2_fh_open);
  • 這個open方法將v4l2_fh 這個同樣很重要的結構體保存到filp->private中,并做一些初始化,具體過程暫且不說
    這里fops中的其他接口的實現比起open,方法是一樣的,而且更簡單

    但是,這里我真正想表達的東西還沒有出現,大家看到這里的 filp - > private_data? = ?fh ;//問題就在這里了,我們真正在read,write中需要傳遞的想要使用的是vivi_dev其實,
    而不是v4l2_fh這個數據結構,我們還是直接看看
    vivi_template 中fops中 定義的read方法吧

  • static ssize_t
  • vivi_read(struct file?*file,?char __user?*data,?size_t count,?loff_t?*ppos)
  • {
  • ????struct vivi_dev?*dev?=?video_drvdata(file);

  • ????dprintk(dev,?1,?"read called\n");
  • ????return vb2_read(&dev->vb_vidq,?data,?count,?ppos,
  • ???????? file->f_flags?&?O_NONBLOCK);
  • }
  • 這里大家看到了,方法獲取到vivi_dev這個數據結構,并傳遞到vb2_read這個方法中,這個方法時驅動中數據傳輸的關鍵,之后再慢慢研究,這里同樣不深究
    而這個vivi_dev是通過video_drvdata方法獲得的,這就是為什么在上面我強調的要用
    video_set_drvdata ( vfd , ?dev ) ; 把vivi_dev裝載到video_device中

    到這里基本的過程都整理完了,其實大頭還在后面呢,待續。。。。。。

    轉載于:https://www.cnblogs.com/jiangu66/archive/2013/04/23/3037421.html

    總結

    以上是生活随笔為你收集整理的虚拟视频驱动程序vivi.c源码分析的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。