V4L2(二)虛擬攝像頭驅動vivi深入分析


轉載於: http://blog.csdn.net/lizuobin2/article/details/53006927

 

本文基於:linux3.5

  前面一篇文章中,簡單分析了 V4L2 大框架,本文借助內核中的虛擬攝像頭驅動 vivi 來分析一個完整的攝像頭驅動程序。vivi 相對於后面要分析的 usb 攝像頭驅動程序,它沒有真正的硬件相關層的操作,也就是說拋開了復雜的 usb 層的相關知識,便於理解 V4L2 驅動框架,側重於驅動和應用的交互。

  前面我們提到,V4L2 的核心是 v4l2-dev.c 它向上提供統一的文件操作接口 v4l2_fops ,向下提供 video_device 注冊接口 register_video_device ,作為一個具體的驅動,需要做的工作就是分配、設置、注冊一個 video_device.框架很簡單,復雜的是視頻設備相關眾多的 ioctl。

一、vivi 框架分析

[cpp] view plain copy
 
 print?在CODE上查看代碼片派生到我的代碼片
  1. static int __init vivi_init(void)  
  2. {  
  3.     ret = vivi_create_instance(i);  
  4.     ...  
  5.     return ret;  
  6. }  
  7. module_init(vivi_init);  

  vivi 分配了一個 video_device 指針,沒有去設置而是直接讓它指向了一個現成的 video_device 結構 vivi_template ,那么全部的工作都將圍繞 vivi_template 展開。

[cpp] view plain copy
 
 print?在CODE上查看代碼片派生到我的代碼片
  1. static int __init vivi_create_instance(int inst)  
  2. {  
  3.     struct vivi_dev *dev;  
  4.     struct video_device *vfd;  
  5.     struct v4l2_ctrl_handler *hdl;  
  6.     struct vb2_queue *q;  
  7.   
  8.     // 分配一個 vivi_dev 結構體  
  9.     dev = kzalloc(sizeof(*dev), GFP_KERNEL);  
  10.       
  11.     // v4l2_dev 初始化,並沒有什么作用  
  12.     ret = v4l2_device_register(NULL, &dev->v4l2_dev);  
  13.       
  14.     // 設置 dev 的一些參數,比如圖像格式、大小  
  15.     dev->fmt = &formats[0];  
  16.     dev->width = 640;  
  17.     dev->height = 480;  
  18.     dev->pixelsize = dev->fmt->depth / 8;  
  19.     ...  
  20.       
  21.     // vivi_dev->vb_vidq(vb2_queue) 初始化  
  22.     q = &dev->vb_vidq;  
  23.     memset(q, 0, sizeof(dev->vb_vidq));  
  24.     q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;  
  25.     q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF | VB2_READ;  
  26.     q->drv_priv = dev;  
  27.     q->buf_struct_size = sizeof(struct vivi_buffer);  
  28.       
  29.     // vivi_dev->vb_vidq(vb2_queue)->ops  
  30.     q->ops     = &vivi_video_qops;  
  31.       
  32.     // vivi_dev->vb_vidq(vb2_queue)->mem_ops  
  33.     q->mem_ops = &vb2_vmalloc_memops;  
  34.       
  35.     // 初始化一些鎖之類的東西  
  36.     vb2_queue_init(q);  
  37.   
  38.     /* init video dma queues */  
  39.     INIT_LIST_HEAD(&dev->vidq.active);  
  40.     init_waitqueue_head(&dev->vidq.wq);  
  41.       
  42.     // 分配一個 video_device ,這才是重點  
  43.     vfd = video_device_alloc();  
  44.   
  45.     *vfd = vivi_template;  
  46.     vfd->debug = debug;  
  47.     vfd->v4l2_dev = &dev->v4l2_dev;  
  48.     set_bit(V4L2_FL_USE_FH_PRIO, &vfd->flags);  
  49.   
  50.     vfd->lock = &dev->mutex;  
  51.       
  52.     // 注冊 video_device !!!  
  53.     ret = video_register_device(vfd, VFL_TYPE_GRABBER, video_nr);  
  54.     // 把 vivi_dev 放入 video_device->dev->p->driver_data ,這個后邊經常用到  
  55.     video_set_drvdata(vfd, dev);  
  56.   
  57.     /* Now that everything is fine, let's add it to device list */  
  58.     list_add_tail(&dev->vivi_devlist, &vivi_devlist);  
  59.   
  60.     if (video_nr != -1)  
  61.         video_nr++;  
  62.     // vivi_dev->vfd(video_device) =  vfd  
  63.     dev->vfd = vfd;  
  64.     v4l2_info(&dev->v4l2_dev, "V4L2 device registered as %s\n",  
  65.           video_device_node_name(vfd));  
  66.     return 0;  
  67. }  

  用戶空間調用的是 v4l2_fops ,但是最終會調用到 vivi_fops ,vivi_fops 中的 ioctl 調用 video_ioctl2

[cpp] view plain copy
 
 print?在CODE上查看代碼片派生到我的代碼片
  1. static struct video_device vivi_template = {  
  2.     .name       = "vivi",  
  3.     .fops           = &vivi_fops,  
  4.     .ioctl_ops  = &vivi_ioctl_ops,  
  5.     .minor      = -1,  
  6.     .release    = video_device_release,  
  7.   
  8.     .tvnorms              = V4L2_STD_525_60,  
  9.     .current_norm         = V4L2_STD_NTSC_M,  
  10. };  

  video_register_device 過程就不詳細分析了,前面的文章中分析過,大概就是向核心層注冊 video_device 結構體,核心層注冊字符設備並提供一個統一的 fops ,當用戶空間 read write ioctl 等,最終還是會跳轉到 video_device->fops ,還有一點就是核心層會把我們注冊進來的 video_device 結構放入一個全局的 video_device數組。

[cpp] view plain copy
 
 print?在CODE上查看代碼片派生到我的代碼片
  1. static const struct v4l2_file_operations vivi_fops = {  
  2.     .owner      = THIS_MODULE,  
  3.     .open           = v4l2_fh_open,  
  4.     .release        = vivi_close,  
  5.     .read           = vivi_read,  
  6.     .poll       = vivi_poll,  
  7.     .unlocked_ioctl = video_ioctl2, /* V4L2 ioctl handler */  
  8.     .mmap           = vivi_mmap,  
  9. };  

  這里,先看一下 v4l2_fh_open 函數

