V4L2(video 4 linux 2) 可以支持多種設備,它可以有以下幾種介面:
1. 視頻採集介面(video capture interface):這種應用的設備可以是高頻頭或者攝像頭.V4L2的最初設計就是應用於這種功能的.
2. 視頻輸出介面(video output interface):可以驅動計算機的外圍視頻圖像設備--像可以輸齣電視信號格式的設備.
3. 直接傳輸視頻介面(video overlay interface):它的主要工作是把從視頻採集設備採集過來的信號直接輸出到輸出設備之上,而不用經過系統的CPU.
4. 視頻間隔消隱信號介面(VBI interface):它可以使應用可以訪問傳輸消隱期的視頻信號.
5. 收音機介面(radio interface):可用來處理從AM或FM高頻頭設備接收來的音頻流.(由於只寫過FM的驅動下面著重講解這種應用.)
V4L2驅動的主要功能是使程序有發現設備的能力和操作設備.它主要是用過一系列的回調函數來實現這些功能.像設置高頻頭的頻率,幀頻,視頻壓縮格式和圖像像參數等等(在我寫的FM驅動中就主要是設置頻率,設置音量等).
一個視頻驅動很可能還要有處理PCI匯流排,或USB匯流排的部分,通常會有一個內部一I2C介面.然後還有一個V4L2的子系統介面.這個子系統是圍繞video_device這個結構體建立的,它代表的是一個V4L2設備.講解進入這個結構體的一切.
V4L2是一個兩層的驅動系統.頂層是videodev模塊,當videodev初始化時,它註冊一個主設備號為81的(參考Documentation/devices.txt)字元設備,同時設置字元設備的功能函數.所有的V4L2驅動都是videodev的client端,videodev通過V4L2驅動調用client驅動.當一個V4L2驅動初始化時,它會註冊每一個掛在videodev上的設備,這個設備通過videodev的包含V4L2驅動的結構體(此結構體包含V4L2的驅動方法、一個次設備號,以及其他兩個詳細信息)來掛在videodev上.V4L2的驅動方式很象通常的linux字元設備驅動,但是又有不同的參數.Videodev扮演這一個環繞在V4L2驅動周圍的薄殼的角色,videodev作為一個模塊實現,所有的V4L2驅動也是作為模塊實現.
當一個驅動初始化時,它列舉出所有的在系統中將要掛在它上面的設備.對於每個設備都有一個video_device結構體,並使用video_register_device()註冊此設備.
/*
* Newer version of video_device, handled by videodev2.c
* This version moves redundant code from video device code to
* the common handler
*/
struct video_device
{
/* device ops */
const struct v4l2_file_operations *fops;
/* sysfs */
struct device dev; /* v4l device */
struct cdev *cdev; /* character device */
/* Set either parent or v4l2_dev if your driver uses v4l2_device */
struct device *parent; /* device parent */
struct v4l2_device *v4l2_dev; /* v4l2_device parent */
/* device info */
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;
/* attribute to differentiate multiple indices on one physical device */
int index;
int debug; /* Activates debug level*/
/* Video standard vars */
v4l2_std_id tvnorms; /* Supported tv norms */
v4l2_std_id current_norm; /* Current tvnorm */
/* callbacks */
void (*release)(struct video_device *vdev);
/* ioctl callbacks */
const struct v4l2_ioctl_ops *ioctl_ops;
};
以我寫的FM為例看三個結構體:
static const struct v4l2_ioctl_ops si470x_ioctl_ops = {
.vidioc_querycap = si470x_vidioc_querycap,
.vidioc_queryctrl = si470x_vidioc_queryctrl,
.vidioc_g_ctrl = si470x_vidioc_g_ctrl,
.vidioc_s_ctrl = si470x_vidioc_s_ctrl,
.vidioc_g_audio = si470x_vidioc_g_audio,
.vidioc_g_tuner = si470x_vidioc_g_tuner,
.vidioc_s_tuner = si470x_vidioc_s_tuner,
.vidioc_g_frequency = si470x_vidioc_g_frequency,
.vidioc_s_frequency = si470x_vidioc_s_frequency,
.vidioc_s_hw_freq_seek = si470x_vidioc_s_hw_freq_seek,
};
/*
* si470x_viddev_template - video device interface
*/
struct video_device si470x_viddev_template = {
.fops = &si470x_fops,
.name = DRIVER_NAME,
.release = video_device_release,
.ioctl_ops = &si470x_ioctl_ops,
};
const struct v4l2_file_operations si470x_fops = {
.owner = THIS_MODULE,
.ioctl = video_ioctl2,
.open = si470x_fops_open,
.release = si470x_fops_release,
};
在video_device結構體中,還有三組不同的函數指針集.第一組只包含一個函數,那就是release(),如果驅動沒有release()函數,內核就會抱怨.release()函數很重要:由於多種原因,對video_device的引用可以在一個應用關閉文件描述符后很長一段時間依然保持.它們甚至可以在設備己經註銷后依然保持.因此,在release()函數調用前,釋放這個結構體是不安全的.這個函數通常要包含一個簡單的kfree()調用.
video_device的file_operations結構體包含都是常規的函數指針.這個結構體是為註冊cdev而準備的,
video_register_driver(radio->videodev,VFL_TYPE_RADIO,radio_nr);---->
__video_register_device(vdev,type,nr,1)
{
1.check device type
name_base = 「radio」;
vdev->vfl_type = type;
vdev->cdev = NULL;
2.find a free minor,device node number and device index.
min_offset = 64,
min_cnt = 64,
dev_node_set(vdev);
3.Initialize the character device
vdev->cdev = cdev_alloc();
if (vdev->fops->unlocked_ioctl)
vdev->cdev->ops = &v4l2_unlocked_fops;
else
vdev->cdev->ops = &v4l2_fops
(這裡的v4l2_fops就會調用video_device里的.fops了)
vdev->cdev->owner = vdev->fops->owner;
ret = cdev_add(vdev->cdev,MKDEV(VIDEO_MAJOR,vdev->minor),1);
(這裡的主設備號就是81)
參考Documentation/devices.txt
81 char video4linux
0 = /dev/video0 Video capture/overlay device
...
63 = /dev/video63 Video capture/overlay device
64 = /dev/radio0 Radio device
...
127 = /dev/radio63 Radio device
192 = /dev/vtx0 Teletext device
...
223 = /dev/vtx31 Teletext device
224 = /dev/vbi0 Vertical blank interrupt
...
255 = /dev/vbi31 Vertical blank interrupt
4.register the device with sysfs
device_register(&vdev->dev);
5.Activate this minor.The char device can now be used
video_device[vdev->minor] = vdev;
}
視頻設備通常都包括open()和release()函數.注意:這裡所說的release函數並非上面所講到的同名的release()函數,這個release() 函數只要設備關閉就要調用.通常都還要有read()和write()函數,這取決於設備的功能是輸入還是輸出.然而我們要注意的是,對於視頻流設備而言,傳輸數據還有別的方法.多數處理視頻流數據的設備還需要實現poll()和mmap();每個V4L2設備都要有ioctl()函數,但是也可以使用V4L2子系統的video_ioctl2(); 在文件操作函數中,我們將IOCTL控制操作函數初始化為video_ioctl2函數,那麼,此設備所支持的IOCTL操作如何實現和被調用呢?
video_ioctl2函數根據IOCTL操作命令,會調用struct video_device結構體變數中定義的相關函數.這是驅動程序實現設備支持的IOCTL操作的方法.
video_ioctl2---->__video_do_ioctl()
{
struct video_device *vfd = video_devdata(file);
const struct v4l2_ioctl_ops *ops = vfd->ioctl_ops;
這裡的ioctl_ops就是驅動里video_device結構體里的.ioctl_ops
然後對應不同的命令,執行相應的操作.
}
第三組函數,const struct v4l2_ioctl_ops *ioctl_ops;、流輸入輸出和其他操作.
,從一開始就要知道的一個欄位就是debug.可以把它設成是V4L2_DEBUG_IOCTL或V4L2_DEBUG_IOCTL_ARG(或是兩個都設,這是個掩碼),可以生成很多的調試信息.
設備註冊:
一旦video_device己經配置好,就可以下面的函數註冊了:
int video_register_device(struct video_device *vfd, int type, int nr);
這裡vfd是設備的結構體(video_device),type的值與它的type欄位值相同,nr也是一樣,想要的子設備號(為-1則註冊時自動分配).返回值當為0,若返回的是負的出錯碼,則表明出錯了,和通常一樣,我們要知道,設備一旦註冊,它的函數可能就會立即調用,不到一切準備就緒,不要調用video_register_device();
type的值可以為以下之一:
VFL_TYPE_GRABBER 表明是一個圖像採集設備?包括攝像頭、調諧器,諸如此類.
VFL_TYPE_VBI 代表的設備是從視頻消隱的時間段取得信息的設備.
VFL_TYPE_RADIO 代表無線電設備.
VFL_TYPE_VTX 代表視傳設備.
設備的註銷方法為:
void video_unregister_device(struct video_device *vfd);
參考:Video for Linux Two Driver Writer's Guide
The Video4Linux2 API: an introduction
http://lwn.net/Articles/203924/
[火星人 ] Linux radio 設備驅動已經有830次圍觀