当前位置: 首页 > 工具软件 > Video4Linux2 > 使用案例 >

Video4Linux2 第二部分:注册和打开

丌官玺
2023-12-01

Video4Linux2 part 2: registration and open()

V4L2 第二部分:注册和打开

This is the second article in the LWN series on writing drivers for the Video4Linux2 kernel interface; those who have not yet seen the introductory articlemay wish to start there. This installment will look at the overall structure of a Video4Linux driver and the device registration process.

这是V4L2内核接口系列文章的第二篇;还没有看过第一篇的可以点击简介篇。这里将会探索V4L驱动的整体结构和设备注册过程。

 

Before starting, it is worth noting that there are two resources which will prove invaluable for anybody working with video drivers:

开始之前,介绍两种资源,它们对任何编写视频驱动的人都是无价的:

1. The V4L2 API Specification. This document covers the API from the user-space point of view, but, to a great extent, V4L2 drivers implement that API directly. So most of the structures are the same, and the semantics of the V4L2 calls are clearly laid out. Print a copy (consider cutting out the Free Documentation License text to save trees) and keep it somewhere within easy reach.

1. V4L2 API技术规格文档。该文档覆盖了用户空间视角的API,但是,如果展开来看,V4L2驱动是直接实现这些API的,对吧。因此大部分结构都是相同的,V4L2调用的语法清楚的罗列了出来。打印一份,放在触手可及的地方。(可以考虑把免费文档许可文本去掉,节约树木资源)

2. The "vivi" driver found in the kernel source as drivers/media/video/vivi.c. It is a virtual driver, in that it generates test patterns and does not actually interface to any hardware. As such, it serves as a relatively clear illustration of how V4L2 drivers should be written.

2. “vivi”驱动,你可以用在内核源码中找到,位置是 drivers/media/video/vivi.c。这是一个虚拟驱动,其中只是内置了一个测试模式,并不实际操作任何硬件。它清楚地演示了V4L2驱动应该如何编写。

 

To start, every V4L2 driver must include the requisite header file:

#include <linux/videodev2.h>

开始吧,每个V4L2驱动一定包含这个必需的头文件:

#include <linux/videodev2.h>

 

Much of the needed information is there. When digging through the headers as a driver author, however, you'll also want to have a look at include/media/v4l2-dev.h, which defines many of the structures you'll be working with.

里面有我们需要的大部分信息。作为驱动作者,你会想要挖掘一下这些个头文件,你一定也想要浏览一下include/media/v4l2-dev.h头文件,其中定义了很多的结构体,你会用到它们。

 

A video driver will probably have sections which deal with the PCI or USB bus (for example); we'll not spend much time on that part of the driver here. There is often an internal i2c interface, which will be examined later on in this article series. Then, there is the interface to the V4L2 subsystem. That interface is built around struct video_device, which represents a V4L2 device. Covering everything that goes into this structure will be the topic of several articles; here we'll just have an overview.

视频驱动很可能要跟PCI总线或者USB总线打交道;我们不想对这些部分花太多时间。还有I2C接口,我们会在后续的系列文章中讲到。接下来,是V4L2子系统接口。这个接口以结构体video_device为中心构建,video_device代表了一个V4L2设备。要完全覆盖这个结构体需要几篇文章,这里我们仅作概述。

 

The name field of struct video_device is a name for the type of device; it will appear in kernel log messages and in sysfs. The name usually matches the name of the driver.

结构体video_device的name成员,代表设备的类型名;这个名字会出现在内核日志消息里,和sysfs里。这个名字通常跟驱动名字配对。

 

There are two fields to describe what type of device is being represented. The first (type) looks like a holdover from the Video4Linux1 API; it can have one of four values:

VFL_TYPE_GRABBER indicates a frame grabber device - including cameras, tuners, and such.

VFL_TYPE_VBI is for devices which pull information transmitted during the video blanking interval.

VFL_TYPE_RADIO for radio devices.

VFL_TYPE_VTX for videotext devices.

还有两个成员描述了设备类型。第一个是type,看起来很像V4L1 API中的type,可以是下列4个值中的一个:

VFL_TYPE_GRABBER代表了一个帧捕获设备,包括摄像头,调谐器,诸如此类。

VFL_TYPE_VBI是一个设备,在视频隔行处理中进行信息转换。

VFL_TYPE_RADIO是一个无线电设备。

VFL_TYPE_VTX是一个视频文本设备。

 

If your device can perform more than one of the above functions, a separate V4L2 device should be registered for each of the supported functions. In V4L2, however, any of the registered devices can be called upon to function in any of the supported modes. What it comes down to is that, for V4L2, there is really only need for a single device, but compatibility with the older Video4Linux API requires that individual devices be registered for each function.

如果你的驱动可以支持不止一个功能,应该为每个功能注册一个V4L2设备。事实上,任何一个注册设备都可以调用其他的功能。为什么会这样呢?每个设备只满足一个需求,只是为了兼容V4L API。

 

