----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
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()注册此设备。
struct video_device
{
const struct v4l2_file_operations
*fops;
struct device dev;
struct cdev *cdev;
struct device *parent;
struct v4l2_device *v4l2_dev;
char name[32];
int vfl_type;
int minor;
u16 num;
unsigned long flags;
int index;
int debug;
v4l2_std_id tvnorms;
v4l2_std_id current_norm;
void (*release)(struct video_device
*vdev);
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,
};
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