linux V4L2驅動中新舊版本下video buffer alloc與mmap的處理區別

  1. 首先需要說明目前在比較新的內核中已經採用了
vb2_queue與vb2_buffer來替代舊版本內核中經常使用到的
videobuf_queue與videobuf_buffer。
兩者主要用於對用戶層申請VIDIOC_REQBUF時的使用。
從用戶層Request的Memory的類型區分,典型的兩種是:
V4L2_MEMORY_USERPTR以及V4L2_MEMORY_MMAP,前者的內存主動權位於用戶層,即驅動中的視頻輸出內存地址由用戶層來提供,後者MMAP操作的內存緩存類型一般需要由驅動自己來實現內存的分配。

在舊版本內核中,假設需要實現兩種Video設備操作接口,分別對應兩種Memory_type, 則一般需要提供的ops接口如下:
V4L2_MEMORY_USERPTR:
static const struct v4l2_file_operations xxx_video0_fops= {
    .owner = THIS_MODULE,
    .open = xxx,
    .release = xxx,
    .poll = xxx,
    .unlocked_ioctl = xxx,
};

V4L2_MEMORY_MMAP:

static const struct v4l2_file_operations xxxx_video1_fops = {
    .owner = THIS_MODULE,
    .open = xxx,
    .release = xxx,
    .poll = xxx,
    .unlocked_ioctl = xxx,
    .mmap = xxx,
};
兩者的區別就是多了一個mmap操作的接口。
此外,對於舊版內核而言mmap的操作可以由內核提供的mmap方式來實現,一般的處理方式如下:
void videobuf_queue_dma_contig_init(struct videobuf_queue *q,
                    const struct videobuf_queue_ops *ops,
                    struct device *dev,
                    spinlock_t *irqlock,
                    enum v4l2_buf_type type,
                    enum v4l2_field field,
                    unsigned int msize,
                    void *priv,
                    struct mutex *ext_lock)
{
    videobuf_queue_core_init(q, ops, dev, irqlock, type, field, msize,
                 priv, &qops, ext_lock);
}

這裏通過內核提供的一個qops來完成對每一個videobuf_buffer的alloc_vb操作,該過程主要是申請一個videobuf_buffer但並不會直接申請存儲視頻圖像數據的內存塊,所以一般在Request_buffer的過程就是主要完成alloc_vb的操作,重點是記錄下申請的每個videobuf_buffer所在的index值,該過程對V4L2_MEMORY_USERPTR 和V4L2_MEMORY_MMAP 是一致的。
static struct videobuf_qtype_ops qops = {
    .magic        = MAGIC_QTYPE_OPS,

    .alloc_vb     = __videobuf_alloc_vb,
    .iolock       = __videobuf_iolock,
    .mmap_mapper  = __videobuf_mmap_mapper,
    .vaddr        = __videobuf_to_vaddr,
};
而對V4L2_MEMORY_MMAP的緩存區域而言,他需要內核完成buffer真正存儲數據的內存塊的申請,一般就是通過用戶層的mmap來完成的,而且是在這個mmap的過程中採取完成buffer區域的alloc操作,在低版本內核中通過__videobuf_mmap_mapper來實現:
mem->size = PAGE_ALIGN(buf->bsize);
    mem->vaddr = dma_alloc_coherent(q->dev, mem->size,
                    &mem->dma_handle, GFP_KERNEL);
在完成實際物理存儲區域的申請後,纔會再去執行一次mmap

  1. 對比舊版本中mmap的處理機制,在新版內核中需要注意的時,可不再使用上述的模式,對於V4L2_MEMORY_MMAP類型的vb2_buffer來而言直接在Request_buffer的過程中就需要完成對實際內存區域塊的申請vb2_reqbufs->__vb2_buf_mem_alloc(),而這個申請的接口在新的內核中通過實現vb2_mem_ops接口來完成,主要用於處理V4L2_MEMORY_MMAP
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);
};
該接口需要在驅動中進行實現,alloc一般完成實際物理內存區域塊的申請,而mmap是用於對用戶空間的mmap操作的接口響應。當然在新版本的內核中依舊還是支持上述1小節所描述的處理過程的,依舊可以採用這種方式來實現video相關的驅動。同理對於方式二這種處理方式也可以在舊內核中來實現,其根本是對vb2_queue的 videobuf_qtype_op的實現。

仔細閱讀源碼後,可以發現本質上1和2小節描述的處理過程的根本區別在於舊版本架構中mmap過程完成實際buffer物理內存的申請以及內存映射的操作,新版本是將alloc過程放在Requestbuffer的過程中,而將mmap映射放在實際的mmap操作中實現,。

總的來說,對於新舊版本之所以這樣處理的原因是,原因在於現在更多的採用V4L2_MEMORY_USERPTR類型的緩存數申請(主要是現在全新的Android系統中video申請緩存主要是來自GPU、共享內存/dev/ION等等區域塊,一般再由內存申請的話會增加驅動的複雜度),故根本不需要再去實現mmap的操作,故一般不需要再實現vb2_mem_ops接口。而如果還是採用舊版的處理方式那麼就需要videobuf_queue_dma_contig_init來提供一個videobuf_qtype_ops,因爲alloc_vb是舊版中無論是V4L2_MEMORY_MMAP還是V4L2_MEMORY_USERPTR 均要實現videobuf_qtype_ops
對比而言對於V4L2_MEMORY_USERPTR來說新版本可以減少一定的編碼量,無需vb2_mem_ops。

當然,架構的實現方式可以由多種多樣,本質的目的是在同樣的V4L2框架的基礎下讓Video設備可以正常工作即可。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章