The second field, called type2, is a bitmask describing the device's capabilities in more detail. It can contain any of the following values:

VID_TYPE_CAPTURE: the device can capture video data.

VID_TYPE_TUNER: it can tune to different frequencies.

VID_TYPE_TELETEXT: it can grab teletext data.

VID_TYPE_OVERLAY: it can overlay video data directly into the frame buffer.

VID_TYPE_CHROMAKEY: a special form of overlay capability where the video data is only displayed where the underlying frame buffer contains pixels of a specific color.

VID_TYPE_CLIPPING: it can clip overlay data.

VID_TYPE_FRAMERAM: it uses memory located in the frame buffer device.

VID_TYPE_SCALES: it can scale video data.

VID_TYPE_MONOCHROME: it is a monochrome-only device.

VID_TYPE_SUBCAPTURE: it can capture sub-areas of the image.

VID_TYPE_MPEG_DECODER: it can decode MPEG streams.

VID_TYPE_MPEG_ENCODER: it can encode MPEG streams.

VID_TYPE_MJPEG_DECODER: it can decode MJPEG streams.

VID_TYPE_MJPEG_ENCODER: it can encode MJPEG streams.

第二个成员,type2,是位掩码,更具体的描述了设备的能力集。它可以包含下述值:

VID_TYPE_CAPTURE:设备可以捕获视频数据。

VID_TYPE_TUNER:它可以调谐到不同频率。

VID_TYPE_TELETEXT:它可以抓取图文电视数据。

VID_TYPE_OVERLAY:它可以直接显示视频数据到显示缓冲区,不需要经过CPU。

VID_TYPE_CHROMAKEY:一种特殊的直接显示方式,视频数据只在显示缓冲区中含有特定颜色的像素区才实现。

VID_TYPE_CLIPPING:可以剪切直接显示数据。

VID_TYPE_FRAMERAM:它直接使用位于显示缓冲区的内存。

VID_TYPE_SCALES:它可以缩放视频数据。

VID_TYPE_MONOCHROME:单色设备。

VID_TYPE_SUBCAPTURE:可以捕获图像的子区域。

VID_TYPE_MPEG_DECODER:可以解码MPEG流。

VID_TYPE_MPEG_ENCODER:可以编码MPEG流。

VID_TYPE_MJPEG_DECODER:可以解码MJPEG 流。

VID_TYPE_MJPEG_ENCODER:可以编码MJPEG流。

 

Another field initialized by all V4L2 drivers is minor, which is the desired minor number for the device. Usually this field will be set to -1, which causes the Video4Linux subsystem to allocate a minor number at registration time.

下一个V4L2驱动要初始化的成员是minor,表示次设备号。通常这个成员被设为-1,这样的话,V4L子系统会在注册的时候动态分配一个次设备号。

 

There are also three distinct sets of function pointers found within struct video_device. The first, consisting of a single function, is the release() method. If a device lacks a release() function, the kernel will complain (your editor was amused to note that it refers offending programmers to an LWN article). The release() function is important: for various reasons, references to a video_device structure can remain long after that last video application has closed its file descriptor. Those references can remain after the device has been unregistered. For this reason, it is not safe to free the structure until the release() method has been called. So, often, this function consists of a simple kfree() call.

结构体video_device含有三组函数指针集合。第一组,只有一个函数,就是release()方法。如果一个设备没有release()方法,内核会抱怨。release()方法是重要的,因为视频应用程序关闭了文件描述符后,video_device结构体还会在内存里停留很久。因为这个原因,释放这个结构体是不安全的,除非release()方法调用之后。因此,通常是这样,这个函数里面仅有一个简单的kfree()函数调用。

 

The video_device structure contains within it a file_operations structure with the usual function pointers. Video drivers will always need open() and release() operations; note that thisrelease() is called whenever the device is closed, not when it can be freed as with the other function with the same name described above. There will often be a read() or write() method, depending on whether the device performs input or output; note, however, that for streaming video devices, there are other ways of transferring data. Most devices which handle streaming video data will need to implement poll() and mmap(). And every V4l2 device needs an ioctl() method - but they can use video_ioctl2(), which is provided by the V4L2 subsystem.

第二组,video_device结构体有一个file_operations结构体指针,其中内置了通常的函数指针。视频驱动总是需要open()和release()操作,注意这个release操作,只要设备关闭就会被调用,这跟上面介绍的同名函数不同。也会有read()或者write()方法,这由设备是否有输入输出能力;注意,这是针对流式视频设备而言的,还有另外的传输数据的方式。大部分设备处理流式视频数据需要实现poll()和mmap()方法。每一个V4L2设备都需要实现ioctl()方法,只是有些可以通过ioctl2()实现,这是由V4L2子系统提供的。

 

The third set of methods, stored in the video_device structure itself, makes up the core of the V4L2 API. There are several dozen of them, handling various device configuration operations, streaming I/O, and more.