[cpp] view plain copy
 
 print?在CODE上查看代碼片派生到我的代碼片
  1. int v4l2_fh_open(struct file *filp)  
  2. {  
  3.   // 前面注冊時,我們將 video_device 結構體放入了全局數組 video_device ,現在通過     video_devdata 函數取出來,后面經常用到這種做法  
  4.     struct video_device *vdev = video_devdata(filp);  
  5.     // 分配一個 v4l2_fh 結構,放入file->private_data 中  
  6.     struct v4l2_fh *fh = kzalloc(sizeof(*fh), GFP_KERNEL);  
  7.     filp->private_data = fh;  
  8.     if (fh == NULL)  
  9.         return -ENOMEM;  
  10.     v4l2_fh_init(fh, vdev);  
  11.     v4l2_fh_add(fh);  
  12.     return 0;  
  13. }  

  1、我們隨時可以通過 video_devdata 取出我們注冊的 video_device 結構進行操作

  2、我們隨時可以通過 file->private_data 取出 v4l2_fh 結構,雖然現在還不知道它有啥用

  下面來分析 ioctl ...首先來看一下調用過程 

[cpp] view plain copy
 
 print?在CODE上查看代碼片派生到我的代碼片
  1. long video_ioctl2(struct file *file,  
  2.            unsigned int cmd, unsigned long arg)  
  3. {  
  4.     return video_usercopy(file, cmd, arg, __video_do_ioctl);  
  5. }  
