眾所周知,linux中可以采用靈活的多層次的驅(qū)動架構來對接口進行統(tǒng)一與抽象,最低層次的驅(qū)動總是直接面向硬件的,而最高層次的驅(qū)動在linux中被劃分為“面向字符設備、面向塊設備、面向網(wǎng)絡接口”三大類來進行處理,前兩類驅(qū)動在文件系統(tǒng)中形成類似文件的“虛擬文件”,又稱為“節(jié)點node”,這些節(jié)點擁有不同的名稱代表不同的設備,在目錄/dev下進行統(tǒng)一管理,系統(tǒng)調(diào)用函數(shù)如open、close、read等也與普通文件的操作有相似之處,這種接口的一致性是由VFS(虛擬文件系統(tǒng)層)抽象完成的。面向網(wǎng)絡接口的設備仍然在UNIX/Linux系統(tǒng)中被分配代表設備的名稱(如eth0),但是沒有映射入文件系統(tǒng)中,其驅(qū)動的調(diào)用方式也與文件系統(tǒng)的調(diào)用open、read等不同。
video4linux2(V4L2)是Linux內(nèi)核中關于視頻設備的中間驅(qū)動層,向上為Linux應用程序訪問視頻設備提供了通用接口,向下為linux中設備驅(qū)動程序開發(fā)提供了統(tǒng)一的V4L2框架。在Linux系統(tǒng)中,V4L2驅(qū)動的視頻設備(如攝像頭、圖像采集卡)節(jié)點路徑通常為/dev中的videoX,V4L2驅(qū)動對用戶空間提供“字符設備”的形式,主設備號為81,對于視頻設備,其次設備號為0-63。除此之外,次設備號為64-127的Radio設備,次設備號為192-223的是Teletext設備,次設備號為224-255的是VBI設備。由V4L2驅(qū)動的Video設備在用戶空間通過各種ioctl調(diào)用進行控制,并且可以使用mmap進行內(nèi)存映射。
linux內(nèi)核概略架構(source:《Linux Device Drivers? Edition 3》chaper1)
(上傳不了,看官去翻書...)
V4l2在linux中的驅(qū)動架構(Edit By Andrew):
可以看到從視頻輸入輸入硬件的整個驅(qū)動鏈被抽象成3個層次:
1、? 最底層是直接面向硬件的,驅(qū)動框架由v4l2提供。值得注意的是,往往該層驅(qū)動需要總線驅(qū)動的支持,比如常見的USB2.0總線。
2、? 中間層便是v4l2。這是v4l的第二版,由Bill Dirks最開始開發(fā),最終被收入標準內(nèi)核驅(qū)動樹。
3、? 上層是linux內(nèi)核三大驅(qū)動模塊之一的“字符設備驅(qū)動層”,因此最終視頻設備以文件系統(tǒng)中/dev目錄下的字符設備的面目出現(xiàn),并被應用程序使用。
V4L2的是V4L的第二個版本。原來的V4L被引入到Linux內(nèi)核2.1.x的開發(fā)周期后期。Video4Linux2修正了一些設計缺陷,并開始出現(xiàn)在2.5.X內(nèi)核,并在內(nèi)核2.6.38之后,取消了對第一個版本v4l的支持。Video4Linux2驅(qū)動程序包括Video4Linux1應用的兼容模式,但實際上,支持是不完整的,并建議V4L2的設備使用V4L2的模式。現(xiàn)在,該項目的DVB-Wiki托管在LinuxTV(--->http://linuxtv.org/wiki/index.php/Main_Page)的網(wǎng)站上。
要想了解 V4l2 有幾個重要的文檔是必須要讀的:
1、源碼Documentation/video4linux目錄下的V4L2-framework.txt和videobuf
2、V4L2的官方API文檔V4L2 API Specification
3、源碼drivers/media/video目錄下的sample程序vivi.c(虛擬視頻驅(qū)動程序,此代碼模擬一個真正的視頻設備V4L2 API)。
V4l2可以支持多種設備,它可以有以下幾種接口:
1. 視頻采集接口(video capture interface):這種應用的設備可以是高頻頭或者攝像頭.V4L2的最初設計就是應用于這種功能的.
2. 視頻輸出接口(video output interface):可以驅(qū)動計算機的外圍視頻圖像設備--像可以輸出電視信號格式的設備.
3. 直接傳輸視頻接口(video overlay interface):它的主要工作是把從視頻采集設備采集過來的信號直接輸出到輸出設備之上,而不用經(jīng)過系統(tǒng)的CPU.
4. 視頻間隔消隱信號接口(VBI interface):它可以使應用可以訪問傳輸消隱期的視頻信號.
5. 收音機接口(radio interface):可用來處理從AM或FM高頻頭設備接收來的音頻流.
Andrew附:值得注意的是,內(nèi)核底層驅(qū)動程序的支持情況往往是我們關心的。推薦這篇博文《Linux 下攝像頭驅(qū)動支持情況》?http://weijb0606.blog.163.com/blog/static/131286274201063152423963/?? 這里做一個簡要的總結:
1、? 由于在內(nèi)核2.6.38版本之后就已經(jīng)完全支持v4l2,且拋棄了第一版。如今(2012-11)的內(nèi)核版本已到3.3.6,且?guī)缀跛械囊曨l設備驅(qū)動都已過渡到新版,因此在開發(fā)程序的時候應該按照v4l2的版本API標準來開發(fā)。
2、? 一般市場上容易買到的“免驅(qū)”攝像頭就是符合UVC標準的,內(nèi)核都可以支持。
3、? 其他種類的如中星微的ZC3XX、Sunplus系列等,GSPCA一般能支持,內(nèi)核也是自帶的。
4、? 內(nèi)核支持的所有種類的視頻設備驅(qū)動,都可以在文件系統(tǒng)目錄/lib/modules/kernel%uname –r/kernel/driver/meida/video下找到。或者在編譯內(nèi)核時,一一列舉。
相關網(wǎng)站:
http://linuxtv.org
http://linuxtv.org/wiki/index.php/Main_Page? linuxTV網(wǎng)站是V4L-?(DVB) Digital Video Broadcasting的維護者。
http://www.ideasonboard.org/uvc/?linux UVC驅(qū)動主頁
http://mxhaard.free.fr/spca5xx.html?gspca驅(qū)動主頁
===========一些v4l2驅(qū)動層細節(jié)==========
——注意這些細節(jié)不是應用程序調(diào)用的,而是編寫驅(qū)動程序需要了解的,或者是關于v4l2本身的一些細節(jié)。
關于編寫應用程序的一些細節(jié)移步“?http://www.cnblogs.com/andrew-wang/archive/2012/11/14/2770701.html? ”。
所有的v4l2驅(qū)動程序有以下結構:
1)?每個設備包含設備狀態(tài)的實例結構。
2)?子設備的初始化和命令方式(如果有).
3)?創(chuàng)建V4L2的設備節(jié)點 (/dev/videoX, /dev/vbiX and /dev/radioX)和跟蹤設備節(jié)點的具體數(shù)據(jù)。
4)文件句柄特定的結構,包含每個文件句柄數(shù)據(jù);
5)?視頻緩沖處理。
V4L2 驅(qū)動核心
V4L2 的驅(qū)動源碼在drivers/media/video目錄下,主要核心代碼有:
v4l2-dev.c //linux版本2視頻捕捉接口,主要結構體 video_device 的注冊v4l2-common.c //在Linux操作系統(tǒng)體系采用低級別的操作一套設備structures/vectors的通用視頻設備接口。 //此文件將替換videodev.c的文件配備常規(guī)的內(nèi)核分配。v4l2-device.c //V4L2的設備支持。注冊v4l2_devicev4l22-ioctl.c //處理V4L2的ioctl命令的一個通用的框架。v4l2-subdev.c //v4l2子設備v4l2-mem2mem.c //內(nèi)存到內(nèi)存為Linux和videobuf視頻設備的框架。設備的輔助函數(shù),使用其源和目的地videobuf緩沖區(qū)。頭文件linux/videodev2.h、media/v4l2-common.h、media/v4l2-device.h、media/v4l2-ioctl.h、media/v4l2-dev.h、media/v4l2-ioctl.h等。
V4l2相關結構體
1.V4l2_device
struct V4l2_device{ /* DEV-> driver_data指向這個結構。 注:DEV可能是空的,如果沒有父設備是如同ISA設備。 */ struct device *dev; /* 用于跟蹤注冊的subdevs */ struct list_head subdevs; /*鎖定此結構體;可以使用的驅(qū)動程序以及如果這個結構嵌入到一個更大的結構。 */ spinlock_t lock; /* 獨特的設備名稱,默認情況下,驅(qū)動程序姓名+總線ID */ char name[V4L2_DEVICE_NAME_SIZE]; /*報告由一些子設備調(diào)用的回調(diào)函數(shù)。 */ void (*notify)(struct v4l2_subdev *sd, unsigned int notification, void *arg);};
v4l2_device注冊和注銷 ? ??
v4l2_device_register(struct device *dev, struct v4l2_device *v4l2_dev);
第一個參數(shù)‘dev’通常是一個pci_dev的struct device的指針,但它是ISA設備或一個設備創(chuàng)建多個PCI設備時這是罕見的DEV為NULL,因此makingit不可能聯(lián)想到一個特定的父母v4l2_dev。 您也可以提供一個notify()回調(diào)子設備,可以通過調(diào)用通知你的事件。取決于你是否需要設置子設備。一個子設備支持的任何通知必須在頭文件中定義 .
注冊時將初始化 v4l2_device 結構體. 如果 dev->driver_data字段是空, 它將連接到 v4l2_dev.
v4l2_device_unregister(struct v4l2_device *v4l2_dev);
注銷也將自動注銷設備所有子設備。
2.video_device ? (進行視頻編程時v4l的最重要也是最常用功能)
在/dev目錄下的設備節(jié)點使用的 struct video_device(v4l2_dev.h)創(chuàng)建。
struct video_device { /*設備操作函數(shù) */ const struct v4l2_file_operations *fops; /* 虛擬文件系統(tǒng) */ struct device dev; /* v4l 設備 */ struct cdev *cdev; /* 字符設備 */ struct device *parent; /*父設備 */ struct v4l2_device *v4l2_dev; /* v4l2_device parent */ /* 設備信息 */ char name[32]; int vfl_type; /* 'minor' is set to -1 if the registration failed */ int minor; u16 num; /* use bitops to set/clear/test flags */ unsigned long flags; /*屬性來區(qū)分一個物理設備上的多個索引 */ int index; /* V4L2 文件句柄 */ spinlock_t fh_lock; /*鎖定所有的 v4l2_fhs */ struct list_head fh_list; /* List of struct v4l2_fh */ int debug; /* Activates debug level*/ /* Video standard vars */ v4l2_std_id tvnorms; /* Supported tv norms */ v4l2_std_id current_norm; /* Current tvnorm */ /* 釋放的回調(diào)函數(shù) */ void (*release)(struct video_device *vdev); /* 控制的回調(diào)函數(shù) */ const struct v4l2_ioctl_ops *ioctl_ops; }
動態(tài)分配:
struct video_device *vdev = video_device_alloc();
結構體配置:
fops:設置這個v4l2_file_operations結構,file_operations的一個子集。v4l2_dev: 設置這個v4l2_device父設備
name:
ioctl_ops:使用v4l2_ioctl_ops簡化的IOCTL,然后設置v4l2_ioctl_ops結構。
lock:如果你想要做的全部驅(qū)動程序鎖定就保留為NULL。否則你給它一個指針指向一個mutex_lock結構體和任何v4l2_file_operations被調(diào)用之前核心應該釋放釋放鎖。
parent:一個硬件設備有多個PCI設備,都共享相同v4l2_device核心時,設置注冊使用NULL v4l2_device作為父設備結構。
flags:可選的。設置到V4L2_FL_USE_FH_PRIO如你想讓框架處理VIDIOC_G/ S_PRIORITY的ioctl。這就需要您使用結構v4l2_fh。這個標志最終會消失,一旦所有的驅(qū)動程序使用的核心優(yōu)先處理。但現(xiàn)在它必須明確設定。
如果使用v4l2_ioctl_ops,那么你應該設置。unlocked_ioctlvideo_ioctl2在v4l2_file_operations結構。
注冊/注銷 video_device:
video_register_device(struct video_device *vdev, int type, int nr); __video_register_device(struct video_device *vdev, int type, int nr,int warn_if_nr_in_use)
參數(shù):
vdev:我們要注冊的視頻設備結構。
type:設備類型注冊
nr:設備號(0==/dev/video0,1??== /dev/video1,...-1==釋放第一個)
warn_if_nr_in_use:如果所需的設備節(jié)點號碼已經(jīng)在使用另一個號碼代替選擇。
注冊程式分配次設備號和設備節(jié)點的數(shù)字根據(jù)請求的類型和注冊到內(nèi)核新設備節(jié)點。如果無法找到空閑次設備號或設備節(jié)點編號,或者如果設備節(jié)點注冊失敗,就返回一個錯誤。
video_unregister_device(struct video_device *vdev);
3.v4l2_subdev 子設備結構體
每個子設備驅(qū)動程序必須有一個v4l2_subdev結構。這個結構可以獨立簡單的設備或者如果需要存儲更多的狀態(tài)信息它可能被嵌入在一個更大的結構。由于子設備可以做很多不同的東西,你不想結束一個巨大的OPS結構其中只有少數(shù)的OPS通常執(zhí)行,函數(shù)指針進行排序按類別,每個類別都有其自己的OPS結構。頂層OPS結構包含的類別OPS結構,這可能是NULL如果在subdev驅(qū)動程序不支持任何從該類別指針。
struct v4l2_subdev { #if defined(CONFIG_MEDIA_CONTROLLER) struct media_entity entity; #endif struct list_head list; struct module *owner; u32 flags; struct v4l2_device *v4l2_dev; const struct v4l2_subdev_ops *ops; /* 從驅(qū)動程序中不要調(diào)用這些內(nèi)部操作函數(shù)! */ const struct v4l2_subdev_internal_ops *internal_ops; /*這個subdev控制處理程序。可能是NULL。 */ struct v4l2_ctrl_handler *ctrl_handler; /* 名字必須是唯一 */ char name[V4L2_SUBDEV_NAME_SIZE]; /* 可用于到類似subdevs組,值是驅(qū)動程序特定的 */ u32 grp_id; /* 私有數(shù)據(jù)的指針 */ void *dev_priv; void *host_priv; /* subdev 設備節(jié)點*/ struct video_device devnode; /* 事件的數(shù)量在打開的時候被分配 */ unsigned int nevents; };
4.v4l2_buffer 緩沖區(qū)結構體
struct v4l2_buffer { __u32 index; enum v4l2_buf_type type; __u32 byteSUSEd; __u32 flags; enum v4l2_field field; struct timeval timestamp; struct v4l2_timecode timecode; __u32 sequence; /* memory location */ enum v4l2_memory memory; union { __u32 offset; unsigned long userptr; } m; __u32 length; __u32 input; __u32 reserved;};
V4L2核心API提供了一套標準方法的用于處理視頻緩沖器(稱為“videobuf”)。這些方法允許驅(qū)動程序以一致的方式來實現(xiàn)read(),mmap()和overlay()。目前使用的設備上的視頻緩沖器,支持scatter/gather方法(videobuf-dma-SG),線性存取的DMA的(videobuf-DMA-contig),vmalloc分配的緩沖區(qū),主要用于在USB驅(qū)動程序(DMA緩沖區(qū)的方法videobuf-vmalloc)。
videobuf層的功能為一種V4L2驅(qū)動和用戶空間之間的粘合層。它可以處理存儲視頻幀緩沖區(qū)的分配和管理。有一組可用于執(zhí)行許多標準的POSIX I / O系統(tǒng)調(diào)用的功能,包括read(),poll()的,happily,mmap()。另一套功能可以用來實現(xiàn)大部分的V4L2的ioctl()調(diào)用相關的流式I/ O的,包括緩沖區(qū)分配,排隊和dequeueing,流控制。驅(qū)動作者使用videobuf規(guī)定了一些設計決定,但回收期在驅(qū)動器和一個V4L2的用戶空間API的貫徹實施在減少代碼的形式。
關于videobuf的層的更多信息,請參閱Documentation/video4linux/videobuf
Sample驅(qū)動源碼分析:vivi.c 虛擬視頻驅(qū)動程序
----- 此代碼模擬一個真正的視頻設備V4L2 API (位于drivers/media/video目錄下)
入口:+int __init vivi_init(void) + vivi_create_instance(i) /*創(chuàng)建設備*//**/。 + 分配一個vivi_dev的結構體 /*它嵌套這結構體v4l2_device 和video_device*/ + v4l2_device_register(NULL, &dev->v4l2_dev);/*注冊vivi_dev中的V4l2_device*/ + 初始化視頻的DMA隊列 + 初始化鎖 + video_device_alloc(); 動態(tài)分配video_device結構體 + 構建一個video_device結構體 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);設置驅(qū)動程序?qū)S袛?shù)據(jù) + 所有控件設置為其默認值 + list_add_tail(&dev->vivi_devlist, &vivi_devlist);添加到設備列表 + 構建 v4l2_file_operations 結構體vivi_fops 并實現(xiàn).open .release .read .poll .mmap函數(shù) ----- .ioctl 用標準的v4l2控制處理程序 + 構建 v4l2_ioctl_ops結構體 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); 訪問驅(qū)動程序?qū)S脭?shù)據(jù) + 分配+初始化句柄(vivi_fh)數(shù)據(jù) + 重置幀計數(shù)器 + videobuf_queue_vmalloc_init(); 初始化視頻緩沖隊列 + 開啟一個新線程用于開始和暫停 + 實現(xiàn)自定義的v4l2_ioctl_ops 函數(shù)
?
評論
查看更多