第三组,直接保存在video_device结构体内,构成了V4L2 API接口的核心。有很多个这样的函数指针,处理各种设备配置操作,流化I/O,等等。

Finally, a useful field to know from the beginning is debug. Setting it to either (or both - it's a bitmask) of V4L2_DEBUG_IOCTL and V4L2_DEBUG_IOCTL_ARG will yield a fair amount of debugging output which can help a befuddled programmer figure out why a driver and an application are failing to understand each other.

最后,一个非常有用的成员变量是debug。这是个掩码,将它设置为V4L2_DEBUG_IOCTL,和V4L2_DEBUG_IOCTL_ARG,或者二者都设置。这会产生相当数量的调试输出,可以帮助定位问题,查看驱动和应用程序为什么不理解对方。

Video device registration

视频设备注册

Once the video_device structure has been set up, it should be registered with:

int video_register_device(struct video_device *vfd, int type, int nr);

video_device设置完成后,可以使用video_register_device函数注册它;

Here, vfd is the device structure, type is the same value found in its type field, and nr is, again, the desired minor number (or -1 for dynamic allocation). The return value should be zero; a negative error code indicates that something went badly wrong. As always, one should be aware that the device's methods can be called immediately once the device is registered; do not call video_register_device() until everything is ready to go.

这里,vfd是设备结构体指针,type就是结构体的type成员,nr呢,就是结构体的minor成员(设为-1代表动态分配)。返回值正常为0,负值表示发生了错误。一定要意识到:一旦设备注册了,那么该设备的方法就可能会被立刻调用;因此,不要调用video_register_device()除非一切都准备好了。

A device can be unregistered with:

void video_unregister_device(struct video_device *vfd);

Stay tuned for the next article in this series, which will begin to look at the implementation of some of these methods.

 使用video_unregister_device注销该设备。别走开,继续关注系列文章的下篇,我们会探索这些方法的实现。

 

open() and release()

open() 和and release()

Every V4L2 device will need an open() method, which will have the usual prototype:

int (*open)(struct inode *inode, struct file *filp);

The first thing an open() method will normally do is to locate an internal device corresponding to the given inode; this is done by keying on the minor number stored in inode. A certain amount of initialization can be performed; this can also be a good time to power up the hardware if it has a power-down option.

每个V4L2设备都需要一个open()方法,函数原型如下:

int (*open)(struct inode *inode, struct file *filp);

open()方法要做的第一件事情是:打开对应inode的内部设备;这是以inode内的minor次设备号为主键找到的。然后执行一些初始化操作;如果有下电选项,那么这时候也是上电的好时机。

 

The V4L2 specification defines some conventions which are relevant here. One is that, by design, all V4L2 devices can have multiple open file descriptors at any given time. The purpose here is to allow one application to display (or generate) video data while another one, perhaps, tweaks control values. So, while certain V4L2 operations (actually reading and writing video data, in particular) can be made exclusive to a single file descriptor, the device as a whole should support multiple open descriptors.

V4L2规格文档定义了一些惯例。第一个是,所有的V4L2设备都可以同时打开多个文件描述符。设计目的是为了允许一个应用程序显示(或者产生)视频数据,另一个应用程序可以同时调整控制参数。因此,某些特定V4L2操作(例如读写视频数据)可以独占单独一个文件描述符,事实上该设备是总体上支持同时打开多个文件描述符的。

 

Another convention worth mentioning is that the open() method should not, in general, make changes to the operating parameters currently set in the hardware. It should be possible to run a command-line program which configures a camera according to a certain set of desires (resolution, video format, etc.), then run an entirely separate application to, for example, capture a frame from the camera. This mode would not work if the camera's settings were reset in the middle, so a V4L2 driver should endeavor to keep existing settings until an application explicitly resets them.

另一个值得一提的惯例是open()方法不应该改变硬件当前的配置参数,通常而言。因为可能先运行一个命令行程序配置摄像头,设置一些特定参数(解析度,视频格式,等等),接着运行一个完整的独立应用程序,从摄像头捕获一帧。如果在两个应用程序中间,参数被重置了,那么这种模式就不能正常工作了,因此V4L2驱动应该致力于保持已有的设置,直到应用程序重新设置参数。

 

The release() method performs any needed cleanup. Since video devices can have multiple open file descriptors, release() will need to decrement a counter and check before doing anything radical. If the just-closed file descriptor was being used to transfer data, it may necessary to shut down the DMA engine and perform other cleanups.

release()执行一些必需的清除操作。既然视频设备可以同时有多个打开的文件描述符,release()应该递减一个计数器,在一些重要的操作前做检查。如果刚才被关闭的文件描述符是用来传输数据的,那么就要关闭DMA引擎,同时执行一些其他的清除操作。

 

The next installment in this series will start into the long process of querying device capabilities and configuring operating modes. Stay tuned.

系列文章的下一集开始了查询设备能力集合配置操作模式,这是一个很长的过程。别走开,广告之后更精彩。

 类似资料: