Video4Linux2 part 3: Basic ioctl() handling
V4L2 第三部分:基本ioctl()处理
Anybody who has spent any amount of time working through the Video4Linux2 API specification will have certainly noted that V4L2 makes heavy use of the ioctl() interface. Perhaps more than just about any other type of peripheral, video hardware has a vast number of knobs to tweak. Video streams have many parameters associated with them, and, often, there is quite a bit of processing done in the hardware. Trying to operate video hardware outside of its well-supported modes can lead to poor performance at best, and often no performance at all. So there is no alternative to exposing many of the hardware's features and quirks to the end application.
只要是在V4L2 API规格文档上面花费了一定数量时间的人,都一定会发现:V4L2使用了很多ioctl()接口。不仅外设有很多种,视频硬件也都有很多旋钮要需要我们去拧。视频流上关联了很多参数,相应地,硬件内部就需要做一点处理。试着让硬件工作在它支持良好的模式之外,会导致性能很差,通常没什么性能可言。因此,我们没有替代方案,只能暴露很多硬件特性和机关,给终端应用程序。
Traditionally, video drivers have included ioctl() functions of approximately the same length as a Neal Stephenson novel; while the functions often come to more satisfying conclusions than the novels, they do tend to drag a lot in the middle. So the V4L2 API was changed in 2.6.18; the interminable ioctl() function has been replaced with a large set of callbacks which implement the individual ioctl() functions. There are, in fact, 79 of them in 2.6.19-rc3. Fortunately, most drivers need not implement all - or even most - of the possible callbacks.
传统上,视频驱动包含的ioctl()函数差不多跟Neal Stephenson的小说一样长;这些函数比小说的效果更好:它们在中间故意拖拉的更长。因此V4L2 API从2.6.18开始更改了;这些没完没了的ioctl()函数被一组回调函数替代了,这些回调函数可以用来实现ioctl()的功能。事实上,2.6.19-rc3一共有79个回调函数。幸运的是,大多数驱动不需要实现全部回调,或者大部分回调函数都不需要去实现。
What has really happened is that the long ioctl() function has been moved into drivers/media/video/videodev.c. This code handles the movement of data between user and kernel space and dispatches individual ioctl() calls to the driver. To use it, the driver need only use video_ioctl2() as its ioctl() method in the video_device structure. Actually, most drivers should be able to use it as unlocked_ioctl() instead; the locking within the Video4Linux2 layer can handle it, and drivers should have proper locking in place as well.
事实上,大部分ioctl()函数已经被移到drivers/media/video/videodev.c中去了。这些代码处理了用户空间和内核空间数据的搬迁,和私有的ioctl()调度。要使用它,驱动只需使用video_ioctl2()作为它的ioctl()方法,对结构体video_device中的ioctl()进行赋值。事实上,大部分驱动也可以使用unlocked_ioctl()作为它的ioctl()方法;V4L2的加锁操作能够处理它,驱动本来就有合适的地方加锁的。
The first callback your driver is likely to implement is:
int (*vidioc_querycap)(struct file *file, void *priv, struct v4l2_capability *cap);This function handles the VIDIOC_QUERYCAPioctl(), which asks a simple "who are you and what can you do?" question. Implementing it is mandatory for V4L2 drivers. In this function, as with all other V4L2 callbacks, the priv argument is the contents of file->private_data field; the usual practice is to point it at the driver's internal structure representing the device at open() time.
第一个要实现的回调函数很可能是这个:
int (*vidioc_querycap)(struct file *file, void *priv, struct v4l2_capability *cap);
这个函数处理VIDIOC_QUERYCAPioctl(),它总是问“你是谁你能做什么”这个问题。实现它是必须的。这个函数内,priv入参就是 file->private_data成员变量,通常是指向驱动的内部的一个结构体,代表open()时打开的设备。
The driver should respond by filling in the structure cap and returning the usual "zero or negative error code" value. On successful return, the V4L2 layer will take care of copying the response back into user space.
驱动响应的时候,应该填充结构体cap,并且返回0或者负值的错误码。成功返回时,V4L2层负责将响应拷贝到用户空间。
The v4l2_capability structure (defined in <linux/videodev2.h>) looks like this:
struct v4l2_capability};
v4l2_capability结构体定义在<linux/videodev2.h>,如下所示:
struct v4l2_capability
{
__u8 driver[16]; /* i.e. "bttv" */
__u8 card[32]; /* i.e. "Hauppauge WinTV" */
__u8 bus_info[32]; /* "PCI:" + pci_name(pci_dev) */
__u32 version; /* should use KERNEL_VERSION() */
__u32 capabilities; /* Device capabilities */
__u32 reserved[4];
};
The driver field should be filled in with the name of the device driver, while the card field should have a description of the hardware behind this particular device. Not all drivers bother with the bus_info field; those that do usually use something like:
sprintf(cap->bus_info, "PCI:%s", pci_name(&my_dev));
driver这个成员变量应该填写设备驱动的名字,card成员变量应该填写紧随特定设备的硬件描述。不是所有的驱动都关注bus_info成员变量;关注该成员变量,可能会这么做:
sprintf(cap->bus_info, "PCI:%s", pci_name(&my_dev));