[cpp] view plain copy
 
 print?在CODE上查看代碼片派生到我的代碼片
  1. static long __video_do_ioctl(struct file *file,  
  2.         unsigned int cmd, void *arg)  
  3. {  
  4.     struct video_device *vfd = video_devdata(file);  
  5.     const struct v4l2_ioctl_ops *ops = vfd->ioctl_ops;  
  6.     void *fh = file->private_data;  
  7.     struct v4l2_fh *vfh = NULL;  
  8.     int use_fh_prio = 0;  
  9.     long ret = -ENOTTY;  
  10.   
  11.     if (ops == NULL) {  
  12.         printk(KERN_WARNING "videodev: \"%s\" has no ioctl_ops.\n",  
  13.                 vfd->name);  
  14.         return ret;  
  15.     }  
  16.   
  17.     if (test_bit(V4L2_FL_USES_V4L2_FH, &vfd->flags)) {  
  18.         vfh = file->private_data;  
  19.         use_fh_prio = test_bit(V4L2_FL_USE_FH_PRIO, &vfd->flags);  
  20.     }  
  21.   
  22.     if (v4l2_is_known_ioctl(cmd)) {  
  23.         struct v4l2_ioctl_info *info = &v4l2_ioctls[_IOC_NR(cmd)];  
  24.   
  25.             if (!test_bit(_IOC_NR(cmd), vfd->valid_ioctls) &&  
  26.             !((info->flags & INFO_FL_CTRL) && vfh && vfh->ctrl_handler))  
  27.             return -ENOTTY;  
  28.   
  29.         if (use_fh_prio && (info->flags & INFO_FL_PRIO)) {  
  30.             ret = v4l2_prio_check(vfd->prio, vfh->prio);  
  31.             if (ret)  
  32.                 return ret;  
  33.         }  
  34.     }  
  35.   
  36.     if ((vfd->debug & V4L2_DEBUG_IOCTL) &&  
  37.                 !(vfd->debug & V4L2_DEBUG_IOCTL_ARG)) {  
  38.         v4l_print_ioctl(vfd->name, cmd);  
  39.         printk(KERN_CONT "\n");  
  40.     }  
  41.   
  42.     switch (cmd) {  
  43.   
  44.     /* --- capabilities ------------------------------------------ */  
  45.     case VIDIOC_QUERYCAP:  
  46.     {  
  47.         struct v4l2_capability *cap = (struct v4l2_capability *)arg;  
  48.   
  49.         cap->version = LINUX_VERSION_CODE;  
  50.         ret = ops->vidioc_querycap(file, fh, cap);  
  51.         if (!ret)  
  52.             dbgarg(cmd, "driver=%s, card=%s, bus=%s, "  
  53.                     "version=0x%08x, "  
  54.                     "capabilities=0x%08x, "  
  55.                     "device_caps=0x%08x\n",  
  56.                     cap->driver, cap->card, cap->bus_info,  
  57.                     cap->version,  
  58.                     cap->capabilities,  
  59.                     cap->device_caps);  
  60.         break;  
  61.     }  

  vivi 驅動就復雜在這些 ioctl 上,下面按照應用層與驅動的交互順序來具體的分析這些 ioctl 。

二、ioctl 深入分析

  應用空間的一個視頻 app 與驅動的交互流程大致如下圖所示:

  下面就根據流程,分析每一個 ioctl 在 vivi 中的具體實現。把以上的過程吃透,自己寫一個虛擬攝像頭程序應該就不成問題了。

 2.1 VIDIOC_QUERYCAP 查詢設備能力

應用層:

[cpp] view plain copy
 
 print?在CODE上查看代碼片派生到我的代碼片
  1. struct v4l2_capability {  
  2.     __u8    driver[16]; /* i.e. "bttv" */  
  3.     __u8    card[32];   /* i.e. "Hauppauge WinTV" */  
  4.     __u8    bus_info[32];   /* "PCI:" + pci_name(pci_dev) */  
  5.     __u32   version;        <span style="white-space:pre">    </span>/* should use KERNEL_VERSION() */  
  6.     __u32   capabilities;   /* Device capabilities */  
  7.     __u32   reserved[4];  
  8. };  
  9.   
  10. struct v4l2_capability cap;  
  11. ret = ioctl(fd,VIDIOC_QUERYCAP,&cap);  
  12. if (ret < 0) {  
  13.     LOG("VIDIOC_QUERYCAP failed (%d)\n", ret);  
  14.     return ret;  
  15. }  

驅動層:

[cpp] view plain copy
 
 print?在CODE上查看代碼片派生到我的代碼片
  1. void *fh = file->private_data;  
  2. ops->vidioc_querycap(file, fh, cap);  
  3. static int vidioc_querycap(struct file *file, void  *priv, struct v4l2_capability *cap)  
  4. {  
  5.     struct vivi_fh  *fh  = priv;  
  6.     struct vivi_dev *dev = fh->dev;  
  7.   
  8.   
  9.     // 這里只是將一些信息寫回用戶空間而已,非常簡單  
  10.     strcpy(cap->driver, "vivi");    
  11.     strcpy(cap->card, "vivi");  
  12.     strlcpy(cap->bus_info, dev->v4l2_dev.name, sizeof(cap->bus_info));  
  13.     cap->version =     VIVI_VERSION; cap->capabilities =V4L2_CAP_VIDEO_CAPTURE |V4L2_CAP_STREAMING     | V4L2_CAP_READWRITE;return 0;}  
  14. }  
 
        

  一般我們只關心 capabilities 成員,比如V4L2_CAP_VIDEO_CAPTURE 具有視頻捕獲能力,其它定義如下:

[cpp] view plain copy
 
 print?在CODE上查看代碼片派生到我的代碼片
  1. /* Values for 'capabilities' field */  
  2. #define V4L2_CAP_VIDEO_CAPTURE      0x00000001  /* Is a video capture device */  
  3. #define V4L2_CAP_VIDEO_OUTPUT       0x00000002  /* Is a video output device */  
  4. #define V4L2_CAP_VIDEO_OVERLAY      0x00000004  /* Can do video overlay */  
  5. #define V4L2_CAP_VBI_CAPTURE        0x00000010  /* Is a raw VBI capture device */  
  6. #define V4L2_CAP_VBI_OUTPUT     0x00000020  /* Is a raw VBI output device */  
  7. #define V4L2_CAP_SLICED_VBI_CAPTURE 0x00000040  /* Is a sliced VBI capture device */  
  8. #define V4L2_CAP_SLICED_VBI_OUTPUT  0x00000080  /* Is a sliced VBI output device */  
  9. #define V4L2_CAP_RDS_CAPTURE        0x00000100  /* RDS data capture */  
  10. #define V4L2_CAP_VIDEO_OUTPUT_OVERLAY   0x00000200  /* Can do video output overlay */  
  11. #define V4L2_CAP_HW_FREQ_SEEK       0x00000400  /* Can do hardware frequency seek  */  
  12. #define V4L2_CAP_RDS_OUTPUT     0x00000800  /* Is an RDS encoder */  

 2.2 VIDIOC_ENUM_FMT 枚舉(查詢)設備支持的視頻格式

應用層:

[cpp] view plain copy
 
 print?在CODE上查看代碼片派生到我的代碼片
  1. struct v4l2_fmtdesc {  
  2.     __u32           index;             /* Format number      */  
  3.     enum v4l2_buf_type  type;              /* buffer type        */  
  4.     __u32               flags;  
  5.     __u8            description[32];   /* Description string */  
  6.     __u32           pixelformat;       /* Format fourcc      */  
  7.     __u32           reserved[4];  
  8. };  
  9.   
  10. struct v4l2_fmtdesc fmtdesc;  
  11. fmtdesc.index=0;  
  12. fmtdesc.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;  
  13. while(ioctl(fd,VIDIOC_ENUM_FMT,&fmtdesc)!=-1)  
  14. {  
  15.     printf("SUPPORT\t%d.%s\n",fmtdesc.index+1,fmtdesc.description);  
  16.     fmtdesc.index++;  
  17. }  

驅動層:

[cpp] view plain copy
 
 print?在CODE上查看代碼片派生到我的代碼片
  1. static struct vivi_fmt formats[] = {  
  2.     {  
  3.         .name     = "4:2:2, packed, YUYV",  
  4.         .fourcc   = V4L2_PIX_FMT_YUYV,  
  5.         .depth    = 16,  
  6.     },  
  7.     ...  
  8. }  
  9. static int vidioc_enum_fmt_vid_cap(struct file *file, void  *priv,  
  10.                     struct v4l2_fmtdesc *f)  
  11. {  
  12.     struct vivi_fmt *fmt;  
  13.   
  14.     if (f->index >= ARRAY_SIZE(formats))  
  15.         return -EINVAL;  
  16.   
  17.     fmt = &formats[f->index];  
  18.   
  19.     strlcpy(f->description, fmt->name, sizeof(f->description));  
  20.     f->pixelformat = fmt->fourcc;  
  21.     return 0;  
  22. }  

  一般一個設備支持多種視頻格式,比如 vivi 它所支持的格式存放在 formats 數組中,由於應用層並不知道設備支持多少種格式,也不知道某種格式具體存放在哪個數組項中,因此通過index從0開始嘗試,對於驅動層來說就是遍歷所有的數組項,返回每一個index對應的視頻格式,比如 V4L2_PIX_FMT_YUYV .

 2.3 VIDIOC_S_FMT 設置視頻格式

應用層:

[cpp] view plain copy
 
 print?在CODE上查看代碼片派生到我的代碼片
  1. struct v4l2_format {  
  2.     enum v4l2_buf_type type;  
  3.     union {  
  4.         struct v4l2_pix_format      pix;     /* V4L2_BUF_TYPE_VIDEO_CAPTURE */  
  5.         struct v4l2_window      win;     /* V4L2_BUF_TYPE_VIDEO_OVERLAY */  
  6.         struct v4l2_vbi_format      vbi;     /* V4L2_BUF_TYPE_VBI_CAPTURE */  
  7.         struct v4l2_sliced_vbi_format   sliced;  /* V4L2_BUF_TYPE_SLICED_VBI_CAPTURE */  
  8.         __u8    raw_data[200];                   /* user-defined */  
  9.     } fmt;  
  10. };  
  11. struct v4l2_pix_format {  
  12.     __u32               width;  
  13.     __u32           height;  
  14.     __u32           pixelformat;  
  15.     enum v4l2_field     field;  
  16.     __u32               bytesperline;   /* for padding, zero if unused */  
  17.     __u32               sizeimage;  
  18.     enum v4l2_colorspace    colorspace;  
  19.     __u32           priv;       /* private data, depends on pixelformat */  
  20. };  
  21. struct v4l2_format fmt;  
  22. memset(&fmt, 0, sizeof(fmt));  
  23. fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;//格式類型  
  24. fmt.fmt.pix.width //寬度  
  25. fmt.fmt.pix.height //高度  
  26. fmt.fmt.pix.pixelformat = VIDEO_FORMAT;//這一項必須是前面查詢出來的某種格式,對應 vivi formats數組  
  27. fmt.fmt.pix.field       = V4L2_FIELD_INTERLACED;//好像是隔行掃描的意思  
  28. ret = ioctl(fd, VIDIOC_S_FMT, &fmt);  
  29. if (ret < 0) {  
  30.     LOG("VIDIOC_S_FMT failed (%d)\n", ret);  
  31.     return ret;  
  32. }  

驅動層:

[cpp] view plain copy
 
 print?在CODE上查看代碼片派生到我的代碼片
  1. static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,  
  2.                     struct v4l2_format *f)  
  3. {  
  4.     struct vivi_dev *dev = video_drvdata(file);  
  5.     struct vb2_queue *q = &dev->vb_vidq;  
  6.   
  7.     int ret = vidioc_try_fmt_vid_cap(file, priv, f);  
  8.     //if (fmt->fourcc == f->fmt.pix.pixelformat)返回formats[k]  
  9.     dev->fmt = get_format(f);  
  10.     dev->pixelsize   = dev->fmt->depth / 8;  
  11.     dev->width       = f->fmt.pix.width;  
  12.     dev->height  = f->fmt.pix.height;  
  13.     dev->field       = f->fmt.pix.field;  
  14.   
  15.     return 0;  
  16. }  
  17. static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,  
  18.             struct v4l2_format *f)  
  19. {  
  20.     struct vivi_dev *dev = video_drvdata(file);  
  21.     struct vivi_fmt *fmt;  
  22.     enum v4l2_field field;  
  23.   
  24.     fmt = get_format(f);  
  25.   
  26.     field = f->fmt.pix.field;  
  27.   
  28.     if (field == V4L2_FIELD_ANY) {  
  29.         field = V4L2_FIELD_INTERLACED;  
  30.     }   
  31.   
  32.     f->fmt.pix.field = field;  
  33.     v4l_bound_align_image(&f->fmt.pix.width, 48, MAX_WIDTH, 2,  
  34.                   &f->fmt.pix.height, 32, MAX_HEIGHT, 0, 0);  
  35.     f->fmt.pix.bytesperline =  
  36.         (f->fmt.pix.width * fmt->depth) >> 3;  
  37.     f->fmt.pix.sizeimage =  
  38.         f->fmt.pix.height * f->fmt.pix.bytesperline;  
  39.     if (fmt->fourcc == V4L2_PIX_FMT_YUYV ||  
  40.         fmt->fourcc == V4L2_PIX_FMT_UYVY)  
  41.         f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;  
  42.     else  
  43.         f->fmt.pix.colorspace = V4L2_COLORSPACE_SRGB;  
  44.     return 0;  
  45. }  

  這里將應用層傳進來的視頻格式簡單處理后存放進了一個 vivi_dev 結構,vivi_dev 哪里來的呢?,在一開始的時候 vivi_create_instance ,我們創建了一個 video_device 結構代表我們的設備,並設置了一個 vivi_dev 作為 video_device->dev->privatedata ,之后 register_video_device ,內核會自動將我們的 video_device 放入全局數組 video_device[] 中。

 2.4 VIDIOC_G_FMT 獲得設置好的視頻格式

