日韩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ò),歡迎將生活随笔推薦給好友。

    欧美日韩另类在线 | 91人人干 | 久久免费福利 | 2019精品手机国产品在线 | 五月天亚洲综合小说网 | 日韩中文字幕免费电影 | 国产精品美乳一区二区免费 | 日韩.com| 91九色网站 | 91精品夜夜 | 国产91九色蝌蚪 | 黄色网在线播放 | 少妇bbw撒尿 | 午夜视频免费在线观看 | 1区2区视频| 一区二区网 | 天天操夜夜拍 | 最新中文字幕在线观看视频 | 欧美伦理电影一区二区 | 久久人人爽人人片 | 中文字幕麻豆 | 99久久99热这里只有精品 | 国产精品va在线观看入 | 日韩免费av片| 日本中文字幕在线视频 | 国产精品国产三级国产aⅴ9色 | 伊人色综合网 | 51久久夜色精品国产麻豆 | 免费观看的av网站 | 久草在线这里只有精品 | 中文一区在线 | 特级西西444www高清大视频 | 超碰在线97观看 | 激情网色 | 91精品视频免费观看 | 黄网站色视频 | 黄a在线 | 免费在线国产视频 | 美女久久久久久久久久久 | 国内外成人在线 | 精品久久一二三区 | 天天干天天操天天射 | 91视频链接 | 欧美日韩国产精品一区二区三区 | 国产视频在线观看一区 | 欧美日韩在线电影 | 国产香蕉久久精品综合网 | av电影在线观看完整版一区二区 | 久草观看 | 亚洲区精品视频 | 国产一级免费播放 | 精品久久久久国产 | 天天爽天天爽天天爽 | 国产精品一二三 | 色婷婷激情电影 | 精品一区精品二区高清 | 国产视频欧美视频 | 婷婷在线观看视频 | 美女在线观看av | 亚洲精品一区二区三区四区高清 | 国产精品高潮呻吟久久av无 | 成年人在线免费看视频 | 永久av免费在线观看 | 成人精品久久久 | 少妇视频一区 | 综合激情av| 久久国产精品电影 | 婷婷视频在线 | 久久久午夜电影 | 日韩精品不卡在线 | 国产精品久久久久aaaa | 亚洲精品h | 综合激情网| 综合国产在线观看 | 最近中文字幕高清字幕在线视频 | 中文字幕一区二区三区四区久久 | 最新av在线免费观看 | 欧美va天堂va视频va在线 | 精品99999 | 91在线免费观看国产 | 奇米影视在线99精品 | 欧美日韩精品影院 | 亚洲综合在线视频 | 国内99视频 | 久久久午夜剧场 | 欧美性色19p | 天天干 天天摸 天天操 | 中文欧美字幕免费 | 亚洲精品一区二区三区四区高清 | 国产免费观看久久 | 欧美一级视频免费看 | 一级免费看| 精品久久久久国产免费第一页 | 99热在线精品观看 | 免费美女av | 久久伊人精品天天 | 波多野结衣视频一区 | 九九热在线视频免费观看 | 国产91学生粉嫩喷水 | 亚洲视频免费视频 | 色黄视频免费观看 | 国产不卡在线播放 | 国产一级在线观看视频 | 久久96| 久久久久久久久久久网站 | 婷婷深爱 | 久草免费在线视频观看 | 69亚洲精品 | 99re8这里有精品热视频免费 | 在线 国产 亚洲 欧美 | 91精品国产欧美一区二区成人 | 99视频在线精品免费观看2 | 在线 影视 一区 | 黄色软件视频大全免费下载 | 亚洲在线视频免费 | 亚洲精品国 | 日日干夜夜干 | 久久久久久久久久久久影院 | 亚洲精品乱码久久久久久蜜桃不爽 | 波多野结衣在线视频免费观看 | 啪一啪在线 | 一级免费黄色 | 国产黄色资源 | 亚洲高清在线视频 | 日韩久久一区 | 国产网站色 | 中文字幕最新精品 | 午夜国产福利在线 | 色88久久| 国模吧一区 | 成人a视频片观看免费 | 亚洲成av人影院 | av成人免费在线观看 | 最近字幕在线观看第一季 | 在线观看成人国产 | 国产精品福利在线播放 | 中文字幕av在线电影 | 99在线播放| 国产小视频福利在线 | 色婷婷色 | 成人一级视频在线观看 | 国产免费小视频 | 日韩精品中文字幕有码 | 狠狠久久伊人 | 欧美日韩高清不卡 | 黄网站app在线观看免费视频 | 久久九九视频 | 国产麻豆果冻传媒在线观看 | 黄色三级久久 | 久久人人添人人爽添人人88v | 人人爽人人爽人人片av | 在线免费观看黄色大片 | 四虎成人精品永久免费av九九 | 久久精品91久久久久久再现 | 丁香六月久久综合狠狠色 | 国产视频91在线 | 色婷婷av在线 | 国产午夜精品一区二区三区 | 狠狠干网站 | 日韩av中文在线 | 91九色视频 | 久久综合狠狠狠色97 | 国产 日韩 欧美 在线 | 黄色小说视频在线 | 97在线观看视频 | 欧美日韩裸体免费视频 | 人人看黄色 | 天天性天天草 | 国产高清成人av | 国产一区在线看 | 久久国产剧场电影 | 免费国产一区二区 | 国产自产高清不卡 | 日本中文字幕网 | 亚洲伊人av| 亚洲国产精久久久久久久 | 国产一级大片在线观看 | 免费成人av在线 | 五月婷婷开心中文字幕 | 国产一区视频在线播放 | 亚洲精品乱码久久久久久写真 | 欧美一级专区免费大片 | 99久久综合精品五月天 | 手机看国产毛片 | 大胆欧美gogo免费视频一二区 | 精品国产伦一区二区三区观看方式 | 久草在线中文视频 | 手机看片 | 久久成人视屏 | 天天艹天天爽 | 久久久www成人免费毛片 | 99r在线播放 | 国产精品久久久久永久免费 | 成人精品999 | 中文字幕在线免费看线人 | 成人久久毛片 | 国产免费精彩视频 | 亚洲激情六月 | 国产免码va在线观看免费 | 精品影院一区二区久久久 | 香蕉国产91 | 国产精品理论片在线观看 | 麻豆国产电影 | 亚洲一级特黄 | 成人国产精品免费观看 | 99精品一级欧美片免费播放 | 欧美大香线蕉线伊人久久 | 五月激情六月丁香 | av福利在线免费观看 | 久久免费福利视频 | 女人魂免费观看 | www.看片网站 | 日韩av中文字幕在线 | 丝袜一区在线 | 色综合久久88色综合天天人守婷 | 成 人 黄 色 视频 免费观看 | 久久久久综合视频 | 91最新视频在线观看 | 国产精品久久久久久久久久久免费 | 国产精品一二三 | 中文字幕在线观看免费高清电影 | 国产高清不卡在线 | 久久优 | 97超碰在线资源 | 日韩激情视频 | 曰本三级在线 | 国产精品久久久久久久久久东京 | 国产不卡一二三区 | 欧美日一级片 | 久草在在线视频 | av资源免费在线观看 | 91一区啪爱嗯打偷拍欧美 | 波多野结衣在线观看一区 | 日韩区欠美精品av视频 | 久久久久久久久久久影视 | 在线中文字幕网站 | 亚洲电影黄色 | 国产爽视频| 国产一级视频在线免费观看 | 欧美网址在线观看 | 西西人体4444www高清视频 | 免费观看www7722午夜电影 | 99久久精品无免国产免费 | 在线韩国电影免费观影完整版 | 欧美作爱视频 | 色香天天 | 天天射综合网视频 | 人人爱爱人人 | 一区二区国产精品 | 国产一区二区在线播放视频 | 亚洲www天堂com | 欧美一级片免费在线观看 | 五月综合网 | 欧洲性视频 | 久久99在线视频 | 欧美国产三区 | 欧美日韩中文视频 | www.五月天婷婷.com | 国产精品成人一区二区三区吃奶 | 久草在线看片 | av在线直接看 | 中文字幕日韩精品有码视频 | 婷婷综合成人 | 在线观看免费视频 | 成人小视频在线 | 日日夜夜精品视频天天综合网 | 999毛片| 午夜国产福利在线观看 | 国产精品99久久免费观看 | 国产一二三在线视频 | 国产精品久久久久一区 | 91久久久久久国产精品 | 成 人 黄 色 片 在线播放 | 亚洲精品国偷自产在线91正片 | 五月天av在线 | 99久久精品免费一区 | 欧美一区成人 | 国产精品夜夜夜一区二区三区尤 | 波多野结衣在线观看视频 | 精品国产色 | 日韩有码第一页 | 国产精品午夜免费福利视频 | 日日碰夜夜爽 | 婷婷色站 | 久久人人爽人人爽 | 天天干天天干天天色 | 91精品国产乱码久久桃 | 色欧美日韩 | 日韩理论在线观看 | 欧美日韩性视频在线 | 精品一区二区免费 | 久久综合精品国产一区二区三区 | 婷婷激情小说网 | av天天色| 亚洲精品av中文字幕在线在线 | 国内精品久久久久久久97牛牛 | av日韩不卡 | 中文字幕免费高清av | 国产精品久久久久9999吃药 | 91视频久久| 欧美成年人在线视频 | 色视频在线 | 在线黄频| 在线免费观看麻豆视频 | 在线免费三级 | 日本动漫做毛片一区二区 | 毛片3| 丁香高清视频在线看看 | 久久久久综合视频 | 超碰人人草人人 | 日本最新中文字幕 | 安徽妇搡bbbb搡bbbb | 蜜桃视频在线视频 | 国产日韩高清在线 | 99视频偷窥在线精品国自产拍 | 国产成人综合图片 | 色综合久久久久综合99 | 999久久久 | 成人午夜黄色影院 | 视频在线观看入口黄最新永久免费国产 | 日韩黄在线观看 | 最近免费观看的电影完整版 | 久久综合一本 | 亚洲欧美日本国产 | 色综合久久五月天 | 欧美性色19p | 亚洲狠狠操 | 免费观看一级成人毛片 | www.黄色在线 | 精品国产一区二区三区久久久久久 | 国产精品成人自产拍在线观看 | 91av电影在线 | 日韩大片免费在线观看 | 国产99在线 | 国产精品第一页在线观看 | av资源免费在线观看 | 免费视频在线观看网站 | 成人av网址大全 | 亚洲老妇xxxxxx| av中文字幕第一页 | 欧美一区二区精美视频 | av3级在线 | 免费福利视频网站 | 婷婷六月在线 | 日韩高清国产精品 | 西西444www大胆高清图片 | 久久字幕精品一区 | av国产网站 | av免费看在线 | av福利在线 | 特级西西444www大精品视频免费看 | 国产精品99久久久久久宅男 | 免费欧美高清视频 | 免费网站在线观看人 | 国产精品嫩草影视久久久 | 国产精品成人自产拍在线观看 | 超碰97人人干| 毛片网站观看 | 人人插人人草 | 91久久久久久久一区二区 | 久久精品看 | 久草在线手机视频 | 国产一二区视频 | 五月婷婷综合激情 | 日韩区在线观看 | 国产精品久久久久久久久软件 | 久久伊人精品一区二区三区 | 日韩中文字幕网站 | 免费黄在线看 | 欧美韩日精品 | 狠狠色丁香婷婷综合最新地址 | 中文字幕在线观看资源 | 久久69精品久久久久久久电影好 | 久草在线免费看视频 | 免费a级大片 | 中文字幕色在线视频 | 高清一区二区 | 少妇高潮流白浆在线观看 | 婷婷色六月天 | 日韩精品一区二区三区视频播放 | 精品久久国产精品 | 国产精品免费久久久 | 国产日韩视频在线观看 | 精精国产xxxx视频在线播放 | www.久久婷婷 | 日韩精品在线播放 | 亚洲永久精品在线 | 欧美日韩一区二区在线 | 欧美成人高清 | 在线免费高清一区二区三区 | 欧美日韩一级视频 | 色综合久久88色综合天天人守婷 | 国产一区欧美在线 | 日韩免费观看视频 | 999久久久国产精品 高清av免费观看 | 在线观看色视频 | 日韩成人免费观看 | 亚洲婷婷在线视频 | 91精品一区国产高清在线gif | 国产成人高清av | 国产精品美女www爽爽爽视频 | 五月婷婷另类国产 | 天天天色综合a | 四虎国产精品免费观看视频优播 | 色播五月激情综合网 | 亚洲一区欧美激情 | 中文字幕成人在线 | 国产精品一区二区免费视频 | 91精选在线观看 | 高清av免费一区中文字幕 | 亚洲国产资源 | 9色在线视频 | 777奇米四色| 久日视频| 深爱激情综合 | 91福利视频网站 | 久久精品999 | 伊人色综合久久天天网 | 97国产精品 | 国产精品久久久久久久久久久久午 | 激情综合啪 | 欧美在线观看禁18 | 午夜三级影院 | 91爱爱电影 | 97超级碰碰碰碰久久久久 | 国产在线91在线电影 | 精品免费久久久久 | 亚洲美女视频在线 | 成人三级视频 | 久久视频中文字幕 | 热re99久久精品国产99热 | 亚洲国产精品久久久久婷婷884 | 国产精品99免视看9 国产精品毛片一区视频 | 亚洲精品视频在线观看免费视频 | 日韩理论在线观看 | 欧美日本不卡高清 | 久久久久国产a免费观看rela | 国内精品久久久久久久 | 一区二区三区四区精品 | 欧美色久| 7799av | 免费av看片 | 成人一级视频在线观看 | 丁香婷婷基地 | 国产精品成人在线观看 | 久精品视频| av播放在线 | 日日躁你夜夜躁你av蜜 | 亚洲精品国产日韩 | 亚洲最新在线 | 麻豆免费在线视频 | 超碰人人av | 欧美日产一区 | 久久久受www免费人成 | 五月婷婷开心中文字幕 | 国产精品久久久久久久久久久久 | 久久久久久久av | 五月天综合 | 国产精品高潮呻吟久久久久 | 久射网| 天天综合亚洲 | 国产手机视频 | 99精品欧美一区二区三区 | 在线亚洲日本 | 久久人人爽人人片av | 国产精品黄色 | 国产成人在线观看 | 亚洲午夜av电影 | 手机成人av | 亚洲视屏 | 五月丁香 | 久久精品免费电影 | 国产在线播放观看 | 不卡av电影在线 | 国产视频在线观看一区二区 | 午夜精品区 | 黄色的视频 | 久久亚洲视频 | 免费在线观看一区 | 日本视频精品 | 日韩av视屏 | 亚洲视频电影在线 | 福利片免费看 | 黄色三级网站在线观看 | 久久免费视频一区 | 一区精品在线 | 色噜噜在线观看 | 亚洲欧洲一区二区在线观看 | 精品视频成人 | 午夜三级影院 | 日本中文字幕在线播放 | 久精品一区 | 久久综合免费视频 | 亚洲精品女人 | 亚洲综合视频在线播放 | 久久久精品国产一区二区电影四季 | 精品国产欧美 | 久久都是精品 | 亚洲黄色一级大片 | 国产黄色资源 | 中文字幕在线乱 | 欧美日韩免费观看一区二区三区 | 五月婷婷中文网 | 在线观看av免费观看 | 欧美在一区| a色网站 | 视频一区二区三区视频 | 亚洲天堂首页 | 天天综合网天天综合色 | 91成人在线网站 | 综合色狠狠 | 成人av久久 | 激情大尺度视频 | 亚洲精品乱码白浆高清久久久久久 | 亚洲伊人色 | 日韩av免费观看网站 | 国产日韩欧美在线观看视频 | 夜夜操网 | 日本性xxxxx 亚洲精品午夜久久久 | 久久8| av电影亚洲| 中文字幕 国产视频 | 色婷婷久久久综合中文字幕 | 国产99久久精品一区二区300 | 婷婷 中文字幕 | 日韩视频免费在线观看 | 成人av av在线| 免费观看十分钟 | 日韩电影精品 | 欧美坐爱视频 | 欧洲亚洲女同hd | 伊人五月天.com | 精品国产不卡 | 日韩久久网站 | 天天噜天天色 | 亚洲爱视频 | 欧美日韩网址 | 亚洲精品乱码久久久久久蜜桃欧美 | 久久久久免费网 | 国产91精品高清一区二区三区 | 婷婷色六月天 | 久草在线费播放视频 | 国际av在线 | 一区二区三区在线观看中文字幕 | 一区二区三区福利 | 狠狠色丁香婷婷综合久小说久 | 二区三区在线观看 | 日韩中文字幕网站 | 91精品影视 | 免费在线色电影 | 久草精品视频在线播放 | 日韩欧美区 | 中文字幕日韩精品有码视频 | 超碰av在线播放 | 国产精品资源在线 | 午夜久久久久久久久久久 | 国产激情小视频在线观看 | 欧美日本啪啪无遮挡网站 | 国产午夜三级一区二区三桃花影视 | 亚洲欧美成人在线 | 久久人人爽人人爽人人 | 一区二区三区日韩精品 | 免费a v在线 | 国产精品无 | 毛片在线网 | 成人网在线免费视频 | 久久精品亚洲综合专区 | 日韩电影在线观看一区二区 | 99热 精品在线 | 伊人色播 | 国产精品第三页 | 国产成人久 | 人人看人人 | 西西人体4444www高清视频 | 91传媒激情理伦片 | 欧美人牲 | 精品国产成人在线 | 亚洲午夜久久久久久久久 | 亚州精品在线视频 | 国产精品a成v人在线播放 | 91视频91蝌蚪 | 99热精品免费观看 | 国模精品在线 | 999视频在线观看 | 欧美伦理电影一区二区 | 亚洲国产成人在线播放 | 五月亚洲婷婷 | 视频国产 | 热re99久久精品国产66热 | 国产美女搞久久 | 字幕网av| 懂色av一区二区在线播放 | 亚洲免费公开视频 | 日韩在线高清 | 在线日韩亚洲 | 日韩试看| 97碰碰精品嫩模在线播放 | 91超碰在线播放 | 中文av字幕在线观看 | 天天干,天天草 | 国产精品久久久久一区二区 | a黄色片| 日韩精品一区二区电影 | 亚洲精品乱码久久久久久蜜桃欧美 | 444av| 在线观看av小说 | 免费国产亚洲视频 | a在线观看免费视频 | 免费瑟瑟网站 | 成人午夜毛片 | 亚洲午夜av电影 | 久久久免费视频播放 | 国产一区二区高清视频 | 美女黄频在线观看 | 国产区 在线 | 日韩中文字幕免费看 | 四虎免费在线观看视频 | 国产精品久久毛片 | 成人免费视频网 | 亚洲成人免费观看 | av女优中文字幕在线观看 | 久久综合九色综合97婷婷女人 | 日韩在线中文字幕视频 | 久久99国产精品久久99 | 狠狠干中文字幕 | 久久久久免费精品国产小说色大师 | 久久精品视频中文字幕 | 久久国产日韩 | 9幺看片 | 制服丝袜在线91 | av片无限看 | 最新在线你懂的 | a级国产乱理论片在线观看 伊人宗合网 | 欧美日韩在线免费观看视频 | 日本黄色免费在线观看 | 一区二区不卡视频在线观看 | 夜色成人av | 男女日麻批 | 亚州精品天堂中文字幕 | av直接看| 天天射天天色天天干 | 99免费在线视频 | 国产一区二区三区四区在线 | 精品麻豆入口免费 | 欧美日韩高清国产 | 97在线超碰| 色综合 久久精品 | 亚洲国产操 | 日韩欧美在线视频一区二区三区 | 五月婷婷六月综合 | 日韩欧美精品在线 | 国产精品久久久久久久久久直播 | 久久超级碰视频 | www五月天婷婷 | 在线观看91视频 | 免费网站观看www在线观看 | 欧美日韩国产页 | 激情中文字幕 | 国产在线视频不卡 | 日本黄区免费视频观看 | 成人久久18免费网站 | 久久99久久久久久 | 久久久观看| avwww在线| 色99色 | 91丨九色丨国产丨porny精品 | 日韩欧美在线综合网 | 婷婷久久一区 | 草久草久 | 精品主播网红福利资源观看 | 亚洲播播 | 349k.cc看片app | 99婷婷狠狠成为人免费视频 | av综合 日韩 | 91看片淫黄大片在线播放 | 亚洲免费资源 | 四虎永久免费网站 | 免费网站黄 | 天天综合网~永久入口 | 国产成人一区二区三区在线观看 | 最新av网址大全 | 精品视频久久 | 久久久美女 | 国产精品久久久久久一区二区 | 免费在线观看一区二区三区 | 日韩中文字幕a | 国产成人免费在线观看 | 久久久久久久影视 | 免费a现在观看 | 色综合久久久 | 久久激情视频 久久 | 国产成人一区二区三区在线观看 | 国产精品久久久久一区二区国产 | 欧美韩国日本在线观看 | 久色 网 | 久久一区二区免费视频 | 成人黄色电影在线 | 免费精品视频在线观看 | av一级片在线观看 | 91在线公开视频 | 欧美日本啪啪无遮挡网站 | 欧美一区二区三区在线看 | 国产区精品区 | 婷婷5月色 | 在线免费观看视频你懂的 | 黄色大片网 | 91麻豆网站 | 狠狠搞,com | 免费在线观看成年人视频 | 欧美日韩不卡一区二区 | 国产人免费人成免费视频 | 91精品久久香蕉国产线看观看 | 久久久免费国产 | 深夜视频久久 | av日韩国产 | 国产精品不卡 | 婷婷亚洲最大 | 在线观看色视频 | 91精品视频免费在线观看 | 一级黄网 | 亚洲人成人在线 | 久久99视频免费观看 | 九九热免费视频在线观看 | 国产精品一区二区久久精品 | 国产色婷婷 | 91丨精品丨蝌蚪丨白丝jk | 精品一区欧美 | 美女视频国产 | 欧美性黄网官网 | 日韩mv欧美mv国产精品 | 97天天综合网 | 亚洲欧洲精品一区二区精品久久久 | 91精品1区2区 | 日韩精品一区二区三区视频播放 | 久久久久亚洲精品中文字幕 | 国产精品久久久网站 | 日韩深夜在线观看 | 欧美日韩国产综合网 | 免费观看午夜视频 | 日韩一二三 | 色狠狠干| 国产你懂的在线 | 丁香婷婷社区 | 在线观看国产高清视频 | 日韩欧美99 | 欧美日韩高清 | 国产日韩欧美在线观看视频 | 久久优| 91九色精品 | 精品国产一区二区三区av性色 | 91一区啪爱嗯打偷拍欧美 | 日韩激情在线视频 | 欧美另类xxxx | 97在线视频免费播放 | 亚洲观看黄色网 | 中文字幕色在线视频 | 成年人在线免费看 | 日韩视频1 | 香蕉视频在线视频 | 美女视频网 | 91精品婷婷国产综合久久蝌蚪 | 亚洲 欧美 91| 欧美日韩中文字幕视频 | 久久久久五月天 | 国产国语在线 | 色婷婷久久久综合中文字幕 | 久久尤物电影视频在线观看 | 精品久久久久久久久久岛国gif | 91在线视频观看 | 欧美激情视频一区二区三区免费 | 欧美日韩a视频 | 天天干国产 | 久久久资源 | 中文字幕在线视频第一页 | 国产午夜精品久久久久久久久久 | 国产精品青草综合久久久久99 | 中文字幕中文字幕 | 国产永久免费高清在线观看视频 | 香蕉久久久久久av成人 | 中文字幕在线日 | 久久精品中文视频 | 叶爱av在线 | 亚洲最新在线视频 | 九九视频在线播放 | 久久精品一区二区国产 | 涩涩网站在线看 | 国产免费成人 | 色综合久久综合 | 91精品中文字幕 | 一区二区三区四区五区在线视频 | 在线网站黄 | 日p在线观看| 黄色软件在线观看视频 | 97视频在线观看网址 | 东方av免费在线观看 | 成 人 黄 色 片 在线播放 | 国产精品久久久久久久久久久久午夜 | 婷婷国产视频 | 国内精品一区二区 | 久青草国产在线 | 九九视频在线播放 | 久久精品免费电影 | 免费在线观看视频一区 | 亚洲日本欧美 | av电影不卡在线 | 超碰最新网址 | 性色av免费看 | 91网在线 | 探花视频在线版播放免费观看 | 日韩精品一区二区不卡 | 9999国产精品| 天天干,天天射,天天操,天天摸 | 午夜精品久久久99热福利 | 免费视频资源 | 中文字幕在线免费看 | 九九色网| 日韩欧美一区视频 | 日韩成人黄色 | 国产一级做a爱片久久毛片a | 日本高清中文字幕有码在线 | 亚洲精品中文在线资源 | 欧美日韩国产精品一区二区三区 | 亚洲精品看片 | 麻豆系列在线观看 | a视频在线播放 | 久精品视频免费观看2 | 色五月激情五月 | 狠狠狠狠干 | www.狠狠插.com| 中文字幕一区二区三区久久蜜桃 | 日本视频久久久 | 在线免费观看成人 | 四虎影视8848dvd | www国产亚洲精品久久网站 | 高清中文字幕av | 天堂成人在线 | 国产精品视频地址 | 日本在线精品视频 | 亚洲国产精品成人va在线观看 | 精品久久一区二区三区 | 国内揄拍国产精品 | 婷婷午夜 | 福利一区二区 | 1000部18岁以下禁看视频 | 亚洲精品玖玖玖av在线看 | 麻豆系列在线观看 | 91av在线不卡 | 日韩精品久久久久 | 日韩中文字幕一区 | 中文字幕在线日亚洲9 | 在线韩国电影免费观影完整版 | 国产精品高潮在线观看 | 婷婷丁香社区 | 久久精品网址 | 一区二区av | 99re中文字幕 | 成人手机在线视频 | 久久区二区 | 欧美黑人性爽 | 色停停五月天 | 国产香蕉在线 | 国产精品毛片久久久久久久久久99999999 | 中文字幕丝袜 | www.久久久.cum | 国产视频在线观看免费 | 天天操网站 | 超碰最新网址 | 最近最新mv字幕免费观看 | 精品 一区 在线 | 久草视频在线资源站 | 99热这里精品 | 手机在线看片日韩 | 2024国产精品视频 | 精品国产一区二区三区免费 | 天天天干天天射天天天操 | 五月婷婷久久综合 | 国产一区在线观看视频 | 亚洲一区久久 | 日韩区视频 | 五月婷婷一区 | 正在播放 国产精品 | 国产精品欧美一区二区三区不卡 | 在线国产中文 | 国产一区二区三精品久久久无广告 | 91人人澡人人爽人人精品 | 香蕉在线播放 | 国产精品a成v人在线播放 | 国产精品入口66mio女同 | 久草网站在线观看 | 成人a视频片观看免费 | 国产不卡在线观看 | 在线a人片免费观看视频 | 婷色| 视频在线观看入口黄最新永久免费国产 | 一区二区三区日韩在线 | 日韩黄色在线观看 | www.久久色| 女人魂免费观看 | 国内一级片在线观看 | 国产精品久久久久久久妇 | 最近中文字幕大全 | 日本久久免费视频 | 国产高清免费 | 日日弄天天弄美女bbbb | 91探花视频 | 久久激情五月激情 | 日日日视频 | 五月天久久久 | 久久精品视频在线观看免费 | 天天色棕合合合合合合 | 欧美一区二区三区特黄 | 91九色性视频 | 麻豆传媒在线免费看 | 成人毛片久久 | 欧美日本在线视频 | 国产精品午夜久久 | 国产精品高清免费在线观看 | 久久精品这里热有精品 | 日韩高清激情 | 天天综合久久 | 日韩精品中文字幕久久臀 | 成人激情开心网 | 久久不见久久见免费影院 | 激情开心站 | 国产精品 久久 | 啪啪动态视频 | 97成人在线观看视频 | 日韩精品一区二区免费 | av黄色在线观看 | 成年人在线观看免费视频 | 992tv人人网tv亚洲精品 | www.777奇米 | 网站在线观看你们懂的 | 欧美色婷 | 91亚洲精品国偷拍 | 天天综合色网 | 91麻豆精品国产自产在线游戏 | 欧美精品一区二区在线播放 | 日韩av男人的天堂 | 日韩有码网站 | 欧洲亚洲国产视频 | 人人爽人人爽人人爽学生一级 | 日韩av手机在线观看 | 久久久久久久久久影视 | 久久99日韩 | 国产 成人 久久 | 成人一区二区在线观看 | 国产999精品久久久久久麻豆 | 日韩在线视频看看 | 玖玖在线看 | av免费在线播放 | 日韩有码第一页 | 九九九九热精品免费视频点播观看 | av久久在线 | 日本爱爱免费 | 美女免费视频网站 | 亚洲精品国产精品乱码在线观看 | av综合 日韩| 四虎成人免费观看 | 国产一区二区精 | 国产又粗又猛又黄又爽的视频 | 91精品久久久久久综合乱菊 | 九九在线精品视频 | 91久久国产综合精品女同国语 | 天天综合日| 国产一级视频在线免费观看 | 久草在线免费看视频 | 天堂av在线网址 | 99热最新精品 | 91免费版成人| 中文字幕精品www乱入免费视频 | 丁香综合网 | 久久人人97超碰com | 久草在线一免费新视频 | 色先锋av资源中文字幕 | 国产成人三级一区二区在线观看一 | 中文字幕乱码在线播放 | 操高跟美女| 九草在线观看 | 婷婷干五月 | 视频在线观看99 | 久久久精品福利视频 | japanese黑人亚洲人4k | 最新成人在线 | 97超碰人人澡人人爱学生 | 亚洲精品美女免费 | 精品国产视频在线 | 福利二区视频 | 亚洲一区 影院 | 日韩精品国产一区 | 国产美女精彩久久 | 成年人国产精品 | 欧美韩日精品 | 午夜影院一级 | 欧美成人h版在线观看 | 成人精品亚洲 | 国产一级黄色电影 | 97免费中文视频在线观看 |