日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪(fǎng)問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

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

發(fā)布時(shí)間:2025/3/15 编程问答 55 豆豆
生活随笔 收集整理的這篇文章主要介紹了 虚拟视频驱动程序vivi.c源码分析 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
虛擬視頻驅(qū)動(dòng)程序vivi.c源碼分析


以下先把上一篇文章中的最后一段,放在這里利于程序源碼的分析:
vivi.c 虛擬視頻驅(qū)動(dòng)程序----- 此代碼模擬一個(gè)真正的視頻設(shè)備V4L2 API (位于drivers/media/video目錄下)
入口:+int __init vivi_init(void)
? ? ? ? ? ? ? ?+ vivi_create_instance(i) /*創(chuàng)建設(shè)備*//**/。
? ? ? ? ? ? ? ? ? ? ? ?+ 分配一個(gè)vivi_dev的結(jié)構(gòu)體 /*它嵌套這結(jié)構(gòu)體v4l2_device 和video_device*/
? ? ? ? ? ? ? ? ? ? ? ?+ v4l2_device_register(NULL, &dev->v4l2_dev);/*注冊(cè)vivi_dev中的V4l2_device*/
? ? ? ? ? ? ? ? ? ? ? ?+ 初始化視頻的DMA隊(duì)列
? ? ? ? ? ? ? ? ? ? ? ?+ 初始化鎖
? ? ? ? ? ? ? ? ? ? ? ?+ video_device_alloc(); 動(dòng)態(tài)分配video_device結(jié)構(gòu)體
? ? ? ? ? ? ? ? ? ? ? ?+ 構(gòu)建一個(gè)video_device結(jié)構(gòu)體 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);設(shè)置驅(qū)動(dòng)程序?qū)S袛?shù)據(jù)
? ? ? ? ? ? ? ? ? ? ?+ 所有控件設(shè)置為其默認(rèn)值
? ? ? ? ? ? ? ? ? ? ?+ list_add_tail(&dev->vivi_devlist, &vivi_devlist);添加到設(shè)備列表
? ? ? ? + 構(gòu)建 v4l2_file_operations 結(jié)構(gòu)體vivi_fops 并實(shí)現(xiàn).open .release .read .poll .mmap函數(shù)----- .ioctl 用標(biāo)準(zhǔn)的v4l2控制處理程序
? ? ? ? + 構(gòu)建 v4l2_ioctl_ops結(jié)構(gòu)體 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); ?訪(fǎng)問(wèn)驅(qū)動(dòng)程序?qū)S脭?shù)據(jù)
? ? ? ? ? ? ? ? ? ?+ 分配+初始化句柄(vivi_fh)數(shù)據(jù)
? ? ? ? ? ? ? ? ? ?+ 重置幀計(jì)數(shù)器
? ? ? ? ? ? ? ? ? ?+ videobuf_queue_vmalloc_init(); 初始化視頻緩沖隊(duì)列
? ? ? ? ? ? ? ? ? ?+ 開(kāi)啟一個(gè)新線(xiàn)程用于開(kāi)始和暫停
? ? ? ? ?+ 實(shí)現(xiàn)自定義的v4l2_ioctl_ops 函數(shù)

現(xiàn)在開(kāi)始分析程序源碼,利于之后對(duì)V4L2驅(qū)動(dòng)的開(kāi)發(fā),學(xué)習(xí)
首先就行驅(qū)動(dòng)的入口開(kāi)始:

  • 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);
  • 這其實(shí)最重要的就是上面標(biāo)注備份,下面重點(diǎn)分析 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.首先通過(guò)v4l2_device_register() 方法注冊(cè) v4l2_device
    2.ctrl_handler初始化
    3.互斥鎖,自旋鎖等初始化
    4.vb2_quene初始化
    5.init video dma queues
    6. 填充video_device,并且調(diào)用 video_register_device注冊(cè)video_device
    7.把vivi_dev結(jié)構(gòu)set進(jìn)video_device中, 方便之后使用,使用 video_set_drvdata ( vfd , ?dev ) ;
    8.設(shè)備信息加入的鏈表結(jié)構(gòu)

    下面針對(duì)以上步驟做詳細(xì)分析:
    1.首先通過(guò)v4l2_device_register() 方法注冊(cè) 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方法中,進(jìn)行v4l2設(shè)備優(yōu)先級(jí)的初始化,這里把v4l2_device中記錄優(yōu)先級(jí)狀態(tài)的v4l2_prio_state結(jié)構(gòu)變量 prio清空,
    另外這里說(shuō)一下kref結(jié)構(gòu)定義的ref變量,這個(gè)變量保存打開(kāi)設(shè)備的計(jì)數(shù),這里第一次注冊(cè)設(shè)備,初始化ref為1,若之后重復(fù)注冊(cè)則會(huì)先檢查ref這個(gè)變量,
    如果ref為1,則表示已經(jīng)注冊(cè)過(guò)了,避免重復(fù)注冊(cè)v4l2_device,這個(gè)初始化在kref_init方法中實(shí)現(xiàn),這是我的理解, 可是我暫時(shí)還沒(méi)有找到檢查ref的地方,暫且跳過(guò)
    最后一步根據(jù)dev 結(jié)構(gòu)體決定,如果dev不為空,則這里setv4l2_device的name,并且將v4l2_device結(jié)構(gòu)set進(jìn)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這個(gè)結(jié)構(gòu)體到底是干什么用的

  • /**?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方法中,主要通過(guò)nr_of_controls_hint變量的大小,計(jì)算nr_of_buckets,并為buckets申請(qǐng)空間,并將申請(qǐng)結(jié)果保存在error變量中,我感覺(jué)可以是用于以后方便check的
    dev->v4l2_dev.ctrl_handler = hdl;最后,關(guān)聯(lián)vivi_dev
    問(wèn)題點(diǎn):
    1. v4l2_ctrl_new_std
    2. v4l2_ctrl_new_custom
    以上兩個(gè)方法不是很理解,待以后研究

    3.互斥鎖,自旋鎖等 初始化
    這個(gè)比較簡(jiǎn)單,就不在做說(shuō)明了

    4.vb2_quene初始化
    首先還是很有必要看一下這個(gè)結(jié)構(gòu)體

  • /**
  • ?*?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方法中,首先對(duì) vb2_quene其中的重要數(shù)據(jù)進(jìn)行填充,最最重要的就是
    q->ops = &vivi_video_qops;
    q->mem_ops = &vb2_vmalloc_memops;
    這兩條簡(jiǎn)單的復(fù)制語(yǔ)句責(zé)任重大啊, 這里先知道有這么一回事,后面補(bǔ)充

  • /**
  • ?*?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);
  • };
  • 最后調(diào)用 vb2_queue_init 方法,進(jìn)行初始化

  • /**
  • ?*?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);
  • 這里方法很簡(jiǎn)單,只是進(jìn)行check,然后最最簡(jiǎn)單的初始化,這里不再多說(shuō)了

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

    6. 填充video_device,并且調(diào)用 video_register_device注冊(cè)video_device
    這里終于到了重點(diǎn)了,很重要,開(kāi)始了
    開(kāi)始簡(jiǎn)單,申請(qǐng)內(nèi)存空間并進(jìn)行填充,然后才真正調(diào)用用video_register_device這個(gè)方法,開(kāi)始了
    但是在調(diào)用之前的這條語(yǔ)句你必須關(guān)注vfd->v4l2_dev = &dev->v4l2_dev;從這里也可以知道v4l2_device和video_device的注冊(cè)順序

  • 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?*/
  • ? ??//這里還是單獨(dú)說(shuō)一下吧,最終你在/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)
  • ????????? ??//這里說(shuō)明vdev和保存在v4l2_device的dev具有共同的parent,對(duì)之后sys 接口那里有用
  • ????????????vdev->parent?=?vdev->v4l2_dev->dev;
  • ????????if?(vdev->ctrl_handler?==?NULL)
  • ????????? ??//上面初始化的ctrl_handler在這里要派上用處了,而且同時(shí)指向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在這里要派上用處了,而且同時(shí)指向video_device和v4l2_device的prio
  • ????????????vdev->prio?=?&vdev->v4l2_dev->prio;
  • ????}
  • ??????//從這里往下挺長(zhǎng)一段代碼是在為要申請(qǐng)的字符設(shè)備尋找一個(gè)合適的設(shè)備號(hào),這里不去深入追究了,有時(shí)間可以可慮回來(lái)看看
  • ????/*?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);

  • ?????//上面的方法獲取到了那個(gè)合適的設(shè)備號(hào),現(xiàn)在要開(kāi)始注冊(cè)我們的字符設(shè)備了
  • ????/*?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,操作設(shè)備的通道
  • ????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;
  • ????}

  • ?????//這里我們也大可先不用關(guān)注,主要是在sysfs的一些設(shè)備添加等等
  • ????/*?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)
  • ? ? ?//這里其實(shí)還是比較重要的,不過(guò)不是所以的驅(qū)動(dòng)都要添加這一個(gè)步驟,這也是為什么有一個(gè)if?define 的原因了
    ?????//意思就是如果這個(gè)驅(qū)動(dòng)中需要用到media controler的時(shí)候就需要在這里注冊(cè)media_device
    ?????//這里同樣先不做深入研究,media_device和media_entity這兩個(gè)重要結(jié)構(gòu)體之后還要研究
  • ????/*?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
  • ? ??//保存注冊(cè)成功標(biāo)記,并將注冊(cè)成功的video_device保存到全局?jǐn)?shù)組video_device中,大功告成
  • ????/*?Part 6:?Activate this minor.?The char device can?now?be used.?*/
  • ????set_bit(V4L2_FL_REGISTERED,?&vdev->flags);//設(shè)置標(biāo)志位,之后還會(huì)遇到test_bit方法用來(lái)check flags的第nr位是否為1.
  • ? ? //這里還是多說(shuō)一點(diǎn),另外還有兩中標(biāo)志位需要知道: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;
  • ?//這里是出錯(cuò)處理函數(shù)
  • 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結(jié)構(gòu)set進(jìn)video_device中, 方便之后使用, 設(shè)備信息加入的鏈表結(jié)構(gòu)
    最后結(jié)尾的這段代碼這里我決定單獨(dú)放在下面分析,也算妥善收尾吧

  • ????/*
  • ????*?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當(dāng)中

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

  • ????dev->vfd?=?vfd;關(guān)聯(lián)video_device和vivi_dev
  • 短短的幾條語(yǔ)句,但我看來(lái),個(gè)個(gè)都短小精悍
    首先說(shuō)說(shuō)這個(gè)方法,他有什么用處呢?其實(shí)用處可大了,就行我上面注釋的那樣,這里把vivi_dev設(shè)置到vedio_device中,是為了之后字符設(shè)備訪(fǎng)問(wèn)接口中使用
    這里多說(shuō)一點(diǎn),也算順便說(shuō)一下用戶(hù)空間操作設(shè)備的流程了
    首先當(dāng)時(shí)用戶(hù)空間訪(fǎng)問(wèn)設(shè)備了,這個(gè)做驅(qū)動(dòng)的不懂那可糗大了,用戶(hù)空間open時(shí),也就是啟動(dòng)了上面video_device_register方法中的很重要的下面結(jié)構(gòu)中的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,
  • };
  • 我們來(lái)看一下這個(gè)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);//這里是用來(lái)計(jì)數(shù)的
  • ????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;
  • }
  • 只有最下面的那個(gè)標(biāo)準(zhǔn)才是重點(diǎn),經(jīng)過(guò)那么多的check,最后走的了最后這一步, ret? = ?vdev - > fops - > open ( filp ) ;
    這個(gè)open方法在哪里呢?那就沿著箭頭方向找吧,是video_device內(nèi)部的fops中的open方法,這個(gè)方法不是有在哪里呢?
    接著找,在 video_device_register方法之前
    *vfd = vivi_template;/* the most important struct */我還特意在這里寫(xiě)了最重要的結(jié)構(gòu)體
    所以最終調(diào)用的是 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 這個(gè)方法,這個(gè)方法與我們之前寫(xiě)字符驅(qū)動(dòng)時(shí)遇到的情況很是不同,他的open方法其實(shí)是有內(nèi)核API寫(xiě)好的,我們先看看

  • 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);
  • 這個(gè)open方法將v4l2_fh 這個(gè)同樣很重要的結(jié)構(gòu)體保存到filp->private中,并做一些初始化,具體過(guò)程暫且不說(shuō)
    這里fops中的其他接口的實(shí)現(xiàn)比起open,方法是一樣的,而且更簡(jiǎn)單

    但是,這里我真正想表達(dá)的東西還沒(méi)有出現(xiàn),大家看到這里的 filp - > private_data? = ?fh ;//問(wèn)題就在這里了,我們真正在read,write中需要傳遞的想要使用的是vivi_dev其實(shí),
    而不是v4l2_fh這個(gè)數(shù)據(jù)結(jié)構(gòu),我們還是直接看看
    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這個(gè)數(shù)據(jù)結(jié)構(gòu),并傳遞到vb2_read這個(gè)方法中,這個(gè)方法時(shí)驅(qū)動(dòng)中數(shù)據(jù)傳輸?shù)年P(guān)鍵,之后再慢慢研究,這里同樣不深究
    而這個(gè)vivi_dev是通過(guò)video_drvdata方法獲得的,這就是為什么在上面我強(qiáng)調(diào)的要用
    video_set_drvdata ( vfd , ?dev ) ; 把vivi_dev裝載到video_device中

    到這里基本的過(guò)程都整理完了,其實(shí)大頭還在后面呢,待續(xù)。。。。。。

    轉(zhuǎn)載于:https://www.cnblogs.com/jiangu66/archive/2013/04/23/3037421.html

    總結(jié)

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

    如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。

    久久免费视频这里只有精品 | 日韩av电影免费在线观看 | 手机在线日韩视频 | www.天天操 | 天天天综合网 | 97碰视频| 欧美污在线观看 | 成人性生爱a∨ | 久久久www | 高清av中文字幕 | 99色| 99久久久国产精品美女 | 日日爽 | 日韩欧美精品在线 | 黄色h在线观看 | 成人福利在线观看 | av一级片| 黄色成年片 | 日韩欧美一级二级 | 黄网站色欧美视频 | 91精品视频在线观看免费 | 99精品久久99久久久久 | 国产黄色免费看 | 日韩欧美在线不卡 | 嫩草av在线 | 精品一二三区视频 | 日本在线观看一区二区三区 | 国产午夜影院 | 久免费视频 | 久久国产精品一二三区 | 国产最新视频在线 | 国产成人在线观看免费 | 国产免费一区二区三区网站免费 | 久久成人免费电影 | 国产精品久久久久久电影 | 日韩免费一区二区在线观看 | 日韩电影在线观看一区二区 | 国产黄色在线网站 | av电影在线免费观看 | 中文乱码视频在线观看 | av在线播放快速免费阴 | 国产精品人人做人人爽人人添 | 天天干天天干天天干天天干天天干天天干 | 在线免费观看的av | 亚洲精品动漫久久久久 | 91在线看视频 | 久久国产视屏 | 国产精品欧美精品 | 免费在线观看一区二区三区 | 国产精品一区二区美女视频免费看 | 国产精品久久久久久一区二区 | 天天操综合网站 | 日韩在线高清 | 青青草国产精品视频 | 91精品久久久久久粉嫩 | 成人在线免费观看网站 | 亚洲免费资源 | 天天插天天干天天操 | 成人黄色影片在线 | 日本精品视频一区二区 | 久久99精品久久久久久清纯直播 | 四虎小视频 | 国产又粗又硬又爽视频 | 97在线影视 | 国产精品久久久影视 | 97色在线观看免费视频 | 亚洲成人网在线 | 97精品国产97久久久久久粉红 | 不卡的av电影在线观看 | 欧美福利精品 | 91成人精品 | 精品超碰 | 国产精品高潮在线观看 | 久久久资源网 | 精品视频成人 | 天天综合网在线观看 | 天天操偷偷干 | 又黄又刺激视频 | 999精品网 | 97看片| 中文字幕91视频 | 91视频88av| 日批视频在线观看免费 | 亚洲在线精品视频 | 日韩最新理论电影 | 在线免费国产视频 | 欧美日韩亚洲第一 | 91精品一 | 欧美激情视频久久 | 精品国产视频在线观看 | 午夜精品一区二区三区在线观看 | www亚洲视频 | 日本韩国中文字幕 | 插插插色综合 | 免费av一级电影 | 免费午夜视频在线观看 | 天天综合操 | 波多野结衣电影一区二区三区 | 久久好看免费视频 | 91高清一区 | 中文字幕欲求不满 | 久久久久久久影院 | 九九九热精品免费视频观看网站 | 最新国产在线视频 | 黄色小视频在线观看免费 | 狠狠干天天操 | 91传媒免费在线观看 | 在线免费高清视频 | 日韩中文幕 | 国产四虎影院 | 国产精品一区二区三区久久 | 天天天综合 | 亚洲最新视频在线播放 | 韩国一区二区av | 亚洲经典视频在线观看 | 亚洲欧美日韩精品一区二区 | 国产人成精品一区二区三 | 91麻豆.com| 狠狠干天天操 | 久久国产高清 | 菠萝菠萝蜜在线播放 | 日韩影视在线观看 | 99久久久国产精品美女 | 99久热在线精品视频成人一区 | 美女网站在线免费观看 | 在线观看视频免费大全 | 国产亚洲成人精品 | 久久69精品久久久久久久电影好 | 97av在线视频 | 成人av片在线观看 | 久久久www免费电影网 | 国产在线播放一区二区三区 | 深夜免费福利视频 | 国产亚洲精品成人av久久影院 | 国产精品大片在线观看 | 97免费中文视频在线观看 | 日本一区二区三区免费观看 | 最新真实国产在线视频 | 日本精品久久久久中文字幕5 | 伊人婷婷色 | 国产福利电影网址 | 国产精品 国内视频 | 五月天六月婷 | 美女视频黄频大全免费 | 91污视频在线观看 | 国产精品美乳一区二区免费 | 亚洲国产美女精品久久久久∴ | 国产精品无av码在线观看 | 日本精品视频一区 | 在线看av的网址 | 美女视频黄是免费的 | 国产精品毛片一区视频 | 精品一区电影国产 | 中文字幕资源在线观看 | 国产精品久久久久久久免费大片 | 日韩在线观看精品 | 成人亚洲免费 | 99精品免费久久久久久久久 | 日韩精品一区二区电影 | 国产高清久久久 | 国产小视频在线免费观看 | 伊人色播| 97日日碰人人模人人澡分享吧 | 国产视频亚洲视频 | 日韩精品一区二区三区外面 | 国产精品欧美激情在线观看 | 日韩av在线看 | 18网站在线观看 | 日韩中文在线播放 | 免费看的国产视频网站 | 亚在线播放中文视频 | 中文字幕色播 | 欧美日韩电影在线播放 | 超碰人人av | 婷婷精品国产一区二区三区日韩 | 国产精品欧美久久 | 黄色特一级片 | 日韩av三区 | 天天色天天操天天爽 | 国产在线不卡一区 | 国产精品毛片久久久久久久久久99999999 | 性色大片在线观看 | 超碰97在线看| 欧美日韩不卡一区二区 | 精品国产综合区久久久久久 | 午夜精品电影一区二区在线 | 欧美日韩网站 | 狠狠躁日日躁狂躁夜夜躁 | 草久视频在线 | 亚洲综合少妇 | 日韩激情综合 | 人人爽人人乐 | 国产在线视频一区二区三区 | 国产小视频在线观看 | 精品久久久99 | 国产a免费 | 人人看人人做人人澡 | 欧美综合久久 | 久久成人18免费网站 | 天天操天天射天天舔 | 久草网在线观看 | 69热国产视频 | 天天综合婷婷 | 国产精品video | 2022久久国产露脸精品国产 | 一区二区三区在线看 | 欧美日韩另类视频 | 激情视频免费在线观看 | 亚洲人成人99网站 | 国产精品免费在线播放 | 久久综合中文字幕 | 精品福利在线视频 | 亚洲欧美日韩国产一区二区 | 少妇按摩av| 日日操天天操狠狠操 | 亚洲精品xxxx | 欧美在线一二区 | 人人爱人人做人人爽 | 久久成人高清视频 | 亚洲春色综合另类校园电影 | 国产视频精品久久 | av中文在线观看 | avhd高清在线谜片 | 欧美日韩在线精品一区二区 | 国产精品第一页在线 | 欧美精品色 | 在线观看国产亚洲 | www.av免费观看| 337p日本大胆噜噜噜噜 | 黄色片视频在线观看 | 国产精品99久久久久久大便 | 又色又爽又黄高潮的免费视频 | 色激情在线 | 欧美精品黑人性xxxx | 亚洲久草视频 | 亚洲天堂网在线观看视频 | zzijzzij亚洲成熟少妇 | 成人影片在线播放 | 毛片二区 | 中文字幕在线观看第一页 | 亚洲精品乱码白浆高清久久久久久 | 人人舔人人射 | 国产精品99蜜臀久久不卡二区 | 亚洲在线视频免费 | 97国产一区二区 | 国产麻豆电影在线观看 | 人人看人人做人人澡 | a视频在线观看免费 | 国产精品永久免费在线 | 91禁看片| 国产一级精品绿帽视频 | 天天操天天操天天操天天操天天操 | 国际精品网 | 久久国产女人 | 欧美精品乱码久久久久久 | 日韩一级黄色大片 | 91亚洲精品国偷拍 | av福利在线 | 在线看成人av | 欧美日韩首页 | 国产a级片免费观看 | 最近中文字幕mv免费高清在线 | 色99网| 视频在线观看日韩 | 一色屋精品视频在线观看 | 黄色精品网站 | 欧美性黑人| 九九九九免费视频 | av电影在线免费 | 激情五月色播五月 | 日本特黄特色aaa大片免费 | 大胆欧美gogo免费视频一二区 | 国产韩国精品一区二区三区 | 久久精品看片 | 日韩欧美高清一区二区三区 | 久久只精品99品免费久23小说 | 综合久久网站 | 欧美日韩免费一区 | 免费在线观看国产精品 | 色在线免费观看 | 九九久久电影 | 久久久久久久影院 | 九九色在线 | 在线观看一 | 91完整版在线观看 | 很黄很色很污的网站 | 中文字幕二区在线观看 | 国产精品白虎 | 手机色在线| 久久伊人操 | 欧美大码xxxx | 久久久久久久久久久久av | 成人sm另类专区 | 亚洲一级黄色大片 | www.99久久.com| 成人国产精品 | 日韩网站中文字幕 | 亚洲国产免费看 | 色狠狠久久av五月综合 | 97视频免费播放 | 国产精品久久网站 | 日日成人网 | 超碰97在线看 | 97超碰总站 | 懂色av懂色av粉嫩av分享吧 | 国产视频一二三 | 午夜电影 电影 | 99精品视频在线 | 一区 二区电影免费在线观看 | 国产高清久久久 | 免费一级片在线 | 99爱在线 | 欧美日韩性视频 | 天天操,夜夜操 | 亚州性色| 欧美小视频在线 | 在线观看成人小视频 | 激情欧美一区二区三区 | 国产精品自产拍在线观看桃花 | 2021国产精品 | 青青色影院 | 国产破处视频在线播放 | 日韩电影在线一区二区 | 99看视频在线观看 | 精品久久久久久久久久久久久久久久久久 | 免费精品人在线二线三线 | 国产一级黄色片免费看 | 久久99精品久久久久久久久久久久 | 精品国产乱码久久久久久天美 | 一区二精品 | 一级黄色视屏 | 欧美另类性 | 一区二区三区在线免费观看视频 | 深夜免费福利视频 | 国产中文字幕一区 | 97在线免费观看视频 | 99久视频 | 超级碰碰碰视频 | 婷婷在线不卡 | 国产精品ⅴa有声小说 | 免费av 在线 | 操高跟美女 | 91成版人在线观看入口 | 美女视频黄免费网站 | 日韩欧美国产成人 | 中文字幕在线观看的网站 | 国产精品自产拍在线观看中文 | 中文字幕在线观看网址 | 国产精品久久久久国产精品日日 | 丁香电影小说免费视频观看 | 天天干天天插 | 色国产视频 | 国产色综合天天综合网 | 精品999久久久 | 成人午夜黄色 | 狠狠色免费 | 在线免费观看视频 | 亚洲欧美精品在线 | 日韩欧美视频免费观看 | 激情九九| 中文免费 | 正在播放 久久 | 日韩中文久久 | 亚洲国产高清在线 | 亚洲天天在线日亚洲洲精 | 久久综合九色 | 国产色网站 | 又黄又爽又色无遮挡免费 | 91在线欧美| 91精品视频在线观看免费 | 国产91aaa | 久久国产99 | 99精品视频在线观看视频 | 日韩高清二区 | 色婷婷亚洲 | av短片在线观看 | 国产精品99久久久久人中文网介绍 | 欧美资源在线观看 | 东方av免费在线观看 | 激情五月六月婷婷 | 国产成人黄色 | 亚洲人成人在线 | 欧美日韩性视频在线 | 9色在线视频 | 911久久香蕉国产线看观看 | 精品在线不卡 | 欧美激情xxxx | 久草在线免费看视频 | 成人蜜桃 | 在线免费观看视频一区 | 五月婷婷在线综合 | 91日韩国产 | 久久资源总站 | 日本久久久久久 | 国产精品日韩在线观看 | 天天射一射| 在线观看黄av | 五月婷婷伊人网 | 国产专区视频 | 91看片一区二区三区 | 手机在线日韩视频 | 在线免费观看黄 | 中文字幕在线观 | 81国产精品久久久久久久久久 | 97手机电影网 | 国产手机免费视频 | 日本精品xxxx | 日韩精品欧美精品 | 日韩久久精品 | 四虎国产精品成人免费4hu | 亚洲五月激情 | 久久视频免费在线观看 | 91成人网在线 | 国产中文字幕亚洲 | 丁香 婷婷 激情 | 在线观看91精品国产网站 | 中文字幕av在线 | 国产区av在线| 亚洲国产网站 | 久久国产精品小视频 | 中文字幕精品视频 | 国产精品999久久久 久产久精国产品 | 美女网站色在线观看 | 中文字幕一区二区三区在线观看 | 欧美性生活久久 | 欧美色插 | 不卡的av| 久久久综合色 | 麻豆一区二区 | 免费看的av片 | 久久久影片 | 国产精品成人免费精品自在线观看 | 久久黄页 | 国产午夜精品一区二区三区欧美 | 婷婷国产在线 | 色人久久 | 91精品国产91久久久久久三级 | 婷婷丁香五 | 97电影网手机版 | 国产亚洲成av片在线观看 | 国内外成人在线视频 | 国产高清在线不卡 | 国产黄在线播放 | 精品一区二区三区香蕉蜜桃 | 亚洲日韩欧美视频 | 在线观看中文字幕一区二区 | 欧美有色| 少妇bbb搡bbbb搡bbbb′ | 午夜av大片 | 免费成人在线电影 | 超碰在线97国产 | 中文字幕在线乱 | 97超碰在线久草超碰在线观看 | www.eeuss影院av撸| 欧美激情精品 | 久久久网页 | 日韩欧美视频在线观看免费 | 精品一区二区三区久久久 | 国产一级黄 | 国产999免费视频 | 久久精品精品电影网 | 懂色av懂色av粉嫩av分享吧 | 伊人婷婷 | 91精品啪啪 | 欧美亚洲久久 | 国产手机视频在线观看 | japanesefreesexvideo高潮 | 人人看看人人 | 欧美日韩国产在线观看 | 四月婷婷在线观看 | 在线小视频你懂的 | 91色影院| 99久久国产免费,99久久国产免费大片 | 亚一亚二国产专区 | 人人精久| 欧美日韩在线播放一区 | 国产伦精品一区二区三区免费 | 激情视频综合网 | 久久999精品 | 亚洲成人二区 | 91国内在线 | 四季av综合网站 | 超碰激情在线 | www中文在线| 日日干影院 | 高清有码中文字幕 | 伊人激情综合 | 97激情影院 | 精品国产一区二区三区男人吃奶 | 黄色av三级在线 | 欧美经典久久 | 国产一级视频免费看 | 81精品国产乱码久久久久久 | 999国内精品永久免费视频 | 在线黄色国产电影 | 91精品国产自产在线观看永久 | 日韩理论在线播放 | 天天做日日做天天爽视频免费 | 18久久久| 91九色网址 | 91一区二区三区在线观看 | 久久久黄视频 | 亚州精品天堂中文字幕 | 在线视频app | 精品久久一二三区 | 免费国产黄线在线观看视频 | 久久免费精品国产 | 久久精品这里热有精品 | 亚洲成人精品久久久 | 久久久久久久网 | 欧美不卡视频在线 | 在线成人免费av | 日韩av免费观看网站 | 最新av免费在线 | 黄色免费在线视频 | 99免费看片 | 国产综合久久 | 亚洲一区日韩精品 | 在线视频成人 | 99免费看片 | 亚洲精品综合在线观看 | 欧美一区二区在线刺激视频 | 免费av电影网站 | 欧美日韩网站 | 91九色网站 | 天天草天天摸 | 久久久免费精品国产一区二区 | 久久久久免费精品国产 | 激情xxxx| 国产精品久久久久免费a∨ 欧美一级性生活片 | 国产精品久久99综合免费观看尤物 | 91九色蝌蚪视频 | 成人黄色在线 | 五月婷婷在线观看 | www毛片com| 日本乱码在线 | 九九九电影免费看 | 亚洲 欧洲 国产 日本 综合 | 国产精品永久久久久久久www | 97视频免费在线观看 | 亚洲无毛专区 | 人人爽人人乐 | 国产va在线 | 日韩av专区 | 国产精品成人一区二区三区 | 麻豆一区二区三区视频 | 中文字幕网站 | 欧美一二三区在线播放 | 91丨九色丨国产女 | 一区二区三区手机在线观看 | 久久久免费在线观看 | 69久久99精品久久久久婷婷 | www.色五月 | 看av免费 | 在线91视频 | 91亚洲在线观看 | 日韩精品一区二区三区三炮视频 | 国产精品 美女 | 亚洲日本激情 | 日韩大片在线免费观看 | 91精品在线麻豆 | 成人精品一区二区三区中文字幕 | 精品福利视频在线观看 | 午夜精品99久久免费 | 久久久久国产精品视频 | 免费看日韩片 | 中文字幕日韩有码 | 久久不射网站 | 一区二区三区四区不卡 | 日本成址在线观看 | 在线观看片 | 欧美日韩在线观看一区二区 | 日日日日日 | 天天干夜夜爽 | 久草热视频 | 精品国产诱惑 | av日韩国产 | 一区二区三区精品在线视频 | 精品福利国产 | 免费观看视频的网站 | 成人资源在线观看 | 日韩理论电影在线观看 | 国产小视频免费在线观看 | 精品人妖videos欧美人妖 | 又爽又黄又无遮挡网站动态图 | 成人在线观看资源 | 狠狠色丁香久久综合网 | av中文字幕日韩 | 97超碰人人 | 成人在线视频网 | 欧美日韩另类在线观看 | 伊人久久国产精品 | 久久在线影院 | 韩国av免费在线 | 日韩91av| 香蕉影视 | 欧美 日韩 性| 国产手机精品视频 | 亚洲免费精彩视频 | 亚洲精品国产精品久久99 | 亚洲一区二区观看 | 伊人va| 中文字幕免费高清 | 久久夜夜操 | 国产vs久久| 久久天天躁夜夜躁狠狠85麻豆 | 亚洲精品www久久久久久 | 免费日韩一区二区三区 | 欧美久草网 | 91精品一区二区三区蜜桃 | av在线播放中文字幕 | 五月天激情电影 | 国产精品美女视频网站 | 久久99久久99精品免费看小说 | 成人超碰在线 | 天天操天天操 | 久久av中文字幕片 | 欧美日韩免费一区二区 | 国产精品第10页 | 亚洲精品黄网站 | 国产精品不卡一区 | 99爱在线 | 麻豆久久久久 | 国产精品久久久久久久久久久杏吧 | a视频在线观看 | 国产97视频| 国产欧美日韩一区 | 国产精品高清在线观看 | 国产精品视频资源 | 久久久久久高清 | 午夜久久视频 | 91精品综合在线观看 | 999视频在线播放 | www.人人干 | 狠狠色丁香久久综合网 | 91大神精品视频在线观看 | 手机成人av | 国产精品网红直播 | 色婷婷亚洲 | 高清av在线免费观看 | 夜夜夜夜操 | 久久久亚洲国产精品麻豆综合天堂 | 久久激情小说 | 久久午夜电影院 | 成人啪啪18免费游戏链接 | 天天天干夜夜夜操 | 日本性久久 | 在线免费视 | 91九色国产蝌蚪 | 粉嫩av一区二区三区四区五区 | 亚洲精品高清一区二区三区四区 | 99精品热视频只有精品10 | 青青草国产免费 | av电影免费 | 69精品在线 | 久久午夜网 | 中文资源在线播放 | 99热国产在线观看 | 国产一区二区观看 | 亚洲男男gaygay无套 | 久久久国产一区 | 久久精品国产精品亚洲精品 | 81精品国产乱码久久久久久 | 成人在线一区二区三区 | 三级免费黄 | 成人av观看 | 久久久亚洲网站 | 99久久精品国产免费看不卡 | 99热超碰在线 | 91网页版在线观看 | av三级av| 尤物97国产精品久久精品国产 | 色婷婷伊人| 欧美亚洲一级片 | 天堂久色 | av噜噜噜在线播放 | 国产美女精品视频免费观看 | 精品在线观看视频 | 亚洲精品中文字幕在线 | 91视频免费网站 | 波多野结衣在线播放视频 | 五月天天色 | 全黄网站| 2021国产视频 | 国产精品1024 | 国产精品久久久视频 | 欧美无极色 | 国产美女精品 | 亚洲精品免费视频 | 国产精品专区在线观看 | 午夜视频在线观看一区二区三区 | 午夜男人影院 | 99精品视频99| 91九色porn在线资源 | 狠狠色噜噜狠狠狠合久 | 婷婷在线视频观看 | 97在线观看免费观看 | 91九色在线视频观看 | 黄色免费网站下载 | 欧美巨乳波霸 | 成人在线黄色电影 | 99视频导航| 中文字幕视频一区二区 | 超碰在线9| 91精品免费看 | 韩国一区二区三区在线观看 | av解说在线| 九九欧美视频 | 欧美另类色图 | 91精品国产91久久久久 | 国产亚洲精品久久久久久网站 | 成年人黄色免费网站 | av中文字幕在线免费观看 | 久久免费看a级毛毛片 | 国产视频在线观看一区 | 成人毛片一区二区三区 | 国产精品原创av片国产免费 | 中文字幕欧美日韩va免费视频 | 国产福利一区在线观看 | 欧美激情综合五月色丁香 | 91精品国自产在线观看 | 久久综合九九 | 一区二区三区精品在线 | 中文字幕乱在线伦视频中文字幕乱码在线 | 欧美成人精品在线 | 国产69精品久久99的直播节目 | 日韩免费中文字幕 | 97在线公开视频 | 精品国产1区二区 | 99tvdz@gmail.com | 波多野结衣在线视频免费观看 | 爱爱av网 | 99精品在线视频播放 | 婷婷伊人综合 | 亚洲区视频在线观看 | 97福利| 97看片| av成人在线观看 | 久久久官网 | 天天操狠狠干 | 综合精品久久久 | 久草免费在线视频观看 | 欧美一区二区三区特黄 | 成人av午夜 | 免费看片在线观看 | 国产高清视频色在线www | 国产亚洲精品女人久久久久久 | 999久久国产 | 91精品人成在线观看 | 在线成人一区 | 欧美激情视频久久 | 国内精品国产三级国产aⅴ久 | 国产分类视频 | 亚洲天堂视频在线 | 伊人影院得得 | 日韩精品中文字幕在线观看 | 免费97视频 | 日本中文字幕久久 | 亚州精品成人 | 香蕉视频在线观看免费 | 狠色在线| 亚洲午夜精品福利 | 久草青青在线观看 | 国产精品久久久久免费 | 亚洲午夜精品在线观看 | 亚洲黄色网络 | 欧美最猛性xxxxx亚洲精品 | 欧美精品久久久久久久久久白贞 | 国产精品国产三级国产aⅴ入口 | 婷久久 | 亚洲精品午夜视频 | 99久久夜色精品国产亚洲96 | 狠狠干狠狠操 | 免费av网站在线看 | 亚洲少妇久久 | 在线中文字幕网站 | 77国产精品 | 亚洲乱码国产乱码精品天美传媒 | 亚洲高清在线观看视频 | 中文字幕123区 | 日韩a级黄色 | 日韩免费在线网站 | 99爱国产精品 | 在线中文字幕视频 | 99精品免费 | 91视频在线免费看 | 久久免费视频99 | 麻豆视频在线免费看 | 国外调教视频网站 | 国产精品视频地址 | 成年人免费观看在线视频 | 97超碰超碰久久福利超碰 | 日韩 在线 | 欧美日韩在线看 | 欧美日韩中文字幕在线视频 | a视频在线观看 | 丁香六月在线观看 | 亚洲爽爽网 | 911精品美国片911久久久 | 日韩一区正在播放 | 视频二区在线 | 欧美激情另类文学 | 黄色a大片| 国产黄a三级三级三级三级三级 | av噜噜噜在线播放 | 欧美在线aa | 伊人色综合网 | 麻豆果冻剧传媒在线播放 | 国模视频一区二区三区 | 国产精品永久免费观看 | 在线韩国电影免费观影完整版 | 国产一区二区在线免费播放 | 99精品免费在线 | 91av亚洲 | 天天爽夜夜爽精品视频婷婷 | 国产在线免费观看 | 免费看搞黄视频网站 | 91网址在线观看 | 韩国视频一区二区三区 | 国产精品成人一区二区三区 | 999在线精品 | 欧美日韩精品综合 | 国产在线日韩 | 欧美精品九九99久久 | 国产91精品一区二区麻豆网站 | 黄色大全视频 | www黄在线| 国产精品亚洲视频 | 亚洲国产精品久久久 | 中文字幕av全部资源www中文字幕在线观看 | 中文字幕一区二区三区四区久久 | 久久精品99国产精品亚洲最刺激 | 亚洲美女免费视频 | 中文字幕一区二区三区在线观看 | 在线性视频日韩欧美 | 丁香六月婷 | 国产做aⅴ在线视频播放 | 91久久偷偷做嫩草影院 | 亚洲精品资源在线观看 | 国产69熟 | 久久精品a | 韩日精品在线观看 | 五月天激情视频在线观看 | 久久九九久久九九 | 激情文学丁香 | 午夜久久久久久久 | 亚洲视频资源在线 | 国产网站在线免费观看 | 成人性生交大片免费观看网站 | 国产成人三级在线 | 久久久久久高潮国产精品视 | 欧美日韩国内在线 | 最新午夜 | 亚洲精品国产精品国 | 久久久久久久久久久成人 | 久草在线视频在线观看 | 97超碰在线人人 | 日本中文字幕久久 | 92av视频| 国产婷婷| 狠狠色狠狠色综合日日92 | 草莓视频在线观看免费观看 | 曰本三级在线 | 久久综合电影 | 婷婷中文在线 | 国精产品满18岁在线 | 国产精品久久久久久久久费观看 | 91精品网站在线观看 | 久久午夜电影 | 黄色网址中文字幕 | www.五月天婷婷.com | 欧美a在线免费观看 | 天天射天天爱天天干 | 国产精品18久久久久久久久 | 国产午夜精品久久 | 超碰在线9| 中文字幕成人 | 午夜男人影院 | 免费视频你懂的 | 婷婷激情在线 | 国产精品嫩草影视久久久 | 欧美乱码精品一区 | 国产一级一级国产 | 国产福利在线不卡 | 91精品少妇偷拍99 | 欧美一级免费片 | 狠狠干我 | 欧美综合在线观看 | 国产乱码精品一区二区三区介绍 | 涩涩网站在线观看 | 日本久久综合视频 | 日韩视频中文字幕在线观看 | 日韩欧美在线免费观看 | 亚洲国产高清在线 | 在线亚洲午夜片av大片 | 手机av资源 | 狠狠色丁香久久婷婷综合丁香 | 国产69精品久久app免费版 | 国产亚洲精品久久久久久无几年桃 | 成人a级黄色片 | 日本中文字幕电影在线免费观看 | 久久综合五月天婷婷伊人 | 91久久国产自产拍夜夜嗨 | 日日干天天插 | 中文字幕乱视频 | 在线中文字幕网站 | 久久99久久99精品免视看婷婷 | 久久精品国产亚洲精品 | 中文字幕 欧美性 | 人人爽爽人人 | 美女黄频视频大全 | www国产在线| 色综合天天综合在线视频 | 国产一区二区高清不卡 | 日韩电影在线观看一区二区三区 | 射射色| 亚洲综合成人专区片 | 国产成人a亚洲精品v | 国产精品一区二区三区四 | 日韩免费b | 国产精成人品免费观看 | 夜色资源站国产www在线视频 | 亚洲综合五月天 | 欧美性大战 | 黄av免费在线观看 | 超薄丝袜一二三区 | 国产淫片免费看 | 片网址 | 久久夜av| 午夜黄网 | 色婷婷色| 二区三区视频 | 婷婷五月在线视频 | 久久久久免费看 | 丁香国产视频 | 久久国产综合视频 | 中文字幕a∨在线乱码免费看 | 久久久久久久久久久影视 | 久草手机视频 | 国产视频一区在线免费观看 | 久久精品香蕉 | 国产精品福利一区 | 天天操人| 视频一区在线免费观看 | 国产黄色理论片 | 少妇性色午夜淫片aaaze | 亚洲国产精品va在线看 | 亚洲高清视频在线 | 免费中午字幕无吗 | 精品96久久久久久中文字幕无 | 成人高清av在线 | 亚洲国产成人精品久久 | 黄色午夜| 久草在线中文视频 | 色综合天天综合 | 国产精品美女久久久网av | 国产一二三精品 | 91av观看| 成年人视频在线免费 | 国产综合激情 | 99在线高清视频在线播放 | 日韩高清国产精品 | 在线免费观看麻豆 | 99久久国产免费免费 | 午夜久久久久久久 | 亚洲成人午夜在线 | 日韩精品一区电影 | 国产精品人成电影在线观看 | 精品一二三区视频 | 超级碰碰碰视频 | 久久久久欧美精品999 | 美女精品久久 | 久久精品8 | 欧美va天堂va视频va在线 | 少妇高潮流白浆在线观看 | 美女网站视频免费都是黄 | 国产精品扒开做爽爽的视频 | 视频一区在线播放 | 色五月成人 | 色网站视频 | 欧美性脚交| 欧美va日韩va | 一级特黄aaa大片在线观看 | 婷婷在线免费 | 亚洲在线视频网站 | 色国产精品一区在线观看 | 亚洲黄色成人av | a级片在线播放 | 91黄色视屏| 国产精品美女久久久 | 午夜影视剧场 | 国产黄色片免费在线观看 | 精品视频免费在线 | 日韩欧美视频 | 国产一区二区在线免费视频 | 爱av在线网 | 91亚色在线观看 | 国产一级黄大片 |