應用層:

[cpp] view plain copy
 
 print?在CODE上查看代碼片派生到我的代碼片
  1. ret = ioctl(fd, VIDIOC_G_FMT, &fmt);  
  2. if (ret < 0) {  
  3.     LOG("VIDIOC_G_FMT failed (%d)\n", ret);  
  4.     return ret;  
  5. }  
  6. // Print Stream Format  
  7. LOG("Stream Format Informations:\n");  
  8. LOG(" type: %d\n", fmt.type);  
  9. LOG(" width: %d\n", fmt.fmt.pix.width);  
  10. LOG(" height: %d\n", fmt.fmt.pix.height);  
  11. char fmtstr[8];  
  12. memset(fmtstr, 0, 8);  
  13. memcpy(fmtstr, &fmt.fmt.pix.pixelformat, 4);  
  14. LOG(" pixelformat: %s\n", fmtstr);  
  15. LOG(" field: %d\n", fmt.fmt.pix.field);  
  16. LOG(" bytesperline: %d\n", fmt.fmt.pix.bytesperline);  
  17. LOG(" sizeimage: %d\n", fmt.fmt.pix.sizeimage);  
  18. LOG(" colorspace: %d\n", fmt.fmt.pix.colorspace);  
  19. LOG(" priv: %d\n", fmt.fmt.pix.priv);  
  20. LOG(" raw_date: %s\n", fmt.fmt.raw_data);  

驅動層:

[cpp] view plain copy
 
 print?在CODE上查看代碼片派生到我的代碼片
  1. static int vidioc_g_fmt_vid_cap(struct file *file, void *priv,  
  2.                     struct v4l2_format *f)  
  3. {  
  4.     struct vivi_dev *dev = video_drvdata(file);  
  5. <span style="white-space:pre">    </span>// 把記錄在 vivi_dev 中的參數寫回用戶空間  
  6.     f->fmt.pix.width        = dev->width;  
  7.     f->fmt.pix.height       = dev->height;  
  8.     f->fmt.pix.field        = dev->field;  
  9.     f->fmt.pix.pixelformat  = dev->fmt->fourcc;  
  10.     f->fmt.pix.bytesperline =  
  11.         (f->fmt.pix.width * dev->fmt->depth) >> 3;  
  12.     f->fmt.pix.sizeimage =  
  13.         f->fmt.pix.height * f->fmt.pix.bytesperline;  
  14.     if (dev->fmt->fourcc == V4L2_PIX_FMT_YUYV ||  
  15.         dev->fmt->fourcc == V4L2_PIX_FMT_UYVY)  
  16.         f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;  
  17.     else  
  18.         f->fmt.pix.colorspace = V4L2_COLORSPACE_SRGB;  
  19.     return 0;  
  20. }  

  將我們之前設置的格式返回而已。
  2.5 VIDIOC_REQBUFS 請求在內核空間分配視頻緩沖區
    分配的內存位於內核空間,應用程序無法直接訪問,需要通過調用mmap內存映射函數,把內核空間的內存映射到用戶空間,應用才可以用用戶空間地址來訪問內核空間。
應用層:

[cpp] view plain copy
 
 print?在CODE上查看代碼片派生到我的代碼片
  1. struct v4l2_requestbuffers {  
  2.     __u32           count;  
  3.     __u32           type;       /* enum v4l2_buf_type */  
  4.     __u32           memory;     /* enum v4l2_memory */  
  5.     __u32           reserved[2];  
  6. };  
  7. struct v4l2_requestbuffers reqbuf;  
  8. reqbuf.type     = V4L2_BUF_TYPE_VIDEO_CAPTURE;  
  9. reqbuf.memory   = V4L2_MEMORY_MMAP;  
  10. reqbuf.count    = BUFFER_COUNT;  
  11. ret = ioctl(fd , VIDIOC_REQBUFS, &reqbuf);  
  12. if(ret < 0) {  
  13.     LOG("VIDIOC_REQBUFS failed (%d)\n", ret);  
  14.     return ret;  
  15. }  

驅動層:

[cpp] view plain copy
 
 print?在CODE上查看代碼片派生到我的代碼片
  1. static int vidioc_reqbufs(struct file *file, void *priv,  
  2.               struct v4l2_requestbuffers *p)  
  3. {  
  4.     struct vivi_dev *dev = video_drvdata(file);  
  5.     return vb2_reqbufs(&dev->vb_vidq, p);    //核心層提供的標准函數  
  6. }  

  vb_vidq 是 vivi_dev 的一個成員,前面我們提到它有兩個 ops ,一個是 ops 另一個是 mem_ops

[cpp] view plain copy
 
 print?在CODE上查看代碼片派生到我的代碼片
  1. static struct vb2_ops vivi_video_qops = {  
  2.     .queue_setup    = queue_setup,  
  3.     .buf_init       = buffer_init,  
  4.     .buf_prepare    = buffer_prepare,  
  5.     .buf_finish         = buffer_finish,  
  6.     .buf_cleanup        = buffer_cleanup,  
  7.     .buf_queue      = buffer_queue,  
  8.     .start_streaming= start_streaming,  
  9.     .stop_streaming     = stop_streaming,  
  10.     .wait_prepare       = vivi_unlock,  
  11.     .wait_finish        = vivi_lock,  
  12. };  
[cpp] view plain copy
 
 print?在CODE上查看代碼片派生到我的代碼片
  1. static int vidioc_reqbufs(struct file *file, void *priv,  
  2.               struct v4l2_requestbuffers *p)  
  3. {  
  4.     struct vivi_dev *dev = video_drvdata(file);  
  5.     return vb2_reqbufs(&dev->vb_vidq, p);    //核心層提供的標准函數  
  6. }  
[cpp] view plain copy
 
 print?在CODE上查看代碼片派生到我的代碼片
  1. int vb2_reqbufs(struct vb2_queue *q, struct v4l2_requestbuffers *req)  
  2. {  
  3.     unsigned int num_buffers, allocated_buffers, num_planes = 0;  
  4.     int ret = 0;  
  5.     // 判斷 re->count 是否小於 VIDEO_MAX_FRAME  
  6.     num_buffers = min_t(unsigned int, req->count, VIDEO_MAX_FRAME);  
  7.     memset(q->plane_sizes, 0, sizeof(q->plane_sizes));  
  8.     memset(q->alloc_ctx, 0, sizeof(q->alloc_ctx));  
  9.     q->memory = req->memory;  
  10.   
  11.     //(q)->ops->queue_setup(q,NULL,...)  
  12.     ret = call_qop(q, queue_setup, q, NULL, &num_buffers, &num_planes,  
  13.                q->plane_sizes, q->alloc_ctx);  
  14.     /* Finally, allocate buffers and video memory */  
  15.     ret = __vb2_queue_alloc(q, req->memory, num_buffers, num_planes);  
  16.   
  17.     allocated_buffers = ret;  
  18.   
  19.     q->num_buffers = allocated_buffers;  
  20.     req->count = allocated_buffers;  
  21.     return 0;  
  22. }  
[cpp] view plain copy
 
 print?在CODE上查看代碼片派生到我的代碼片
  1. static int queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt,  
  2.                 unsigned int *nbuffers, unsigned int *nplanes,  
  3.                 unsigned int sizes[], void *alloc_ctxs[])  
  4. {  
  5.     struct vivi_dev *dev = vb2_get_drv_priv(vq);  
  6.     unsigned long size;  
  7.     // 每一個buffer 的大小  
  8.     size = dev->width * dev->height * dev->pixelsize;  
  9.     if (0 == *nbuffers)  
  10.         *nbuffers = 32;  
  11.     // 如果申請的buffer過多,導致空間不夠減少buffer  
  12.     while (size * *nbuffers > vid_limit * 1024 * 1024)  
  13.         (*nbuffers)--;  
  14.     *nplanes = 1;  
  15.     // 把總大小放入 vivi_dev->vb_vidq->plane_size[0]  
  16.     sizes[0] = size;  
  17.     return 0;  
  18. }  
[cpp] view plain copy
 
 print?在CODE上查看代碼片派生到我的代碼片
  1. static int __vb2_queue_alloc(struct vb2_queue *q, enum v4l2_memory memory,  
  2.                  unsigned int num_buffers, unsigned int num_planes)  
  3. {  
  4.     unsigned int buffer;  
  5.     struct vb2_buffer *vb;  
  6.     int ret;  
  7.     // 分配多個 vb2_buffer 填充並放入 vivi_dev->vb_vidq->bufs[]  
  8.     for (buffer = 0; buffer < num_buffers; ++buffer) {  
  9.         /* Allocate videobuf buffer structures */  
  10.         vb = kzalloc(q->buf_struct_size, GFP_KERNEL);  
  11.           
  12.         /* Length stores number of planes for multiplanar buffers */  
  13.         if (V4L2_TYPE_IS_MULTIPLANAR(q->type))  
  14.             vb->v4l2_buf.length = num_planes;  
  15.   
  16.         vb->state = VB2_BUF_STATE_DEQUEUED;  
  17.         vb->vb2_queue = q;  
  18.         vb->num_planes = num_planes;  
  19.         vb->v4l2_buf.index = q->num_buffers + buffer;  
  20.         vb->v4l2_buf.type = q->type;  
  21.         vb->v4l2_buf.memory = memory;  
  22.   
  23.         /* Allocate video buffer memory for the MMAP type */  
  24.         if (memory == V4L2_MEMORY_MMAP) {  
  25.             ret = __vb2_buf_mem_alloc(vb);//核心提供的標准函數  
  26.             ret = call_qop(q, buf_init, vb);//q->ops->buf_init  
  27.         }  
  28.   
  29.         q->bufs[q->num_buffers + buffer] = vb;  
  30.     }  
  31.     __setup_offsets(q, buffer);  
  32.     return buffer;  
  33. }  
[cpp] view plain copy
 
 print?在CODE上查看代碼片派生到我的代碼片
  1. static int __vb2_buf_mem_alloc(struct vb2_buffer *vb)  
  2. {  
  3.     struct vb2_queue *q = vb->vb2_queue;  
  4.     void *mem_priv;  
  5.     int plane;  
  6.   
  7.     /* num_planes == 1 */  
  8.     for (plane = 0; plane < vb->num_planes; ++plane) {  
  9.         mem_priv = call_memop(q, alloc, q->alloc_ctx[plane],  
  10.                       q->plane_sizes[plane]);  
  11.   
  12.         /* Associate allocator private data with this plane */  
  13.         vb->planes[plane].mem_priv = mem_priv;  
  14.         vb->v4l2_planes[plane].length = q->[plane];  
  15.     }  
  16.   
  17.     return 0;  
  18. }  
[cpp] view plain copy
 
 print?在CODE上查看代碼片派生到我的代碼片
  1. static void *vb2_vmalloc_alloc(void *alloc_ctx, unsigned long size)  
  2. {  
  3.     struct vb2_vmalloc_buf *buf;  
  4.   
  5.     buf = kzalloc(sizeof(*buf), GFP_KERNEL);  
  6.   
  7.     buf->size = size;  
  8.     // 分配空間  
  9.     buf->vaddr = vmalloc_user(buf->size);  
  10.     buf->handler.refcount = &buf->refcount;  
  11.     buf->handler.put = vb2_vmalloc_put;  
  12.     buf->handler.arg = buf;  
  13.   
  14.     atomic_inc(&buf->refcount);  
  15.     return buf;  
  16. }  

  2.6 VIDIOC_QUERYBUF 查詢分配好的 buffer 信息
    查詢已經分配好的V4L2視頻緩沖區的相關信息,包括緩沖區的使用狀態、在內核空間的偏移地址、緩沖區長度等,然后應用程序根據這些信息使用mmap把內核空間地址映射到用戶空間。
應用層:

[cpp] view plain copy
 
 print?在CODE上查看代碼片派生到我的代碼片
  1. struct v4l2_buffer {  
  2.     __u32                   index;  
  3.     enum v4l2_buf_type      type;  
  4.     __u32                   bytesused;  
  5.     __u32                   flags;  
  6.     enum v4l2_field         field;  
  7.     struct timeval          timestamp;  
  8.     struct v4l2_timecode    timecode;  
  9.     __u32                   sequence;  
  10.   
  11.     /* memory location */  
  12.     enum v4l2_memory        memory;  
  13.     union {  
  14.         __u32               offset;  
  15.         unsigned long       userptr;  
  16.     } m;  
  17.     __u32                   length;  
  18.     __u32                   input;  
  19.     __u32                   reserved;  
  20. };  
  21. v4l2_buffer buf;  
  22. buf.index = i;  
  23. buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;  
  24. buf.memory = V4L2_MEMORY_MMAP;  
  25. ret = ioctl(fd , VIDIOC_QUERYBUF, &buf);  
  26. if(ret < 0) {  
  27.     LOG("VIDIOC_QUERYBUF (%d) failed (%d)\n", i, ret);  
  28.     return ret;  
  29. }  

驅動層:

[cpp] view plain copy
 
 print?在CODE上查看代碼片派生到我的代碼片
  1. ops->vidioc_querybuf(file, fh, p);  
  2. static int vidioc_querybuf(struct file *file, void *priv, struct v4l2_buffer *p)  
  3. {  
  4.     struct vivi_dev *dev = video_drvdata(file);  
  5.     return vb2_querybuf(&dev->vb_vidq, p);  
  6. }  
[cpp] view plain copy
 
 print?在CODE上查看代碼片派生到我的代碼片
  1. int vb2_querybuf(struct vb2_queue *q, struct v4l2_buffer *b)  
  2. {  
  3.     struct vb2_buffer *vb;  
  4.     // 取出 buf  
  5.     vb = q->bufs[b->index];  
  6.     // 將 buf 信息寫回用戶空間傳遞的 b  
  7.     return __fill_v4l2_buffer(vb, b);  
  8. }  
[cpp] view plain copy
 
 print?在CODE上查看代碼片派生到我的代碼片
  1. static int __fill_v4l2_buffer(struct vb2_buffer *vb, struct v4l2_buffer *b)  
  2. {  
  3.     struct vb2_queue *q = vb->vb2_queue;  
  4.     int ret;  
  5.   
  6.     /* Copy back data such as timestamp, flags, input, etc. */  
  7.     memcpy(b, &vb->v4l2_buf, offsetof(struct v4l2_buffer, m));  
  8.     b->input = vb->v4l2_buf.input;  
  9.     b->reserved = vb->v4l2_buf.reserved;  
  10.   
  11.     if (V4L2_TYPE_IS_MULTIPLANAR(q->type)) {  
  12.         ret = __verify_planes_array(vb, b);  
  13.         if (ret)  
  14.             return ret;  
  15.   
  16.         /* 
  17.          * Fill in plane-related data if userspace provided an array 
  18.          * for it. The memory and size is verified above. 
  19.          */  
  20.         memcpy(b->m.planes, vb->v4l2_planes,  
  21.             b->length * sizeof(struct v4l2_plane));  
  22.   
  23.         if (q->memory == V4L2_MEMORY_DMABUF) {  
  24.             unsigned int plane;  
  25.             for (plane = 0; plane < vb->num_planes; ++plane)  
  26.                 b->m.planes[plane].m.fd = 0;  
  27.         }  
  28.     } else {  
  29.         /* 
  30.          * We use length and offset in v4l2_planes array even for 
  31.          * single-planar buffers, but userspace does not. 
  32.          */  
  33.         b->length = vb->v4l2_planes[0].length;  
  34.         b->bytesused = vb->v4l2_planes[0].bytesused;  
  35.         if (q->memory == V4L2_MEMORY_MMAP)  
  36.             b->m.offset = vb->v4l2_planes[0].m.mem_offset;  
  37.         else if (q->memory == V4L2_MEMORY_USERPTR)  
  38.             b->m.userptr = vb->v4l2_planes[0].m.userptr;  
  39.         else if (q->memory == V4L2_MEMORY_DMABUF)  
  40.             b->m.fd = 0;  
  41.     }  
  42.   
  43.     /* 
  44.      * Clear any buffer state related flags. 
  45.      */  
  46.     b->flags &= ~V4L2_BUFFER_STATE_FLAGS;  
  47.   
  48.     switch (vb->state) {  
  49.     case VB2_BUF_STATE_QUEUED:  
  50.     case VB2_BUF_STATE_ACTIVE:  
  51.         b->flags |= V4L2_BUF_FLAG_QUEUED;  
  52.         break;  
  53.     case VB2_BUF_STATE_ERROR:  
  54.         b->flags |= V4L2_BUF_FLAG_ERROR;  
  55.         /* fall through */  
  56.     case VB2_BUF_STATE_DONE:  
  57.         b->flags |= V4L2_BUF_FLAG_DONE;  
  58.         break;  
  59.     case VB2_BUF_STATE_PREPARED:  
  60.         b->flags |= V4L2_BUF_FLAG_PREPARED;  
  61.         break;  
  62.     case VB2_BUF_STATE_DEQUEUED:  
  63.         /* nothing */  
  64.         break;  
  65.     }  
  66.   
  67.     if (__buffer_in_use(q, vb))  
  68.         b->flags |= V4L2_BUF_FLAG_MAPPED;  
  69.   
  70.     return 0;  
  71. }  

2.7 mmap
應用層:

[cpp] view plain copy
 
 print?在CODE上查看代碼片派生到我的代碼片
  1. v4l2_buffer framebuf[]  
  2. framebuf[i].length = buf.length;  
  3. framebuf[i].start = (char *) mmap(  
  4.     NULL,       // 欲指向內存的起始地址,一般為NULL,表示系統自動分配  
  5.     buf.length, //映射長度  
  6.     PROT_READ|PROT_WRITE,   //可讀可寫  
  7.     MAP_SHARED,     //對映射區的讀寫會寫回內核空間,而且允許其它映射該內核空間地址的進程共享  
  8.     fd,   
  9.     buf.m.offset  
  10. );  
  11. if (framebuf[i].start == MAP_FAILED) {  
  12.     LOG("mmap (%d) failed: %s\n", i, strerror(errno));  
  13.     return -1;  
  14. }  

驅動層:

[cpp] view plain copy
 
 print?在CODE上查看代碼片派生到我的代碼片
  1. static int vivi_mmap(struct file *file, struct vm_area_struct *vma)  
  2. {  
  3.     struct vivi_dev *dev = video_drvdata(file);  
  4.     int ret;  
  5.     ret = vb2_mmap(&dev->vb_vidq, vma);//核心層提供的函數  
  6.     return ret;  
  7. }  

2.8 VIDIOC_QBUF 
  投放一個空的視頻緩沖區到視頻緩沖區輸入隊列,執行成功后,在啟動視頻設備拍攝圖像時,相應的視頻數據被保存到視頻輸入隊列相應的視頻緩沖區中。
應用層:

[cpp] view plain copy
 
 print?在CODE上查看代碼片派生到我的代碼片
  1. ret = ioctl(fd , VIDIOC_QBUF, &buf);  
  2. if (ret < 0) {  
  3.     LOG("VIDIOC_QBUF (%d) failed (%d)\n", i, ret);  
  4.     return -1;  

驅動層:

[cpp] view plain copy
 
 print?在CODE上查看代碼片派生到我的代碼片
  1. static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *p)  
  2. {  
  3.     struct vivi_dev *dev = video_drvdata(file);  
  4.     return vb2_qbuf(&dev->vb_vidq, p);  
  5. }  
[cpp] view plain copy
 
 print?在CODE上查看代碼片派生到我的代碼片
  1. int vb2_qbuf(struct vb2_queue *q, struct v4l2_buffer *b)  
  2. {  
  3.     struct rw_semaphore *mmap_sem = NULL;  
  4.     struct vb2_buffer *vb;  
  5.     int ret = 0;  
  6.   
  7.     vb = q->bufs[b->index];  
  8.   
  9.     switch (vb->state) {  
  10.     case VB2_BUF_STATE_DEQUEUED:  
  11.         ret = __buf_prepare(vb, b);  
  12.     }  
  13.     // 將這個 buffer 掛入 q->queued_list  
  14.     list_add_tail(&vb->queued_entry, &q->queued_list);  
  15.     vb->state = VB2_BUF_STATE_QUEUED;  
  16.   
  17.     if (q->streaming)  
  18.         __enqueue_in_driver(vb);  
  19.   
  20.     /* Fill buffer information for the userspace */  
  21.     __fill_v4l2_buffer(vb, b);  
  22.   
  23. unlock:  
  24.     if (mmap_sem)  
  25.         up_read(mmap_sem);  
  26.     return ret;  
  27. }  

  實質上就是取出一個 vb2_buffer 掛入 vivi_dev->vb_vidq->queued_list
2.9 VIDIOC_STREAMON
應用層:

[cpp] view plain copy
 
 print?在CODE上查看代碼片派生到我的代碼片
  1. enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;  
  2. ret = ioctl(fd, VIDIOC_STREAMON, &type);  
  3. if (ret < 0) {  
  4.     LOG("VIDIOC_STREAMON failed (%d)\n", ret);  
  5.     return ret;  
  6. }  

驅動層:

[cpp] view plain copy
 
 print?在CODE上查看代碼片派生到我的代碼片
  1. static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i)  
  2. {  
  3.     struct vivi_dev *dev = video_drvdata(file);  
  4.     return vb2_streamon(&dev->vb_vidq, i);  
  5. }  
[cpp] view plain copy
 
 print?在CODE上查看代碼片派生到我的代碼片
  1. int vb2_streamon(struct vb2_queue *q, enum v4l2_buf_type type)  
  2. {  
  3.     struct vb2_buffer *vb;  
  4.     int ret;  
  5.     vb->state = VB2_BUF_STATE_ACTIVE;  
  6.     // 在 queued_list 鏈表中取出每一個 buffer 調用buffer queue,對於vivi來說就是放入 vidq->active 鏈表  
  7.     list_for_each_entry(vb, &q->queued_list, queued_entry)  
  8.             __enqueue_in_driver(vb);  
  9.     ret = call_qop(q, start_streaming, q, atomic_read(&q->queued_count));  
  10.   
  11.     q->streaming = 1;  
  12.     return 0;  
  13. }  
[cpp] view plain copy
 
 print?在CODE上查看代碼片派生到我的代碼片
  1. static void __enqueue_in_driver(struct vb2_buffer *vb)  
  2. {  
  3.     struct vb2_queue *q = vb->vb2_queue;  
  4.     vb->state = VB2_BUF_STATE_ACTIVE;  
  5.   
  6.     /* sync buffers */  
  7.     for (plane = 0; plane < vb->num_planes; ++plane)  
  8.     call_memop(q, prepare, vb->planes[plane].mem_priv);  
  9.   
  10.     q->ops->buf_queue(vb);//    list_add_tail(&buf->list, &vidq->active);      
  11. }  
[cpp] view plain copy
 
 print?在CODE上查看代碼片派生到我的代碼片
  1. static int start_streaming(struct vb2_queue *vq, unsigned int count)  
  2. {  
  3.     struct vivi_dev *dev = vb2_get_drv_priv(vq);  
  4.     dprintk(dev, 1, "%s\n", __func__);  
  5.     return vivi_start_generating(dev);  
  6. }  
[cpp] view plain copy
 
 print?在CODE上查看代碼片派生到我的代碼片
  1. static int vivi_start_generating(struct vivi_dev *dev)  
  2. {  
  3.     struct vivi_dmaqueue *dma_q = &dev->vidq;  
  4.   
  5.     /* Resets frame counters */  
  6.     dev->ms = 0;  
  7.     dev->mv_count = 0;  
  8.     dev->jiffies = jiffies;  
  9.   
  10.     dma_q->frame = 0;  
  11.     dma_q->ini_jiffies = jiffies;  
  12.     // 創建一個內核線程,入口函數 vivi_thread  
  13.     dma_q->kthread = kthread_run(vivi_thread, dev, dev->v4l2_dev.name);  
  14.       
  15.     /* Wakes thread */  
  16.     wake_up_interruptible(&dma_q->wq);  
  17.   
  18.     return 0;  
  19. }  
[cpp] view plain copy
 
 print?在CODE上查看代碼片派生到我的代碼片
  1. static int vivi_thread(void *data)  
  2. {  
  3.     struct vivi_dev *dev = data;  
  4.   
  5.     dprintk(dev, 1, "thread started\n");  
  6.   
  7.     set_freezable();  
  8.   
  9.     for (;;) {  
  10.         vivi_sleep(dev);  
  11.   
  12.         if (kthread_should_stop())  
  13.             break;  
  14.     }  
  15.     dprintk(dev, 1, "thread: exit\n");  
  16.     return 0;  
  17. }  
[cpp] view plain copy
 
 print?在CODE上查看代碼片派生到我的代碼片
  1. static void vivi_sleep(struct vivi_dev *dev)  
  2. {  
  3.     struct vivi_dmaqueue *dma_q = &dev->vidq;  
  4.     int timeout;  
  5.     DECLARE_WAITQUEUE(wait, current);  
  6.   
  7.     add_wait_queue(&dma_q->wq, &wait);  
  8.     if (kthread_should_stop())  
  9.         goto stop_task;  
  10.   
  11.     /* Calculate time to wake up */  
  12.     timeout = msecs_to_jiffies(frames_to_ms(1));  
  13.   
  14.     vivi_thread_tick(dev);  
  15.   
  16.     schedule_timeout_interruptible(timeout);  
  17.   
  18. stop_task:  
  19.     remove_wait_queue(&dma_q->wq, &wait);  
  20.     try_to_freeze();  
  21. }  

  每次調用 vivi_sleep 這個線程都被掛入等待隊列,調用 vivi_thread_tick 填充數據,然后休眠指定的時間自動喚醒,一直循環下去。這樣就生成了一幀一幀的視頻數據。

