2四、vb2_buffer和videobuf_buffer比較分析

看韋東山視頻第三期攝像頭驅動中構造了本身的vivi驅動,可是使用的videoBuf結構體,新的版本用的是vb2_buffer結構,我機器上(ubuntu12.04)使用的內核是linux3.2,看了看改動仍是挺大的,本身看代碼本身理解了下:linux

        首先是韋東山老師總結的攝像頭驅動的架構以下
 
攝像頭驅動程序必需的11個ioctl:
    // 表示它是一個攝像頭設備
 .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_streamon      = vidioc_streamon,
 .vidioc_streamoff     = vidioc_streamoff,
     
分析數據的獲取過程:
1. 請求分配緩衝區: ioctl(4, VIDIOC_REQBUFS          // 請求系統分配緩衝區
2. 查詢映射緩衝區:  ioctl(4, VIDIOC_QUERYBUF         // 查詢所分配的緩衝區 
3. 把緩衝區放入隊列: ioctl(4, VIDIOC_QBUF             // 把緩衝區放入隊列         
4. 啓動攝像頭  ioctl(4, VIDIOC_STREAMON 
5. 用select查詢是否有數據  
6. 有數據後從隊列裏取出緩衝區  ioctl(4, VIDIOC_DQBUF
7. 應用程序根據VIDIOC_DQBUF所獲得緩衝區狀態,知道是哪個緩衝區有數據    就去讀對應的地址(該地址來自前面的mmap) 
 
對於架構都是同樣的,有關於VB2_buffer 的主要是數據的獲取過程,根據韋東山老師的視頻中分析步驟分析以下:
1,首先是建立並初始化一個vb2_queue結構體 ,
        static   struct vb2_queue    Myvivi_vb2_queue;
 
        struct vb2_queue  *q = &Myvivi_vb2_queue;
         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);  // 將vb2_buffer結構體封裝到咱們本身的buffer中,此爲咱們本身的buffer的size
         q->ops = &vivi_video_qops;   
         q->mem_ops = &vb2_vmalloc_memops;     // 
        vb2_queue_init(q);
    其中vivi_video_qops 是 隊列的操做函數,之後的REQBUFS 等請求會調用到此中的函數,其結構以下
static struct vb2_ops vivi_video_qops = {
 .queue_setup = queue_setup,   // 必須有,vb2_queue_init中會判斷
 .buf_init = buffer_init,
 .buf_prepare = buffer_prepare,
 .buf_finish = buffer_finish,
 .buf_cleanup = buffer_cleanup,
 .buf_queue = buffer_queue,  // 必須有,vb2_queue_init中會判斷
 .start_streaming = start_streaming,
 .stop_streaming = stop_streaming,
 .wait_prepare = vivi_unlock,
 .wait_finish = vivi_lock,
};
      其中vb2_vmalloc_memops用於有關此隊列中mem分配問題,其中的函數通常不須要咱們本身寫,使用默認
const struct vb2_mem_ops vb2_vmalloc_memops = {
     .alloc = vb2_vmalloc_alloc,
     .put = vb2_vmalloc_put,
     .vaddr = vb2_vmalloc_vaddr,
     .mmap = vb2_vmalloc_mmap,
     .num_users = vb2_vmalloc_num_users,
};
 
2,初始化完成後,調用VIDIOC_REQBUFS  請求系統分配緩衝區,該函數的調用過程以下
    vb2_reqbufs  ===》
            q->ops->queue_setup此函數容許咱們的驅動函數自定義分配空間的大小
             __vb2_queue_alloc  分配vivi_buffer結構體的空間(緩存區頭部信息), 若是使用的是V4L2_MEMORY_MMAP類型則 調用==>__vb2_buf_mem_alloc  ==> q->mem_ops->alloc  即vb2_vmalloc_alloc 分配空間,將分配的空間指向vb2_buffer->planes[0].mem_priv ,該指針保存着分配到的空間,該指針指向vb2_vmalloc_buf結構體
韋東山老師視頻中講的video_buf中,講到在這不真正的分配空間,可是在vb2中此時卻已經分配了空間,這是個人理解
 
3,查詢映射緩衝區 VIDIOC_QUERYBUF , 返回實際上分配到的buffer,
      查詢分配好的緩存區,返回v4l2_buffer結構,設置vb->state 
 
4,使用mmap
    vb2_mmap ==》q->mem_ops->mmap   即  vb2_vmalloc_mmap  用於映射,將上面分配好的vb2_buffer->planes[0].mem_priv指向的空間重映射到mmap參數中的用戶空間
 
5,把緩衝區放入隊列: VIDIOC_QBUF     
        vb2_qbuf    將 list_add_tail(&vb->queued_entry, &q->queued_list);    將vb2_buffer 放入隊列q的queued_list中
        設置vb->state = VB2_BUF_STATE_PREPARED;
 
6,  啓動攝像頭   VIDIOC_STREAMON 
    vb2_streamon 
        q->streaming = 1;   
// 若是 q->queued_list 中部位空,即有qbuf沒有被處理 調用__enqueue_in_driver ()
 
7, 用select查詢是否有數據  會調用poll函數   
       vb2_poll   等待 q->done_list 中有數據,   
 
8, 怎樣往 q->done_list 中添加數據呢 ?
每次調用qbuf 和 vidioc_streamon 時候都會查詢,若是這兩個條件都成立,則調用q->ops->buf_queue 將 核心中的vb2_buffer調如咱們寫的驅動中,放入一個列表, 在vivi中 週期性的調用函數向這個列表中的vb緩衝區中添加數據 即 向vb2_buffer->planes中添加數據 ,而後後調用  
vb2_buffer_done(&vb, VB2_BUF_STATE_DONE);  ==》 list_add_tail(&vb->done_entry, &q->done_list);  將vivi驅動中的vb2 放入 q->done_list中 ,而後設置vb->state = VB2_BUF_STATE_DONE;
最後wake_up(&q->done_wq);  喚醒poll中休眠的進程。
 
9,調用VIDIOC_DQBUF, 從隊列裏取出緩衝區
        vb2_dqbuf  ==> __vb2_get_done_vb  將q->done_list 中的vb2_buffer中提出來,而後 將vb2_buffer中的v4l2_buffer信息返回,並將其從q->done_list 中刪除
 
10,應用程序將數據取出來(mmap的空間)
 
總結:    
結構體以下 
struct vb2_buffer {
 struct v4l2_buffer v4l2_buf;    // 裏面有該vb2中數據的信息
 struct v4l2_plane v4l2_planes[VIDEO_MAX_PLANES];
 
 struct vb2_queue *vb2_queue;
 
 unsigned int num_planes;
 
/* Private: internal use only */
 enum vb2_buffer_state state;
 
 struct list_head queued_entry;
 struct list_head done_entry;
 
 struct vb2_plane planes[VIDEO_MAX_PLANES];   // 存放實際數據的結構
};
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 plane_sizes[VIDEO_MAX_PLANES];
 
 unsigned int streaming:1;
 
 struct vb2_fileio_data *fileio;
};
在vb2中細化了鎖,而且將核心部分封裝,使咱們更容易使用。
與vb中有衝突的地方以下
1,結構中紅色部分都爲本身的數據,通常對於咱們的驅動程序來講不要使用,所以使用vb2_buffer時候不能(也不須要)直接指定vb->state 的內容。 好比說通知數據完成只須要調用 vb2_buffer_done(&vb2, VB2_BUF_STATE_DONE); 便可,
2,對於vb2_buffer,沒用供咱們使用的list,所以若是要將vb能夠放入list head,須要咱們本身添加list ,例如
struct vivi_buffer {
     /* common v4l buffer stuff -- must be first */
     struct vb2_buffer vb;
     struct list_head list;
};
使用vb2,總結以下 
    1,調用vb2_queue_init 初始化隊列  q 。
    2,調用reqbuf 時候會根據請求(v4l2_requestbuffers)分配vb2結構,而且加入到q->buf中
    3,調用querybuf時候,根據信息(v4l2_buffer)返回q->buf中對應的vb2_buffer的信息(v4l2_buffer)
    4,mmap上面信息對應的 vb空間到用戶空間
    5,調用qbuf 時,將對應的vb2_buffer ( vivi_bufer->list )添加到  q->queued_list 隊列中
    6,使用select 調用poll 休眠等待 q->done_list 有數據
    7, 調用qbuf 和 vidioc_streamon 時候都會查詢,若是這兩個條件都成立,則調用q->ops->buf_queue 將 核心中的vb2_buffer調如咱們寫的驅動中,放入一個列表,而後等待(上面的poll過程休眠)咱們的驅動程序將數據放入該vb2_buffer
    8, 數據存放完成後 調用vb2_buffer_done函數,即將上面有數據的vb2_buffer放入q->done_list中,而後喚醒上面poll休眠的進程
    9, poll喚醒後會調用dqbuf將q->done_list 中的vb2_buffer提出來後,將此vb2的信息(v4l2_buffer)返回
    10, 應用程序獲得buffer信息後,就去對應的mmap後的用戶空間中讀數據。
 
 
以上爲此次根據韋東山視頻步驟對vivi程序的分析,若有錯誤,忘指出
相關文章
相關標籤/搜索