[cpp] view plain copy
 
 print?在CODE上查看代碼片派生到我的代碼片
  1. static void vivi_thread_tick(struct vivi_dev *dev)  
  2. {  
  3.     struct vivi_dmaqueue *dma_q = &dev->vidq;  
  4.     struct vivi_buffer *buf;  
  5.     unsigned long flags = 0;  
  6.   
  7.     spin_lock_irqsave(&dev->slock, flags);  
  8.   
  9.     buf = list_entry(dma_q->active.next, struct vivi_buffer, list);  
  10.     list_del(&buf->list);  
  11.     spin_unlock_irqrestore(&dev->slock, flags);  
  12.   
  13.     do_gettimeofday(&buf->vb.v4l2_buf.timestamp);  
  14.   
  15.     /* 填充Buffer */  
  16.     vivi_fillbuff(dev, buf);  
  17.   
  18.     vb2_buffer_done(&buf->vb, VB2_BUF_STATE_DONE);  
  19. }  
[cpp] view plain copy
 
 print?在CODE上查看代碼片派生到我的代碼片
  1. void vb2_buffer_done(struct vb2_buffer *vb, enum vb2_buffer_state state)  
  2. {  
  3.     struct vb2_queue *q = vb->vb2_queue;  
  4.     unsigned long flags;  
  5.     unsigned int plane;  
  6.   
  7.     /* sync buffers */  
  8.     for (plane = 0; plane < vb->num_planes; ++plane)  
  9.         call_memop(q, finish, vb->planes[plane].mem_priv);  
  10.   
  11.     /* Add the buffer to the done buffers list */  
  12.     spin_lock_irqsave(&q->done_lock, flags);  
  13.     vb->state = state;  
  14.     list_add_tail(&vb->done_entry, &q->done_list);  
  15.     atomic_dec(&q->queued_count);  
  16. #ifdef CONFIG_SYNC  
  17.     sw_sync_timeline_inc(q->timeline, 1);  
  18. #endif  
  19.     spin_unlock_irqrestore(&q->done_lock, flags);  
  20.   
  21.     /* 應用程序select 時 poll_wait 里休眠,現在有數據了喚醒 */  
  22.     wake_up(&q->done_wq);  
  23. }  

  開始的時候我們將以一個 vb_buffer 掛入 vb_vidq->queued_list ,當啟動視頻傳輸之后,它被取出掛入 vb_vidq->vidq->active 隊列,然后在內核線程中每一個 tick ,又將它取出填充視頻數據之后,再掛入 vb_vidq->done_list ,喚醒正在休眠等待視頻數據的應用程序。
2.10 select
驅動層:

[cpp] view plain copy
 
 print?在CODE上查看代碼片派生到我的代碼片
  1. vivi_poll(struct file *file, struct poll_table_struct *wait)  
  2. {  
  3.     struct vivi_dev *dev = video_drvdata(file);  
  4.     struct vb2_queue *q = &dev->vb_vidq;  
  5.   
  6.     return vb2_poll(q, file, wait);  
  7. }  
[cpp] view plain copy
 
 print?在CODE上查看代碼片派生到我的代碼片
  1. unsigned int vb2_poll(struct vb2_queue *q, struct file *file, poll_table *wait)  
  2. {  
  3.     // 掛入休眠隊列,是否休眠還要看返回值,大概沒有數據就休眠,有數據就不休眠  
  4.     poll_wait(file, &q->done_wq, wait);  
  5.   
  6.     if (!list_empty(&q->done_list))  
  7.         vb = list_first_entry(&q->done_list, struct vb2_buffer,  
  8.                     done_entry);  
  9.     spin_unlock_irqrestore(&q->done_lock, flags);  
  10.   
  11.     if (vb && (vb->state == VB2_BUF_STATE_DONE  
  12.             || vb->state == VB2_BUF_STATE_ERROR)) {  
  13.         return (V4L2_TYPE_IS_OUTPUT(q->type)) ?  
  14.                 res | POLLOUT | POLLWRNORM :  
  15.                 res | POLLIN | POLLRDNORM;  
  16.     }  
  17.     return res;  
  18. }  

  喚醒之后,我們就可以去從視頻輸出隊列中取出buffer,然后根據映射關系,在應用空間取出視頻數據了
2.11 VIDIOC_DQBUF
應用層:

[cpp] view plain copy
 
 print?在CODE上查看代碼片派生到我的代碼片
  1. ret = ioctl(fd, VIDIOC_DQBUF, &buf);  
  2. if (ret < 0) {  
  3.     LOG("VIDIOC_DQBUF failed (%d)\n", ret);  
  4.     return ret;  
  5. }  
[cpp] view plain copy
 
 print?在CODE上查看代碼片派生到我的代碼片
  1. static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p)  
  2. {  
  3.     struct vivi_dev *dev = video_drvdata(file);  
  4.     return vb2_dqbuf(&dev->vb_vidq, p, file->f_flags & O_NONBLOCK);  
  5. }  
[cpp] view plain copy
 
 print?在CODE上查看代碼片派生到我的代碼片
  1. int vb2_dqbuf(struct vb2_queue *q, struct v4l2_buffer *b, bool nonblocking)  
  2. {  
  3.     struct vb2_buffer *vb = NULL;  
  4.     int ret;  
  5.     // 等待在 q->done_list 取出第一個可用的 buffer  
  6.     ret = __vb2_get_done_vb(q, &vb, nonblocking);  
  7.   
  8.     ret = call_qop(q, buf_finish, vb);  
  9.   
  10.     /* 寫回buffer的信息到用戶空間,應用程序找個這個buffer的mmap之后的地址讀數據 */  
  11.     __fill_v4l2_buffer(vb, b);  
  12.     /* Remove from videobuf queue */  
  13.     list_del(&vb->queued_entry);  
  14.   
  15.     vb->state = VB2_BUF_STATE_DEQUEUED;  
  16.     return 0;  
  17. }  
[cpp] view plain copy
 
 print?在CODE上查看代碼片派生到我的代碼片
  1. static int __vb2_get_done_vb(struct vb2_queue *q, struct vb2_buffer **vb,int nonblocking)  
  2. {  
  3.     unsigned long flags;  
  4.     int ret;  
  5.     /* 
  6.     * Wait for at least one buffer to become available on the done_list. 
  7.     */  
  8.     ret = __vb2_wait_for_done_vb(q, nonblocking);  
  9.   
  10.   
  11.     spin_lock_irqsave(&q->done_lock, flags);  
  12.     *vb = list_first_entry(&q->done_list, struct vb2_buffer, done_entry);  
  13.     list_del(&(*vb)->done_entry);  
  14.     spin_unlock_irqrestore(&q->done_lock, flags);  
  15.     return 0;  
  16. }  
[cpp] view plain copy
 
 print?在CODE上查看代碼片派生到我的代碼片
    1. static int buffer_finish(struct vb2_buffer *vb)  
    2. {  
    3.     struct vivi_dev *dev = vb2_get_drv_priv(vb->vb2_queue);  
    4.     dprintk(dev, 1, "%s\n", __func__);  
    5.     return 0;  
    6. }


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM