二十四、V4L2框架主要结构体分析和虚拟摄像头驱动编写

一、V4L2框架主要结构体分析

V4L2(video for linux version 2),是内核中视频设备的驱动框架,为上层访问视频设备提供统一接口。

V4L2整体框架如下图:

二十四、V4L2框架主要结构体分析和虚拟摄像头驱动编写

图中主要包括两层和三个结构体:

两层是:

1. v4l2驱动核心层:包含video_device和v4l2_device的分配、设置和注册

2. v4l2下层接口层:具体的底层传感器驱动,现在的摄像头可能支持硬件解码,这就需要在摄像头驱动下面提供解码器IC驱动

三个结构体是:

1. v4l2_device:

struct v4l2_device {
/* dev->driver_data points to this struct.
Note: dev might be NULL if there is no parent device
as is the case with e.g. ISA devices. */
struct device *dev;
/* used to keep track of the registered subdevs */
struct list_head subdevs;
/* lock this struct; can be used by the driver as well if this
struct is embedded into a larger struct. */
spinlock_t lock;
/* unique device name, by default the driver name + bus ID */
char name[V4L2_DEVICE_NAME_SIZE];
/* notify callback called by some sub-devices. */
void (*notify)(struct v4l2_subdev *sd,
unsigned int notification, void *arg);
/* The control handler. May be NULL. */
struct v4l2_ctrl_handler *ctrl_handler;
/* Device's priority state */
struct v4l2_prio_state prio;
/* BKL replacement mutex. Temporary solution only. */
struct mutex ioctl_lock;
/* Keep track of the references to this struct. */
struct kref ref;
/* Release function that is called when the ref count goes to 0. */
void (*release)(struct v4l2_device *v4l2_dev);
};

2. video_device:

struct video_device
{
const struct v4l2_file_operations *fops; /* 文件操作函数 */ struct device dev; /* v4l device */
struct cdev *cdev; /* character device */ struct device *parent; /* device parent */
struct v4l2_device *v4l2_dev; /* v4l2_device parent */ /* Control handler associated with this device node. May be NULL. */
struct v4l2_ctrl_handler *ctrl_handler;
/* Priority state. If NULL, then v4l2_dev->prio will be used. */
struct v4l2_prio_state *prio; /* device info */
char name[];
int vfl_type;
/* 'minor' is set to -1 if the registration failed */
int minor;
u16 num;
...
/* ioctl callbacks */
const struct v4l2_ioctl_ops *ioctl_ops; /* 控制函数 */ /* serialization lock */
struct mutex *lock;
};

3. v4l2-subdev:

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;
/* Never call these internal ops from within a driver! */
const struct v4l2_subdev_internal_ops *internal_ops;
/* The control handler of this subdev. May be NULL. */
struct v4l2_ctrl_handler *ctrl_handler;
/* name must be unique */
char name[V4L2_SUBDEV_NAME_SIZE];
/* can be used to group similar subdevs, value is driver-specific */
u32 grp_id;
/* pointer to private data */
void *dev_priv;
void *host_priv;
/* subdev device node */
struct video_device *devnode;
};

v4l2_device在v4l2框架中是v4l2_subdev的父设备

video_device是v4l2_device的上层结构体,存储v4l2_device数据和cdev,它会在/dev目录下生成设备节点文件

video_device的struct v4l2_ioctl_ops会在后面分析

在此我给出它的作用方便读者理解函数分析过程:

struct v4l2_ioctl_ops:它会在应用层使用ioctl()时根据传入的宏调用对应操作函数

二、核心层提供的注册函数

1. 注册和注销v4l2_device:

/* 注册 */
int v4l2_device_register(struct device *dev, struct v4l2_device *v4l2_dev) /* 注销 */
void v4l2_device_unregister(struct v4l2_device *v4l2_dev)

注册函数调用关系如下:

/* 将设备与v4l2_device捆绑,并把v4l2设备放到driver data中
* 我们可以使用dev_get_drvdata(dev)获取到v4l2设备
*/
v4l2_device_register(&intf->dev, &dev->vdev)
-> dev_set_drvdata(dev, v4l2_dev);
-> dev->p->driver_data = data; /* device->device_private为v4l2_device */

2. 注册和注销v4l2_subdev:

/* 注册 */
int v4l2_device_register_subdev(struct v4l2_device *v4l2_dev, struct v4l2_subdev *sd) /* 注销 */
void v4l2_device_unregister_subdev(struct v4l2_subdev *sd)

注册函数调用关系如下:

v4l2_device_register_subdev()
/* struct v4l2_ctrl_handler用于控制设备的亮度、饱和度等 */
-> v4l2_ctrl_add_handler(v4l2_dev->ctrl_handler, sd->ctrl_handler);
-> handler_new_ref(hdl, ctrl); /* 设置控制属性 */
-> v4l2_ctrl_new_std(hdl, NULL, class_ctrl, , , , )
-> v4l2_ctrl_fill(id, &name, &type, &min, &max, &step, &def, &flags);
-> v4l2_ctrl_new(hdl, ops, ...); /* ops为v4l2_ctrl_ops,定义控制函数 */
-> list_add_tail(&ctrl->node, &hdl->ctrls);
-> list_add_tail(&sd->list, &v4l2_dev->subdevs);

v4l2_subdev注册函数主要设置了设备的控制属性和控制函数,并把设备放入列表中

控制函数结构体定义如下,读者有印象即可

struct v4l2_ctrl_ops {
int (*g_volatile_ctrl)(struct v4l2_ctrl *ctrl); /* 获取控制器值 */
int (*try_ctrl)(struct v4l2_ctrl *ctrl); /* 测试控制器值是否有效 */
int (*s_ctrl)(struct v4l2_ctrl *ctrl); /* 设置控制器值 */
};

3. 注册和注销video_device:

/* 注册 */
static inline int __must_check video_register_device(struct video_device *vdev, int type, int nr) /* 注销 */
void video_unregister_device(struct video_device *vdev)

注册函数调用关系如下:

video_register_device(vdev, VFL_TYPE_GRABBER, -);
-> __video_register_device(vdev, type, nr, , vdev->fops->owner);
-> case VFL_TYPE_GRABBER: /* 摄像头设备 */
-> name_base = "video";
-> case VFL_TYPE_GRABBER:
-> minor_offset = ;
-> minor_cnt = ;
-> vdev->minor = i + minor_offset;
-> vdev->cdev = cdev_alloc();
-> vdev->cdev->ops = &v4l2_fops;
-> cdev_add(vdev->cdev, MKDEV(VIDEO_MAJOR, vdev->minor), ); /* 依据次设备号分配cdev */
-> dev_set_name(&vdev->dev, "%s%d", name_base, vdev->num); /* 设备名为video%d */
-> device_register(&vdev->dev);
-> device_add(dev);

video_register_device()函数主要分配、设置并注册了cdev和device。

三、v4l2_device和video_device的file_operations分析

分析到这里,产生了两个问题:

1. register_chrdev_region()和创建class在哪里被调用的?

2. v4l2_fops函数都做了什么?

我们先来决解第一个问题,两函数应该在初始化框架时被调用,初始化函数定义在v4l2-dev.c文件中:

 static int __init videodev_init(void)
{
dev_t dev = MKDEV(VIDEO_MAJOR, ); /* VIDEO_MAJOR = 81 */
int ret; ret = register_chrdev_region(dev, VIDEO_NUM_DEVICES, VIDEO_NAME);
...
ret = class_register(&video_class);
...
return ;
}

现在我们来看v4l2_fops的定义:

static const struct file_operations v4l2_fops = {
.owner = THIS_MODULE,
.read = v4l2_read,
.write = v4l2_write,
.open = v4l2_open,
.get_unmapped_area = v4l2_get_unmapped_area,
.mmap = v4l2_mmap,
.unlocked_ioctl = v4l2_ioctl,
.release = v4l2_release,
.poll = v4l2_poll,
.llseek = no_llseek,
};

首先从open()函数分析:

 static int v4l2_open(struct inode *inode, struct file *filp)
{
struct video_device *vdev;
int ret = ;
...
vdev = video_devdata(filp);
...
if (vdev->fops->open) {
...
if (video_is_registered(vdev))
ret = vdev->fops->open(filp); /* 最终会调用video_device->fops的open()函数 */
...
}
...
}

read()和其他函数亦是如此:

 static ssize_t v4l2_read(struct file *filp, char __user *buf, size_t sz, loff_t *off)
{
struct video_device *vdev = video_devdata(filp);
...
if (video_is_registered(vdev))
ret = vdev->fops->read(filp, buf, sz, off);
...
} static long v4l2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
struct video_device *vdev = video_devdata(filp);
int ret = -ENODEV; if (vdev->fops->unlocked_ioctl) {
...
if (video_is_registered(vdev))
ret = vdev->fops->unlocked_ioctl(filp, cmd, arg);
...
}
...
}

可以看到v4l2_fops定义的函数在底层都会调用video_device的fops函数,因此我们可以在SI4内核项目中搜索“struct v4l2_file_operations ”:

二十四、V4L2框架主要结构体分析和虚拟摄像头驱动编写

在此我以drivers/staging/media/solo6x10/v4l2.c文件为例分析

若读者使用它其他文件分析,可能在后面会遇到struct vb2_buffer、struct vb2_queue、struct vb2_ops和struct vb2_mem_ops,其实vb2就是videobuf2,是videobuf的新版本,在此不做介绍

文件链接:

https://files.cnblogs.com/files/Lioker/24_v4l2_xawtv.zip

static const struct v4l2_file_operations solo_v4l2_fops = {
.owner = THIS_MODULE,
.open = solo_v4l2_open,
.release = solo_v4l2_release,
.read = solo_v4l2_read,
.poll = solo_v4l2_poll,
.mmap = solo_v4l2_mmap,
.ioctl = video_ioctl2,
};

首先从open()函数分析:

solo_v4l2_open
-> INIT_LIST_HEAD(&fh->vidq_active); /* 初始化用于存放queue的list_head */
-> solo_start_thread(fh); /* 启动线程查询是否有数据 */
-> solo_thread /* 等待数据 */
-> DECLARE_WAITQUEUE(wait, current);
-> add_wait_queue(&solo_dev->disp_thread_wait, &wait);
-> schedule_timeout_interruptible(HZ);
-> solo_thread_try(fh); /* 如果有数据 */
/* 取出等待队列头的第一个队列 */
-> struct videobuf_buffer * vb = list_first_entry(&fh->vidq_active, struct videobuf_buffer, queue);
-> list_del(&vb->queue);
-> solo_fillbuf(fh, vb);
-> wake_up(&vb->done); /* 唤醒上层读写操作 */
-> remove_wait_queue(&solo_dev->disp_thread_wait, &wait);
-> videobuf_queue_sg_init(&fh->vidq,
&solo_video_qops, /* solo_video_qops为struct videobuf_queue_ops */
..., &fh->slock, /* fh->slock为spinlock_t用于保护队列 */
V4L2_BUF_TYPE_VIDEO_CAPTURE, /* 是摄像头 */
SOLO_DISP_PIX_FIELD,
sizeof(struct videobuf_buffer), fh,...);

函数所使用的struct videobuf_queue、struct videobuf_queue_ops和struct videobuf_buffer会在后面分析

在此我给出它们的作用方便读者理解函数分析过程:

struct videobuf_queue:在数据传输过程中,首先把buffer放入queue,然后把queue放入list_head中,读写函数会通过操作list_head来操作queue中的buffer

struct videobuf_queue_ops:提供设置videobuf_buffer函数,添加queue到list_head函数,释放内存函数等

struct videobuf_buffer:用于数据传输,是数据传输的基本单位

函数所使用的videobuf_queue_sg_init()函数用于初始化queue,除此函数外,还有两函数可以用来初始化queue:

void videobuf_queue_vmalloc_init(struct videobuf_queue *q,
struct videobuf_queue_ops *ops,
struct device *dev,
spinlock_t *irqlock,
enum v4l2_buf_type type,
enum v4l2_field field,
unsigned int msize,
void *priv); void videobuf_queue_dma_contig_init(struct videobuf_queue *q,
struct videobuf_queue_ops *ops,
struct device *dev,
spinlock_t *irqlock,
enum v4l2_buf_type type,
enum v4l2_field field,
unsigned int msize,
void *priv);

videobuf_queue_sg_init():物理上和虚拟地址都是离散的buffer

videobuf_queue_vmalloc_init():物理上离散的,但虚拟地址是连续的buffer

videobuf_queue_dma_contig_init():物理上和虚拟地址都是连续的buffer

read()函数的调用关系如下:

solo_v4l2_read
/* 从videobuf_queue fh->vidq取出数据 */
-> videobuf_read_stream(&fh->vidq, data, count, ppos, , file->f_flags & O_NONBLOCK);
-> q->read_buf = list_entry(q->stream.next, struct videobuf_buffer, stream);
-> list_del(&q->read_buf->stream);

ioctl()函数的调用关系如下:

video_ioctl2
/* 根据cmd,调用__video_do_ioctl()将数据拷贝到内核空间 */
-> __video_do_ioctl
-> struct v4l2_ioctl_ops *ops = vfd->ioctl_ops;
-> case VIDIOC_QUERYCAP: /* 设备支持的能力 */
-> ops->vidioc_querycap(file, fh, cap);
-> case VIDIOC_G_FMT: /* 获取格式、分辨率等 */
-> ps->vidioc_g_fmt_vid_cap(file, fh, f);
-> case VIDIOC_QBUF: /* 把缓存区放入传输队列 */
-> ops->vidioc_qbuf(file, fh, p);
-> case VIDIOC_DQBUF: /* 把缓存从队列中取出 */
-> ops->vidioc_dqbuf(file, fh, p);
-> case VIDIOC_STREAMON: /* 启动视频传输 */
-> ops->vidioc_streamon(file, fh, i);
-> case VIDIOC_STREAMOFF: /* 关闭视频传输 */
-> ops->vidioc_streamoff(file, fh, i);

分析完了这两个问题,下面我们来分析v4l2.c的init()函数

四、v4l2.c的init()函数分析

init()函数的调用关系:

solo_v4l2_init
/* 初始化open()等函数使用的wait_queue_head_t */
-> init_waitqueue_head(&solo_dev->disp_thread_wait);
/* 分配、设置和注册video_device */
-> solo_dev->vfd = video_device_alloc();
-> *solo_dev->vfd = solo_v4l2_template; /* struct video_device */
-> video_register_device(solo_dev->vfd, VFL_TYPE_GRABBER, video_nr);

根据上述过程,我们可以得到下图:

二十四、V4L2框架主要结构体分析和虚拟摄像头驱动编写

五、数据传输和控制结构体分析

我在前几节有几个结构体没有介绍,在此我还是以v4l2.c文件的实例进行介绍

数据控制相关:

1. struct v4l2_ioctl_ops

static const struct v4l2_ioctl_ops solo_v4l2_ioctl_ops = {
/* 表示它是一个摄像头设备 */
.vidioc_querycap = solo_querycap,
/* 列举、获取、设置标准
* 列举、获取标准使用的是
* video_device的tvnorms和current_norm
*/
.vidioc_s_std = solo_s_std,
/* 列举、获取、设置输入源 */
.vidioc_enum_input = solo_enum_input,
.vidioc_s_input = solo_set_input,
.vidioc_g_input = solo_get_input,
/* 列举、获得、测试、设置摄像头的数据格式 */
.vidioc_enum_fmt_vid_cap = solo_enum_fmt_cap,
.vidioc_try_fmt_vid_cap = solo_try_fmt_cap,
.vidioc_s_fmt_vid_cap = solo_set_fmt_cap,
.vidioc_g_fmt_vid_cap = solo_get_fmt_cap,
/* 缓冲区操作:申请、查询、放入队列、取出队列 */
.vidioc_reqbufs = solo_reqbufs,
.vidioc_querybuf = solo_querybuf,
.vidioc_qbuf = solo_qbuf,
.vidioc_dqbuf = solo_dqbuf,
/* 启动、停止视频流 */
.vidioc_streamon = solo_streamon,
.vidioc_streamoff = solo_streamoff,
/* Controls */
.vidioc_queryctrl = solo_disp_queryctrl,
.vidioc_g_ctrl = solo_disp_g_ctrl,
.vidioc_s_ctrl = solo_disp_s_ctrl,
};

数据传输相关:

1. struct videobuf_buffer

struct videobuf_buffer {
unsigned int i;
u32 magic; /* info about the buffer */
unsigned int width; /* 摄像头图像像素长度 */
unsigned int height; /* 宽度 */
unsigned int bytesperline; /* 一行的字节数 */
unsigned long size; /* size = height * bytesperline */
unsigned int input;
enum v4l2_field field;
enum videobuf_state state; /* 用于存储状态 */
struct list_head stream; /* QBUF/DQBUF list */ /* touched by irq handler */
struct list_head queue;
wait_queue_head_t done;
unsigned int field_count;
struct timeval ts; /* Memory type */
enum v4l2_memory memory; /* buffer size */
size_t bsize; /* buffer offset (mmap + overlay) */
size_t boff; /* buffer addr (userland ptr!) */
unsigned long baddr; /* for mmap'ed buffers */
struct videobuf_mapping *map; /* Private pointer to allow specific methods to store their data */
int privsize;
void *priv;
};

2. struct videobuf_queue

struct videobuf_queue {
struct mutex vb_lock;
struct mutex *ext_lock;
spinlock_t *irqlock;
struct device *dev; wait_queue_head_t wait; /* wait if queue is empty */ enum v4l2_buf_type type;
unsigned int inputs; /* for V4L2_BUF_FLAG_INPUT */
unsigned int msize;
enum v4l2_field field;
enum v4l2_field last; /* for field=V4L2_FIELD_ALTERNATE */
struct videobuf_buffer *bufs[VIDEO_MAX_FRAME];
const struct videobuf_queue_ops *ops;
struct videobuf_qtype_ops *int_ops; unsigned int streaming:;
unsigned int reading:; /* capture via mmap() + ioctl(QBUF/DQBUF) */
struct list_head stream; /* capture via read() */
unsigned int read_off;
struct videobuf_buffer *read_buf; /* driver private data */
void *priv_data;
};

3. struct videobuf_queue_ops

static struct videobuf_queue_ops solo_video_qops = {
/* 被v4l2_ioctl_ops->vidioc_reqbufs调用,用于重新调整count和size */
.buf_setup = solo_buf_setup,
/* 被v4l2_ioctl_ops->vidioc_qbuf调用,用于设置videobuf_buffer成员 */
.buf_prepare = solo_buf_prepare,
/* 被v4l2_ioctl_ops->vidioc_qbuf调用,用于把queue放到list_head */
.buf_queue = solo_buf_queue,
/* 被v4l2_ioctl_ops->vidioc_streamoff调用,用于释放内存 */
.buf_release = solo_buf_release,
};

当开始流I/O时,摄像头的图像会在应用和驱动间传输,它可能会有三个状态:

1. 在驱动的传入队列中,用户空间通过ioctl(..., VIDIOC_QBUF, ...)填充缓冲区,把缓冲区放入队列。

2. 在驱动的传出队列中,缓冲区已经填充了视频数据,等待用户空间读取

3. 在用户空间的队列中,用户空间已经通过ioctl(..., VIDIOC_DQBUF, ...)读取缓冲区了,此时驱动无法访问缓冲区

这三种状态的切换如下图:

二十四、V4L2框架主要结构体分析和虚拟摄像头驱动编写

六、虚拟摄像头驱动vivi.c虚拟机测试

本节我通过应用程序xawtv对vivi.c的系统调用的关系进行分析,我们需要做的有以下几步:

1. 执行$ sudo apt-get install xawtv在虚拟机中安装xawtv

2. 在网站https://www.kraxel.org/releases/xawtv/下载源码xawtv-3.95.tar.gz,并创建SI4工程

3. 虚拟机连接摄像头,执行$ ls /dev/video*可以看到生成了/dev/video0,执行xawtv即可看到图像

4. 执行$ uname -a,根据虚拟机内核版本去http://ftp.sjtu.edu.cn/sites/ftp.kernel.org/pub/linux/kernel/下载同版本的内核

二十四、V4L2框架主要结构体分析和虚拟摄像头驱动编写

5. 解压后把drivers/media/video目录单独取出,修改它的Makefile为:

KERN_DIR = /work/vmware/tools/linux-4.8

all:
make -C $(KERN_DIR) M=`pwd` modules clean:
make -C $(KERN_DIR) M=`pwd` modules clean
rm -rf modules.order obj-m += vivi.o
obj-m += videobuf-core.o
obj-m += videobuf-vmalloc.o
obj-m += v4l2-common.o

6. 编译后,由于vivi.ko模块涉及较多文件,为防止有模块未被载入,我们可以使用$ sudo modprobe vivi加载内核自带vivi驱动及其所需框架和文件,然后依次执行$ sudo rmmod vivi和$ sudo insmod ./vivi.ko安装我们编译的vivi模块

7. 执行$ ls /dev/video*查看虚拟摄像头,然后执行$ xawtv -c /dev/videox打开虚拟摄像头

七、ioctl()执行过程和v4l2_ioctl_ops必需函数指针的确定

在可以看到图像后,我们就可以通过应用程序xawtv对应用程序ioctl()执行过程进行分析。

查看程序系统调用过程可以使用strace命令实现:

$ strace -o xawtv.log xawtv    /* 执行xawtv命令,调用过程会存储在xawtv.log文件中 */

xawtv.log文件会放在上面的百度网盘分享链接中

首先,我们搜索“open("/dev/video”,我们只需要关注第二次open(),因为第一次open()后面有close(),可以看到文件描述符为4

二十四、V4L2框架主要结构体分析和虚拟摄像头驱动编写

接下来,我们搜索“(4, ”,查看有哪些对应文件描述符的操作:

 fstat64(, {st_mode=S_IFREG|, st_size=, ...}) =
read(, "\1\0\0\fbook-desktop\0\0010\0\22MIT-MAGIC-C"..., ) =
read(, "", ) =
fstat64(, {st_mode=S_IFREG|, st_size=, ...}) =
read(, "#\t$XdotOrg: lib/X11/nls/locale.a"..., ) =
read(, "\t\t\t\t\tbg_BG.UTF-8\nbn_IN.utf8\t\t\t\t\t"..., ) =
read(, "8859-15\t\t\t\tde_CH.ISO8859-15\nde_C"..., ) =
read(, "_ZA.ISO8859-1\nen_ZA.ISO-8859-1\t\t"..., ) =
read(, "85915\t\t\t\teu_ES.ISO8859-15\neu_ES."..., ) =
read(, "14\ngv_GB.ISO-8859-14\t\t\t\tgv_GB.IS"..., ) =
read(, "w_GB.ISO8859-1\nkw_GB.ISO-8859-1\t"..., ) =
read(, ".ISO8859-1\noc\t\t\t\t\t\toc_FR.ISO8859"..., ) =
read(, "r_RS.UTF-8\nsr_YU.UTF-8@cyrillic\t"..., ) =
read(, "yi_US.CP1255\nzh_CN\t\t\t\t\t\tzh_CN.gb"..., ) =
read(, ":00 dawes Exp $\n#\n\nPOSIX:\t\t\t\t\t\tC"..., ) =
read(, "4:\t\t\t\tbr_FR.ISO8859-14\nbr_FR.ISO"..., ) =
read(, "de_DE.88591.en:\t\t\t\t\tde_DE.ISO885"..., ) =
read(, "59-1:\t\t\t\ten_ZA.ISO8859-1\nen_ZA.I"..., ) =
read(, "_ES.ISO8859-1\neu_ES.iso88591:\t\t\t"..., ) =
read(, "F-8\ngu_IN.utf8:\t\t\t\t\tgu_IN.UTF-8\n"..., ) =
read(, "o_KR.utf8:\t\t\t\t\tko_KR.UTF-8\nKO_KR"..., ) =
read(, "o_NO.utf8:\t\t\t\t\tno_NO.UTF-8\nnr:\t\t"..., ) =
read(, "O8859-2\nsl_SI.iso88592:\t\t\t\t\tsl_S"..., ) =
read(, "N\nvi_VN.tcvn5712:\t\t\t\t\tvi_VN.TCVN"..., ) =
read(, "9-9\nturkish.iso88599:\t\t\t\ttr_TR.I"..., ) =
read(, "", ) =
fstat64(, {st_mode=S_IFREG|, st_size=, ...}) =
read(, "#\t$XdotOrg: lib/X11/nls/locale.d"..., ) =
read(, "59-1/XLC_LOCALE\t\t\tes_AR.ISO8859-"..., ) =
read(, "_FR.ISO8859-1\niso8859-15/XLC_LOC"..., ) =
read(, "8/XLC_LOCALE\t\t\tbe_BY.UTF-8\nen_US"..., ) =
read(, "en_US.UTF-8/XLC_LOCALE\t\t\tlo_LA.U"..., ) =
fstat64(, {st_mode=S_IFREG|, st_size=, ...}) =
read(, "# $Xorg: C,v 1.3 2000/08/17 19:"..., ) =
read(, "", ) =
fstat64(, {st_mode=S_IFREG|, st_size=, ...}) =
read(, "#\t$XdotOrg: lib/X11/nls/locale.a"..., ) =
read(, "\t\t\t\t\tbg_BG.UTF-8\nbn_IN.utf8\t\t\t\t\t"..., ) =
read(, "8859-15\t\t\t\tde_CH.ISO8859-15\nde_C"..., ) =
read(, "_ZA.ISO8859-1\nen_ZA.ISO-8859-1\t\t"..., ) =
read(, "85915\t\t\t\teu_ES.ISO8859-15\neu_ES."..., ) =
read(, "14\ngv_GB.ISO-8859-14\t\t\t\tgv_GB.IS"..., ) =
read(, "w_GB.ISO8859-1\nkw_GB.ISO-8859-1\t"..., ) =
read(, ".ISO8859-1\noc\t\t\t\t\t\toc_FR.ISO8859"..., ) =
read(, "r_RS.UTF-8\nsr_YU.UTF-8@cyrillic\t"..., ) =
read(, "yi_US.CP1255\nzh_CN\t\t\t\t\t\tzh_CN.gb"..., ) =
read(, ":00 dawes Exp $\n#\n\nPOSIX:\t\t\t\t\t\tC"..., ) =
read(, "4:\t\t\t\tbr_FR.ISO8859-14\nbr_FR.ISO"..., ) =
read(, "de_DE.88591.en:\t\t\t\t\tde_DE.ISO885"..., ) =
read(, "59-1:\t\t\t\ten_ZA.ISO8859-1\nen_ZA.I"..., ) =
read(, "_ES.ISO8859-1\neu_ES.iso88591:\t\t\t"..., ) =
read(, "F-8\ngu_IN.utf8:\t\t\t\t\tgu_IN.UTF-8\n"..., ) =
read(, "o_KR.utf8:\t\t\t\t\tko_KR.UTF-8\nKO_KR"..., ) =
read(, "o_NO.utf8:\t\t\t\t\tno_NO.UTF-8\nnr:\t\t"..., ) =
read(, "O8859-2\nsl_SI.iso88592:\t\t\t\t\tsl_S"..., ) =
read(, "N\nvi_VN.tcvn5712:\t\t\t\t\tvi_VN.TCVN"..., ) =
read(, "9-9\nturkish.iso88599:\t\t\t\ttr_TR.I"..., ) =
read(, "", ) =
fstat64(, {st_mode=S_IFREG|, st_size=, ...}) =
read(, "#\t$XdotOrg: lib/X11/nls/locale.d"..., ) =
read(, "59-1/XLC_LOCALE\t\t\tes_AR.ISO8859-"..., ) =
read(, "_FR.ISO8859-1\niso8859-15/XLC_LOC"..., ) =
read(, "8/XLC_LOCALE\t\t\tbe_BY.UTF-8\nen_US"..., ) =
read(, "en_US.UTF-8/XLC_LOCALE\t\t\tlo_LA.U"..., ) =
fstat64(, {st_mode=S_IFREG|, st_size=, ...}) =
read(, "# $Xorg: C,v 1.3 2000/08/17 19:"..., ) =
read(, "", ) =
fstat64(, {st_mode=S_IFREG|, st_size=, ...}) =
read(, "# Locale name alias data base.\n#"..., ) =
read(, "", ) =
fstat64(, {st_mode=S_IFREG|, st_size=, ...}) =
fstat64(, {st_mode=S_IFREG|, st_size=, ...}) =
fstat64(, {st_mode=S_IFREG|, st_size=, ...}) =
fstat64(, {st_mode=S_IFREG|, st_size=, ...}) =
fstat64(, {st_mode=S_IFREG|, st_size=, ...}) =
fstat64(, {st_mode=S_IFREG|, st_size=, ...}) =
fstat64(, {st_mode=S_IFREG|, st_size=, ...}) =
fstat64(, {st_mode=S_IFDIR|, st_size=, ...}) =
fstat64(, {st_mode=S_IFREG|, st_size=, ...}) =
fstat64(, {st_mode=S_IFREG|, st_size=, ...}) =
fstat64(, {st_mode=S_IFREG|, st_size=, ...}) =
fstat64(, {st_mode=S_IFREG|, st_size=, ...}) =
fstat64(, {st_mode=S_IFREG|, st_size=, ...}) =
fstat64(, {st_mode=S_IFREG|, st_size=, ...}) =
fstat64(, {st_mode=S_IFREG|, st_size=, ...}) =
read(, "#\t$XdotOrg: lib/X11/nls/locale.a"..., ) =
read(, "\t\t\t\t\tbg_BG.UTF-8\nbn_IN.utf8\t\t\t\t\t"..., ) =
read(, "8859-15\t\t\t\tde_CH.ISO8859-15\nde_C"..., ) =
read(, "_ZA.ISO8859-1\nen_ZA.ISO-8859-1\t\t"..., ) =
read(, "85915\t\t\t\teu_ES.ISO8859-15\neu_ES."..., ) =
read(, "14\ngv_GB.ISO-8859-14\t\t\t\tgv_GB.IS"..., ) =
read(, "w_GB.ISO8859-1\nkw_GB.ISO-8859-1\t"..., ) =
read(, ".ISO8859-1\noc\t\t\t\t\t\toc_FR.ISO8859"..., ) =
read(, "r_RS.UTF-8\nsr_YU.UTF-8@cyrillic\t"..., ) =
read(, "yi_US.CP1255\nzh_CN\t\t\t\t\t\tzh_CN.gb"..., ) =
read(, ":00 dawes Exp $\n#\n\nPOSIX:\t\t\t\t\t\tC"..., ) =
read(, "4:\t\t\t\tbr_FR.ISO8859-14\nbr_FR.ISO"..., ) =
read(, "de_DE.88591.en:\t\t\t\t\tde_DE.ISO885"..., ) =
read(, "59-1:\t\t\t\ten_ZA.ISO8859-1\nen_ZA.I"..., ) =
read(, "_ES.ISO8859-1\neu_ES.iso88591:\t\t\t"..., ) =
read(, "F-8\ngu_IN.utf8:\t\t\t\t\tgu_IN.UTF-8\n"..., ) =
read(, "o_KR.utf8:\t\t\t\t\tko_KR.UTF-8\nKO_KR"..., ) =
read(, "o_NO.utf8:\t\t\t\t\tno_NO.UTF-8\nnr:\t\t"..., ) =
read(, "O8859-2\nsl_SI.iso88592:\t\t\t\t\tsl_S"..., ) =
read(, "N\nvi_VN.tcvn5712:\t\t\t\t\tvi_VN.TCVN"..., ) =
read(, "9-9\nturkish.iso88599:\t\t\t\ttr_TR.I"..., ) =
read(, "", ) =
fstat64(, {st_mode=S_IFREG|, st_size=, ...}) =
read(, "#\t$XdotOrg: lib/X11/nls/locale.a"..., ) =
read(, "\t\t\t\t\tbg_BG.UTF-8\nbn_IN.utf8\t\t\t\t\t"..., ) =
read(, "8859-15\t\t\t\tde_CH.ISO8859-15\nde_C"..., ) =
read(, "_ZA.ISO8859-1\nen_ZA.ISO-8859-1\t\t"..., ) =
read(, "85915\t\t\t\teu_ES.ISO8859-15\neu_ES."..., ) =
read(, "14\ngv_GB.ISO-8859-14\t\t\t\tgv_GB.IS"..., ) =
read(, "w_GB.ISO8859-1\nkw_GB.ISO-8859-1\t"..., ) =
read(, ".ISO8859-1\noc\t\t\t\t\t\toc_FR.ISO8859"..., ) =
read(, "r_RS.UTF-8\nsr_YU.UTF-8@cyrillic\t"..., ) =
read(, "yi_US.CP1255\nzh_CN\t\t\t\t\t\tzh_CN.gb"..., ) =
read(, ":00 dawes Exp $\n#\n\nPOSIX:\t\t\t\t\t\tC"..., ) =
read(, "4:\t\t\t\tbr_FR.ISO8859-14\nbr_FR.ISO"..., ) =
read(, "de_DE.88591.en:\t\t\t\t\tde_DE.ISO885"..., ) =
fstat64(, {st_mode=S_IFREG|, st_size=, ...}) =
read(, "#\t$XdotOrg: lib/X11/nls/locale.d"..., ) =
read(, "59-1/XLC_LOCALE\t\t\tes_AR.ISO8859-"..., ) =
read(, "_FR.ISO8859-1\niso8859-15/XLC_LOC"..., ) =
read(, "8/XLC_LOCALE\t\t\tbe_BY.UTF-8\nen_US"..., ) =
read(, "en_US.UTF-8/XLC_LOCALE\t\t\tlo_LA.U"..., ) =
read(, "LE:\t\t\taf_ZA.ISO8859-1\niso8859-15"..., ) =
read(, "LE:\t\t\tes_MX.ISO8859-1\niso8859-1/"..., ) =
read(, "9-2/XLC_LOCALE:\t\t\tpl_PL.ISO8859-"..., ) =
read(, " byn_ER.UTF-8\nen_US.UT"..., ) =
fstat64(, {st_mode=S_IFREG|, st_size=, ...}) =
read(, "# $XFree86: xc/nls/XLC_LOCALE/e"..., ) =
read(, "GB2312.1980-0:GL; GB2312.1980-0:"..., ) =
read(, "", ) =
fstat64(, {st_mode=S_IFREG|, st_size=, ...}) =
read(, "\n! -----------------------------"..., ) =
fcntl64(, F_GETFD) = 0x1 (flags FD_CLOEXEC)
getdents64(, /* 19 entries */, ) =
getdents64(, /* 0 entries */, ) =
read(, "\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3\0\3\0\1\0\0\0\240\3\0\0004\0\0\0"..., ) =
fstat64(, {st_mode=S_IFREG|, st_size=, ...}) =
read(, "\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3\0\3\0\1\0\0\0\320\v\0\0004\0\0\0"..., ) =
fstat64(, {st_mode=S_IFREG|, st_size=, ...}) =
read(, "\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3\0\3\0\1\0\0\0\220\3\0\0004\0\0\0"..., ) =
fstat64(, {st_mode=S_IFREG|, st_size=, ...}) =
read(, "\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3\0\3\0\1\0\0\0\240\17\0\0004\0\0\0"..., ) =
fstat64(, {st_mode=S_IFREG|, st_size=, ...}) =
read(, "\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3\0\3\0\1\0\0\0000'\0\0004\0\0\0"..., ) =
fstat64(, {st_mode=S_IFREG|, st_size=, ...}) =
fstat64(, {st_mode=S_IFREG|, st_size=, ...}) =
read(, "\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3\0\3\0\1\0\0\0\0\17\0\0004\0\0\0"..., ) =
fstat64(, {st_mode=S_IFREG|, st_size=, ...}) =
read(, "\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3\0\3\0\1\0\0\0@\22\0\0004\0\0\0"..., ) =
fstat64(, {st_mode=S_IFREG|, st_size=, ...}) =
read(, "\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3\0\3\0\1\0\0\0\360\30\0\0004\0\0\0"..., ) =
fstat64(, {st_mode=S_IFREG|, st_size=, ...}) =
read(, "\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3\0\3\0\1\0\0\0\0\6\0\0004\0\0\0"..., ) =
fstat64(, {st_mode=S_IFREG|, st_size=, ...}) =
read(, "\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3\0\3\0\1\0\0\0\240\4\0\0004\0\0\0"..., ) =
fstat64(, {st_mode=S_IFREG|, st_size=, ...}) =
read(, "\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3\0\3\0\1\0\0\0P\4\0\0004\0\0\0"..., ) =
fstat64(, {st_mode=S_IFREG|, st_size=, ...}) =
read(, "\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3\0\3\0\1\0\0\0`\4\0\0004\0\0\0"..., ) =
fstat64(, {st_mode=S_IFREG|, st_size=, ...}) =
read(, "\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3\0\3\0\1\0\0\0\240\3\0\0004\0\0\0"..., ) =
fstat64(, {st_mode=S_IFREG|, st_size=, ...}) =
read(, "\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3\0\3\0\1\0\0\0\240\3\0\0004\0\0\0"..., ) =
fstat64(, {st_mode=S_IFREG|, st_size=, ...}) =
read(, "\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3\0\3\0\1\0\0\0\200\10\0\0004\0\0\0"..., ) =
fstat64(, {st_mode=S_IFREG|, st_size=, ...}) =
read(, "\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3\0\3\0\1\0\0\0\260\n\0\0004\0\0\0"..., ) =
fstat64(, {st_mode=S_IFREG|, st_size=, ...}) =
fstat64(, {st_mode=S_IFREG|, st_size=, ...}) =
read(, "\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3\0\3\0\1\0\0\0\3405\0\0004\0\0\0"..., ) =
fstat64(, {st_mode=S_IFREG|, st_size=, ...}) =
read(, "\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3\0\3\0\1\0\0\0\220\r\0\0004\0\0\0"..., ) =
fstat64(, {st_mode=S_IFREG|, st_size=, ...}) =
read(, "\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3\0\3\0\1\0\0\0\220\10\0\0004\0\0\0"..., ) =
fstat64(, {st_mode=S_IFREG|, st_size=, ...}) =
read(, "\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3\0\3\0\1\0\0\0\240\7\0\0004\0\0\0"..., ) =
fstat64(, {st_mode=S_IFREG|, st_size=, ...}) =
fstat64(, {st_mode=S_IFREG|, st_size=, ...}) =
read(, "! $Xorg: XKeysymDB,v 1.3 2000/08"..., ) =
ioctl(, VIDIOC_QUERYCAP or VT_OPENQRY, 0x9b40998) = - EINVAL (Invalid argument)
ioctl(, VIDIOC_QUERYCAP or VT_OPENQRY, 0xbfaccd44) =
ioctl(, VIDIOC_G_FMT or VT_SENDSIG, 0xbfaccc78) =
ioctl(, VIDIOC_ENUM_FMT or VT_SETMODE, 0xbfaccbec) =
ioctl(, 0xc02c564a, 0xbfaccb58) = - EINVAL (Invalid argument)
ioctl(, VIDIOC_ENUM_FMT or VT_SETMODE, 0xbfaccbec) =
ioctl(, 0xc02c564a, 0xbfaccb58) = - EINVAL (Invalid argument)
ioctl(, VIDIOC_ENUM_FMT or VT_SETMODE, 0xbfaccbec) =
ioctl(, 0xc02c564a, 0xbfaccb58) = - EINVAL (Invalid argument)
ioctl(, VIDIOC_ENUM_FMT or VT_SETMODE, 0xbfaccbec) =
ioctl(, VIDIOC_ENUM_FMT or VT_SETMODE, 0xbfaccbec) =
ioctl(, VIDIOC_ENUM_FMT or VT_SETMODE, 0xbfaccbec) =
ioctl(, VIDIOC_ENUM_FMT or VT_SETMODE, 0xbfaccbec) = - EINVAL (Invalid argument)
ioctl(, VIDIOC_QUERYCAP or VT_OPENQRY, 0xbfaccb84) =
ioctl(, VIDIOC_G_INPUT, 0xbfacca2c) =
ioctl(, VIDIOC_ENUMINPUT, 0xbfacca2c) =
fstat64(, {st_mode=S_IFCHR|, st_rdev=makedev(, ), ...}) =
ioctl(, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0xbfacca78) = - EINVAL (Invalid argument)
ioctl(, VIDIOC_QUERYCAP or VT_OPENQRY, 0x9b40998) =
fcntl64(, F_SETFD, FD_CLOEXEC) =
ioctl(, VIDIOC_ENUMINPUT, 0x9b40acc) =
ioctl(, VIDIOC_ENUMINPUT, 0x9b40b18) =
ioctl(, VIDIOC_ENUMINPUT, 0x9b40b64) =
ioctl(, VIDIOC_ENUMINPUT, 0x9b40bb0) =
ioctl(, VIDIOC_ENUMINPUT, 0x9b40bfc) = - EINVAL (Invalid argument)
ioctl(, VIDIOC_ENUMSTD, 0x9b40f8c) =
ioctl(, VIDIOC_ENUMSTD, 0x9b40fcc) =
ioctl(, VIDIOC_ENUMSTD, 0x9b4100c) =
ioctl(, VIDIOC_ENUMSTD, 0x9b4104c) =
ioctl(, VIDIOC_ENUMSTD, 0x9b4108c) =
ioctl(, VIDIOC_ENUMSTD, 0x9b410cc) =
ioctl(, VIDIOC_ENUMSTD, 0x9b4110c) =
ioctl(, VIDIOC_ENUMSTD, 0x9b4114c) = - EINVAL (Invalid argument)
ioctl(, VIDIOC_ENUM_FMT or VT_SETMODE, 0x9b4138c) =
ioctl(, VIDIOC_ENUM_FMT or VT_SETMODE, 0x9b413cc) =
ioctl(, VIDIOC_ENUM_FMT or VT_SETMODE, 0x9b4140c) =
ioctl(, VIDIOC_ENUM_FMT or VT_SETMODE, 0x9b4144c) =
ioctl(, VIDIOC_ENUM_FMT or VT_SETMODE, 0x9b4148c) =
ioctl(, VIDIOC_ENUM_FMT or VT_SETMODE, 0x9b414cc) =
ioctl(, VIDIOC_G_PARM, 0x9b40a00) =
ioctl(, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b41b8c) =
ioctl(, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b41bd0) =
ioctl(, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b41c14) =
ioctl(, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b41c58) =
ioctl(, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b41c9c) = - EINVAL (Invalid argument)
ioctl(, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b41ce0) =
ioctl(, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b41d24) = - EINVAL (Invalid argument)
ioctl(, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b41d68) = - EINVAL (Invalid argument)
ioctl(, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b41dac) = - EINVAL (Invalid argument)
ioctl(, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b41df0) = - EINVAL (Invalid argument)
ioctl(, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b41e34) = - EINVAL (Invalid argument)
ioctl(, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b41e78) = - EINVAL (Invalid argument)
ioctl(, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b41ebc) = - EINVAL (Invalid argument)
ioctl(, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b41f00) = - EINVAL (Invalid argument)
ioctl(, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b41f44) = - EINVAL (Invalid argument)
ioctl(, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b41f88) = - EINVAL (Invalid argument)
ioctl(, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b41fcc) = - EINVAL (Invalid argument)
ioctl(, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b42010) = - EINVAL (Invalid argument)
ioctl(, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b42054) = - EINVAL (Invalid argument)
ioctl(, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b42098) = - EINVAL (Invalid argument)
ioctl(, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b420dc) = - EINVAL (Invalid argument)
ioctl(, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b42120) = - EINVAL (Invalid argument)
ioctl(, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b42164) = - EINVAL (Invalid argument)
ioctl(, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b421a8) = - EINVAL (Invalid argument)
ioctl(, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b421ec) = - EINVAL (Invalid argument)
ioctl(, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b42230) = - EINVAL (Invalid argument)
ioctl(, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b42274) = - EINVAL (Invalid argument)
ioctl(, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b422b8) = - EINVAL (Invalid argument)
ioctl(, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b422fc) = - EINVAL (Invalid argument)
ioctl(, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b42340) = - EINVAL (Invalid argument)
ioctl(, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b42384) = - EINVAL (Invalid argument)
ioctl(, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b423c8) = - EINVAL (Invalid argument)
ioctl(, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b4240c) = - EINVAL (Invalid argument)
ioctl(, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b42450) = - EINVAL (Invalid argument)
ioctl(, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b42494) = - EINVAL (Invalid argument)
ioctl(, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b424d8) = - EINVAL (Invalid argument)
ioctl(, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b4251c) = - EINVAL (Invalid argument)
ioctl(, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b42560) = - EINVAL (Invalid argument)
ioctl(, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b425a4) = - EINVAL (Invalid argument)
ioctl(, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b425e8) = - EINVAL (Invalid argument)
ioctl(, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b4262c) = - EINVAL (Invalid argument)
ioctl(, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b42670) = - EINVAL (Invalid argument)
ioctl(, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b426b4) = - EINVAL (Invalid argument)
ioctl(, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b426f8) = - EINVAL (Invalid argument)
ioctl(, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b4273c) = - EINVAL (Invalid argument)
ioctl(, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b42780) = - EINVAL (Invalid argument)
ioctl(, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b427c4) = - EINVAL (Invalid argument)
ioctl(, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b42808) = - EINVAL (Invalid argument)
ioctl(, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b4284c) = - EINVAL (Invalid argument)
ioctl(, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b42890) = - EINVAL (Invalid argument)
ioctl(, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b428d4) = - EINVAL (Invalid argument)
ioctl(, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b42918) = - EINVAL (Invalid argument)
ioctl(, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b4295c) = - EINVAL (Invalid argument)
ioctl(, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b429a0) = - EINVAL (Invalid argument)
ioctl(, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b429e4) = - EINVAL (Invalid argument)
ioctl(, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b42a28) = - EINVAL (Invalid argument)
ioctl(, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b42a6c) = - EINVAL (Invalid argument)
ioctl(, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b42ab0) = - EINVAL (Invalid argument)
ioctl(, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b42af4) = - EINVAL (Invalid argument)
ioctl(, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b42b38) = - EINVAL (Invalid argument)
ioctl(, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b42b7c) = - EINVAL (Invalid argument)
ioctl(, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b42bc0) = - EINVAL (Invalid argument)
ioctl(, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b42c04) = - EINVAL (Invalid argument)
ioctl(, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b42c48) = - EINVAL (Invalid argument)
ioctl(, VIDIOC_G_STD, 0xbfacce08) =
ioctl(, VIDIOC_G_INPUT, 0xbfacce1c) =
ioctl(, MATROXFB_G_TVOCTRL or VIDIOC_G_CTRL, 0xbfacce14) =
ioctl(, MATROXFB_G_TVOCTRL or VIDIOC_G_CTRL, 0xbfacce14) =
ioctl(, MATROXFB_G_TVOCTRL or VIDIOC_G_CTRL, 0xbfacce14) =
ioctl(, MATROXFB_G_TVOCTRL or VIDIOC_G_CTRL, 0xbfacce14) =
ioctl(, MATROXFB_G_TVOCTRL or VIDIOC_G_CTRL, 0xbfacce24) =
ioctl(, VIDIOC_TRY_FMT, 0x9b42ca4) =
ioctl(, VIDIOC_S_FMT or VT_RELDISP, 0xbfacc754) =
ioctl(, VIDIOC_TRY_FMT, 0x9b42ca4) =
ioctl(, VIDIOC_REQBUFS or VT_DISALLOCATE, 0x9b42d80) =
ioctl(, VIDIOC_QUERYBUF or VT_RESIZE, 0x9b42d94) =
ioctl(, VIDIOC_QUERYBUF or VT_RESIZE, 0x9b42dd8) =
ioctl(, VIDIOC_QBUF, 0x9b42d94) =
ioctl(, VIDIOC_QBUF, 0x9b42dd8) =
ioctl(, VIDIOC_STREAMON, 0xbfacca2c) =
ioctl(, MATROXFB_S_TVOCTRL or VIDIOC_S_CTRL, 0xbfacce10) =
ioctl(, MATROXFB_S_TVOCTRL or VIDIOC_S_CTRL, 0xbfacce10) =
ioctl(, MATROXFB_S_TVOCTRL or VIDIOC_S_CTRL, 0xbfacce10) =
ioctl(, MATROXFB_S_TVOCTRL or VIDIOC_S_CTRL, 0xbfacce10) =
ioctl(, VIDIOC_S_INPUT, 0xbfacce84) =
ioctl(, VIDIOC_S_STD, 0x9b40f90) =
ioctl(, VIDIOC_DQBUF, 0xbfaccd00) =
ioctl(, VIDIOC_QBUF, 0x9b42d94) =
ioctl(, VIDIOC_DQBUF, 0xbfaccd00) =
ioctl(, VIDIOC_QBUF, 0x9b42dd8) =
ioctl(, VIDIOC_DQBUF, 0xbfaccd00) =
ioctl(, VIDIOC_QBUF, 0x9b42d94) =
ioctl(, VIDIOC_DQBUF, 0xbfaccd00) =
ioctl(, VIDIOC_QBUF, 0x9b42dd8) =
ioctl(, VIDIOC_DQBUF, 0xbfaccd00) =
ioctl(, VIDIOC_QBUF, 0x9b42d94) =
ioctl(, VIDIOC_DQBUF, 0xbfaccd00) =
ioctl(, VIDIOC_QBUF, 0x9b42dd8) =
ioctl(, VIDIOC_DQBUF, 0xbfaccd00) =
ioctl(, VIDIOC_QBUF, 0x9b42d94) =
ioctl(, VIDIOC_DQBUF, 0xbfaccd00) =
ioctl(, VIDIOC_QBUF, 0x9b42dd8) =
ioctl(, VIDIOC_DQBUF, 0xbfaccd00) =
ioctl(, VIDIOC_QBUF, 0x9b42d94) =
ioctl(, VIDIOC_DQBUF, 0xbfaccd00) =
ioctl(, VIDIOC_QBUF, 0x9b42dd8) =
ioctl(, VIDIOC_DQBUF, 0xbfaccd00) =
ioctl(, VIDIOC_QBUF, 0x9b42d94) =
ioctl(, VIDIOC_DQBUF, 0xbfaccd00) =
ioctl(, VIDIOC_QBUF, 0x9b42dd8) =
ioctl(, VIDIOC_DQBUF, 0xbfaccd00) =
ioctl(, VIDIOC_QBUF, 0x9b42d94) =
ioctl(, VIDIOC_DQBUF, 0xbfaccd00) =
ioctl(, VIDIOC_QBUF, 0x9b42dd8) =
ioctl(, VIDIOC_DQBUF, 0xbfaccd00) =
ioctl(, VIDIOC_QBUF, 0x9b42d94) =
ioctl(, VIDIOC_DQBUF, 0xbfaccd00) =
ioctl(, VIDIOC_QBUF, 0x9b42dd8) =
ioctl(, VIDIOC_DQBUF, 0xbfaccd00) =
ioctl(, VIDIOC_QBUF, 0x9b42d94) =
ioctl(, VIDIOC_DQBUF, 0xbfaccd00) =
ioctl(, VIDIOC_QBUF, 0x9b42dd8) =
ioctl(, VIDIOC_DQBUF, 0xbfaccd00) =
ioctl(, VIDIOC_QBUF, 0x9b42d94) =
ioctl(, VIDIOC_DQBUF, 0xbfaccd00) =
ioctl(, VIDIOC_QBUF, 0x9b42dd8) =
ioctl(, VIDIOC_DQBUF, 0xbfaccd00) =
ioctl(, VIDIOC_QBUF, 0x9b42d94) =
ioctl(, VIDIOC_DQBUF, 0xbfaccd00) =
ioctl(, VIDIOC_QBUF, 0x9b42dd8) =
ioctl(, VIDIOC_DQBUF, 0xbfaccd00) =
ioctl(, VIDIOC_QBUF, 0x9b42d94) =
ioctl(, VIDIOC_DQBUF, 0xbfaccd00) =
ioctl(, VIDIOC_QBUF, 0x9b42dd8) =
ioctl(, VIDIOC_DQBUF, 0xbfaccd00) =
ioctl(, VIDIOC_QBUF, 0x9b42d94) =
ioctl(, VIDIOC_DQBUF, 0xbfaccd00) =
ioctl(, VIDIOC_QBUF, 0x9b42dd8) =
ioctl(, VIDIOC_DQBUF, 0xbfaccd00) =
ioctl(, VIDIOC_QBUF, 0x9b42d94) =
ioctl(, VIDIOC_DQBUF, 0xbfaccd00) =
ioctl(, VIDIOC_QBUF, 0x9b42dd8) =
ioctl(, VIDIOC_DQBUF, 0xbfaccd00) =
ioctl(, VIDIOC_QBUF, 0x9b42d94) =
ioctl(, VIDIOC_DQBUF, 0xbfaccd00) =
ioctl(, VIDIOC_QBUF, 0x9b42dd8) =
ioctl(, VIDIOC_DQBUF, 0xbfaccd00) =
ioctl(, VIDIOC_QBUF, 0x9b42d94) =
ioctl(, VIDIOC_DQBUF, 0xbfaccd00) =
ioctl(, VIDIOC_QBUF, 0x9b42dd8) =
ioctl(, VIDIOC_DQBUF, 0xbfaccd00) =
ioctl(, VIDIOC_QBUF, 0x9b42d94) =
ioctl(, VIDIOC_DQBUF, 0xbfaccd00) =
ioctl(, VIDIOC_QBUF, 0x9b42dd8) =
ioctl(, VIDIOC_DQBUF, 0xbfaccd00) =
ioctl(, VIDIOC_QBUF, 0x9b42d94) =
ioctl(, VIDIOC_DQBUF, 0xbfaccd00) =
ioctl(, VIDIOC_QBUF, 0x9b42dd8) =
ioctl(, VIDIOC_DQBUF, 0xbfaccd00) =
ioctl(, VIDIOC_QBUF, 0x9b42d94) =
ioctl(, VIDIOC_DQBUF, 0xbfaccd00) =
ioctl(, VIDIOC_QBUF, 0x9b42dd8) =
ioctl(, VIDIOC_DQBUF, 0xbfaccd00) =
ioctl(, VIDIOC_QBUF, 0x9b42d94) =
ioctl(, VIDIOC_DQBUF, 0xbfaccd00) =
ioctl(, VIDIOC_QBUF, 0x9b42dd8) =
ioctl(, VIDIOC_DQBUF, 0xbfaccd00) =
ioctl(, VIDIOC_QBUF, 0x9b42d94) =
ioctl(, VIDIOC_DQBUF, 0xbfaccd00) =
ioctl(, VIDIOC_QBUF, 0x9b42dd8) =
ioctl(, VIDIOC_DQBUF, 0xbfaccd00) =
ioctl(, VIDIOC_QBUF, 0x9b42d94) =
ioctl(, VIDIOC_DQBUF, 0xbfaccd00) =
ioctl(, VIDIOC_QBUF, 0x9b42dd8) =
ioctl(, VIDIOC_DQBUF, 0xbfaccd00) =
ioctl(, VIDIOC_QBUF, 0x9b42d94) =
ioctl(, VIDIOC_DQBUF, 0xbfaccd00) =
ioctl(, VIDIOC_QBUF, 0x9b42dd8) =
ioctl(, VIDIOC_DQBUF, 0xbfaccd00) =
ioctl(, VIDIOC_QBUF, 0x9b42d94) =
ioctl(, VIDIOC_DQBUF, 0xbfaccd00) =
ioctl(, VIDIOC_QBUF, 0x9b42dd8) =
ioctl(, VIDIOC_DQBUF, 0xbfaccd00) =
ioctl(, VIDIOC_QBUF, 0x9b42d94) =
ioctl(, VIDIOC_DQBUF, 0xbfaccd00) =
ioctl(, VIDIOC_QBUF, 0x9b42dd8) =
ioctl(, VIDIOC_DQBUF, 0xbfaccd00) =
ioctl(, VIDIOC_QBUF, 0x9b42d94) =
ioctl(, VIDIOC_DQBUF, 0xbfaccd00) =
ioctl(, VIDIOC_QBUF, 0x9b42dd8) =
ioctl(, VIDIOC_DQBUF, 0xbfaccd00) =
ioctl(, VIDIOC_QBUF, 0x9b42d94) =
ioctl(, VIDIOC_DQBUF, 0xbfaccd00) =
ioctl(, VIDIOC_QBUF, 0x9b42dd8) =
ioctl(, VIDIOC_DQBUF, 0xbfaccd00) =
ioctl(, VIDIOC_QBUF, 0x9b42d94) =
ioctl(, VIDIOC_DQBUF, 0xbfaccd00) =
ioctl(, VIDIOC_QBUF, 0x9b42dd8) =
ioctl(, VIDIOC_DQBUF, 0xbfaccd00) =
ioctl(, VIDIOC_QBUF, 0x9b42d94) =
ioctl(, VIDIOC_DQBUF, 0xbfaccd00) =
ioctl(, VIDIOC_QBUF, 0x9b42dd8) =
ioctl(, VIDIOC_DQBUF, 0xbfaccd00) =
ioctl(, VIDIOC_QBUF, 0x9b42d94) =
ioctl(, VIDIOC_DQBUF, 0xbfaccd00) =
ioctl(, VIDIOC_QBUF, 0x9b42dd8) =
ioctl(, VIDIOC_DQBUF, 0xbfaccd00) =
ioctl(, VIDIOC_QBUF, 0x9b42d94) =
ioctl(, VIDIOC_DQBUF, 0xbfaccd00) =
ioctl(, VIDIOC_QBUF, 0x9b42dd8) =
ioctl(, VIDIOC_DQBUF, 0xbfaccd00) =
ioctl(, VIDIOC_QBUF, 0x9b42d94) =
ioctl(, VIDIOC_DQBUF, 0xbfaccd00) =
ioctl(, VIDIOC_QBUF, 0x9b42dd8) =
ioctl(, VIDIOC_DQBUF, 0xbfaccd00) =
ioctl(, VIDIOC_QBUF, 0x9b42d94) =
ioctl(, VIDIOC_DQBUF, 0xbfaccd00) =
ioctl(, VIDIOC_QBUF, 0x9b42dd8) =
ioctl(, VIDIOC_DQBUF, 0xbfaccd00) =
ioctl(, VIDIOC_QBUF, 0x9b42d94) =
ioctl(, VIDIOC_DQBUF, 0xbfaccd00) =
ioctl(, VIDIOC_QBUF, 0x9b42dd8) =
ioctl(, VIDIOC_DQBUF, 0xbfaccd00) =
ioctl(, VIDIOC_QBUF, 0x9b42d94) =
ioctl(, VIDIOC_DQBUF, 0xbfaccd00) =
ioctl(, VIDIOC_QBUF, 0x9b42dd8) =
ioctl(, VIDIOC_DQBUF, 0xbfaccd00) =
ioctl(, VIDIOC_QBUF, 0x9b42d94) =
ioctl(, VIDIOC_DQBUF, 0xbfaccd00) =
ioctl(, VIDIOC_QBUF, 0x9b42dd8) =
ioctl(, VIDIOC_DQBUF, 0xbfaccd00) =
ioctl(, VIDIOC_QBUF, 0x9b42d94) =
ioctl(, VIDIOC_DQBUF, 0xbfaccd00) =
ioctl(, VIDIOC_QBUF, 0x9b42dd8) =
ioctl(, VIDIOC_DQBUF, 0xbfaccd00) =
ioctl(, VIDIOC_QBUF, 0x9b42d94) =
ioctl(, VIDIOC_DQBUF, 0xbfaccd00) =
ioctl(, VIDIOC_QBUF, 0x9b42dd8) =
ioctl(, VIDIOC_DQBUF, 0xbfaccd00) =
ioctl(, VIDIOC_QBUF, 0x9b42d94) =
ioctl(, VIDIOC_DQBUF, 0xbfaccd00) =
ioctl(, VIDIOC_QBUF, 0x9b42dd8) =
ioctl(, VIDIOC_DQBUF, 0xbfaccd00) =
ioctl(, VIDIOC_QBUF, 0x9b42d94) =
ioctl(, VIDIOC_DQBUF, 0xbfaccd00) =
ioctl(, VIDIOC_QBUF, 0x9b42dd8) =
ioctl(, VIDIOC_DQBUF, 0xbfaccd00) =
ioctl(, VIDIOC_QBUF, 0x9b42d94) =
ioctl(, VIDIOC_DQBUF, 0xbfaccd00) =
ioctl(, VIDIOC_QBUF, 0x9b42dd8) =
ioctl(, VIDIOC_DQBUF, 0xbfaccd00) =
ioctl(, VIDIOC_QBUF, 0x9b42d94) =
ioctl(, VIDIOC_DQBUF, 0xbfaccd00) =
ioctl(, VIDIOC_QBUF, 0x9b42dd8) =
ioctl(, VIDIOC_DQBUF, 0xbfaccd00) =
ioctl(, VIDIOC_QBUF, 0x9b42d94) =
ioctl(, VIDIOC_DQBUF, 0xbfaccd00) =
ioctl(, VIDIOC_STREAMOFF, 0xbfacc53c) =
ioctl(, VIDIOC_QUERYBUF or VT_RESIZE, 0xbfacc48c) =
ioctl(, VIDIOC_QUERYBUF or VT_RESIZE, 0xbfacc48c) =
ioctl(, VIDIOC_REQBUFS or VT_DISALLOCATE, 0x9b42d80) = - EINVAL (Invalid argument)
ioctl(, VIDIOC_TRY_FMT, 0x9b42ca4) = - EINVAL (Invalid argument)
ioctl(, VIDIOC_TRY_FMT, 0xbfacc000) =
ioctl(, VIDIOC_TRY_FMT, 0xbfacc000) =
ioctl(, VIDIOC_TRY_FMT, 0xbfacc000) =
ioctl(, VIDIOC_TRY_FMT, 0xbfacc000) =
ioctl(, VIDIOC_TRY_FMT, 0xbfacc000) =
ioctl(, VIDIOC_TRY_FMT, 0xbfacc000) =
ioctl(, VIDIOC_REQBUFS or VT_DISALLOCATE, 0xbfacc70c) =
ioctl(, VIDIOC_QUERYBUF or VT_RESIZE, 0xbfacc61c) =
ioctl(, VIDIOC_QUERYBUF or VT_RESIZE, 0xbfacc61c) =
ioctl(, VIDIOC_QBUF, 0xbfacc61c) =
ioctl(, VIDIOC_QBUF, 0xbfacc61c) =
ioctl(, VIDIOC_STREAMON, 0xbfacc65c) =
ioctl(, VIDIOC_DQBUF, 0xbfacc6c8) =
ioctl(, VIDIOC_QBUF, 0xbfacc61c) =

xawtv.log文件中open()函数之后调用了ioctl(4, VIDIOC_QUERYCAP, ...),因此文件描述符操作ioctl(4, VIDIOC_QUERYCAP, ...)上面的所有行不会涉及驱动底层

最后,我们可以根据文件描述符操作文件、xawtv.log和SI4工程确定ioctl()执行过程:

open("/dev/video0", O_RDWR|O_LARGEFILE)    /* 1. 打开video0 */
ioctl(, VIDIOC_QUERYCAP /* 2. 列举性能 */
ioctl(, VIDIOC_G_FMT /* 3. 获得格式 */
for ()
-> ioctl(, VIDIOC_ENUM_FMT /* 4. 列举格式 */
ioctl(, VIDIOC_QUERYCAP /* 5. */
ioctl(, VIDIOC_G_INPUT /* 6. 获取当前输入源 */
ioctl(, VIDIOC_ENUMINPUT /* 7. 列举输入源 */
ioctl(, VIDIOC_QUERYCTRL /* 8. 查询属性,如亮度范围、对比度范围 */
ioctl(, VIDIOC_QUERYCAP /* 9. */
ioctl(, VIDIOC_ENUMINPUT /* 10. */
for ()
-> ioctl(, VIDIOC_ENUMINPUT /* 11. */
for ()
-> ioctl(, VIDIOC_ENUMSTD /* 12.列举标准 */
for ()
-> ioctl(, VIDIOC_ENUM_FMT /* 13. */
ioctl(, VIDIOC_G_PARM /* 14. */
for ()
-> ioctl(, VIDIOC_QUERYCTRL /* 15. */
ioctl(, VIDIOC_G_STD /* 16.获取当前使用的标准 */
ioctl(, VIDIOC_G_INPUT /* 17. */
ioctl(, VIDIOC_G_CTRL /* 18.获取当前所使用的属性 */
ioctl(, VIDIOC_TRY_FMT /* 19.测试摄像头支持的格式 */
ioctl(, VIDIOC_S_FMT /* 20.设置摄像头格式 */
ioctl(, VIDIOC_REQBUFS /* 21.请求系统分配缓冲区 */
ioctl(, VIDIOC_QUERYBUF /* 22.查询所分配的缓冲区 */
for ()
-> ioctl(, VIDIOC_QBUF /* 23.把缓冲区放入队列 */
ioctl(, VIDIOC_STREAMON /* 24.启动摄像头 */
for ()
-> ioctl(, VIDIOC_S_CTRL /* 25.设置属性 */
ioctl(, VIDIOC_S_INPUT /* 26.设置输入源 */
ioctl(, VIDIOC_S_STD /* 27.设置标准 */
for ()
-> select(, [] /* 28.查询文件描述符4是否有数据 */
-> ioctl(, VIDIOC_DQBUF /* 29.把缓冲区从队列中取出 */
-> /* 处理数据 */
-> ioctl(, VIDIOC_QBUF /* 30.把缓冲区放入队列 */
ioctl(, VIDIOC_STREAMOFF /* 31.关闭摄像头 */

在分析完ioctl()的执行过程后,我们需要在SI4工程文件drv0-v4l2.c文件中搜索ioctl()各个宏,确定v4l2_ioctl_ops必需的函数指针:

/** 1~2被v4l2_open(char *device)调用 */
open("/dev/video0", O_RDWR|O_LARGEFILE) /* 1. 打开video0 */
ioctl(, VIDIOC_QUERYCAP /* 2. 列举性能 */ /** 3~10没有看到调用过程,可能源码和应用程序不一致,略 */ /** 11~15被get_device_capabilities(h)调用 */
for ()
-> ioctl(, VIDIOC_ENUMINPUT /* 11. */
for ()
-> ioctl(, VIDIOC_ENUMSTD /* 12.列举标准 */
for ()
-> ioctl(, VIDIOC_ENUM_FMT /* 13. */
ioctl(, VIDIOC_G_PARM /* 14. */
for ()
-> ioctl(, VIDIOC_QUERYCTRL /* 15. */ /** 16~18被v4l2_read_attr()调用 */
ioctl(, VIDIOC_G_STD /* 16.获取当前使用的标准 */
ioctl(, VIDIOC_G_INPUT /* 17. */
ioctl(, VIDIOC_G_CTRL /* 18.获取当前所使用的属性 */ /** 19~20被v4l2_overlay()调用 */
ioctl(, VIDIOC_TRY_FMT /* 19.测试摄像头支持的格式 */
ioctl(, VIDIOC_S_FMT /* 20.设置摄像头格式 */ /** 21~24被v4l2_start_streaming()调用 */
ioctl(, VIDIOC_REQBUFS /* 21.请求系统分配缓冲区 */
ioctl(, VIDIOC_QUERYBUF /* 22.查询所分配的缓冲区 */
for ()
-> ioctl(, VIDIOC_QBUF /* 23.把缓冲区放入队列 */
-> h->buf_me[i].data = mmap(...);
ioctl(, VIDIOC_STREAMON /* 24.启动摄像头 */ /** 25~27被v4l2_write_attr()调用 */
ioctl(, VIDIOC_S_CTRL /* 25.设置属性 */
ioctl(, VIDIOC_S_INPUT /* 26.设置输入源 */
ioctl(, VIDIOC_S_STD /* 27.设置标准 */ /** 28~29被v4l2_waiton()调用
* 30被v4l2_queue_buffer()调用
* v4l2_waiton()和v4l2_queue_buffer()被v4l2_nextframe()调用
*/
for ()
-> select(, [] /* 28.查询文件描述符4是否有数据 */
-> ioctl(, VIDIOC_DQBUF /* 29.把缓冲区从队列中取出 */
-> /* 处理数据,之前使用mmap()获得了缓冲区的地址 */
-> ioctl(, VIDIOC_QBUF /* 30.把缓冲区放入队列 */
/** 31被v4l2_stop_streaming()调用 */
ioctl(, VIDIOC_STREAMOFF /* 31.关闭摄像头 */

剩下这么多宏,有哪个是可以省略的呢?

我们现在要回到虚拟机内核的vivi.c中vivi_ioctl_ops逐个测试,测试方法为:注释某个函数指针,重新编译挂载,测试是否可以正常使用。

我在此不再一一测试,最终结果如下:

static const struct v4l2_ioctl_ops vivi_ioctl_ops = {
/* 表示它是一个摄像头设备。不可省略 */
.vidioc_querycap = vidioc_querycap,
#if 0
/* 列举、获取、设置输入源。可以省略,使用默认输入源 */
.vidioc_enum_input = vidioc_enum_input,
.vidioc_g_input = vidioc_g_input,
.vidioc_s_input = vidioc_s_input,
#endif
/* 列举、获得、测试、设置摄像头的数据格式。不可省略 */
.vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
.vidioc_g_fmt_vid_cap = vidioc_g_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,
#if 0
/* 列举、获取、设置标准。可以省略
* 列举、获取标准使用的是
* video_device的tvnorms和current_norm
*/
.vidioc_s_std = vidioc_s_std,
#endif
/* 启动、停止视频流。不可省略 */
.vidioc_streamon = vidioc_streamon,
.vidioc_streamoff = vidioc_streamoff,
#if 0
/* 在内核日志中记录状态。可以省略 */
.vidioc_log_status = v4l2_ctrl_log_status,
/* 子系统事件。可以省略 */
.vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
.vidioc_unsubscribe_event = v4l2_event_unsubscribe,
#endif
};

八、数据的获取过程

本节使用v4l2.c进行分析,读者若对vb2感兴趣也可以分析vivi.c

1. 请求分配缓冲区,但此阶段其实只分配了videobuf_buffer

ioctl(, VIDIOC_REQBUFS
-> solo_reqbufs
-> videobuf_reqbufs(&fh->vidq, req);
-> *size = solo_image_size(solo_dev); /* 设置图片size */
-> __videobuf_mmap_setup(q, count, size, req->memory);
-> q->bufs[i] = videobuf_alloc_vb(q); /* 分配设置videobuf_buffer */

2. 查询映射缓冲区,通过videobuf_buffer设置供上层使用的v4l2_buffer

ioctl(, VIDIOC_QUERYBUF
-> videobuf_querybuf
-> videobuf_status(q, b, q->bufs[b->index], q->type);
-> b->flags |= V4L2_BUF_FLAG_MAPPED;
-> b->field = vb->field;
-> b->timestamp = vb->ts; /* v4l2_buffer */

3. 真正分配缓冲区

mmap(NULL, h->buf_v4l2[i].length, ...)
-> solo_v4l2_mmap
-> videobuf_mmap_mapper(&fh->vidq, vma);
-> CALL(q, mmap_mapper, q, buf, vma)

4. 把缓冲区放入队列

ioctl(, VIDIOC_QBUF
-> videobuf_qbuf(&fh->vidq, buf);
-> buf = q->bufs[b->index];
-> solo_buf_prepare /* 设置video_device成员 */
-> vb->width = solo_dev->video_hsize;
-> vb->height = solo_vlines(solo_dev);
-> list_add_tail(&buf->stream, &q->stream)
-> solo_buf_queue /* 把queue放到list_head */
-> list_add_tail(&vb->queue, &fh->vidq_active);

5. 启动摄像头

ioctl(, VIDIOC_STREAMON
-> solo_streamon
-> videobuf_streamon(&fh->vidq);
-> solo_buf_queue /* 把queue放到list_head */
-> list_add_tail(&vb->queue, &fh->vidq_active);
-> wake_up_interruptible_sync(&q->wait);

6. 查询是否有数据

select(h->fd + , &rdset, NULL, NULL, &tv)
-> solo_v4l2_poll
-> videobuf_poll_stream(file, &fh->vidq, wait);
-> poll_wait(file, &buf->done, wait);

唤醒此进程的是open()函数注册的solo_thread()函数

7. 从缓冲区中取数据

ioctl(, VIDIOC_DQBUF
-> solo_dqbuf
-> videobuf_dqbuf(&fh->vidq, buf, file->f_flags & O_NONBLOCK);
-> stream_next_buffer(q, &buf, nonblocking); /* 在queue中获取有数据的缓冲区 */
-> videobuf_status(q, b, buf, q->type); /* 把缓冲区的状态返回给应用层 */
-> list_del(&buf->stream);

九、虚拟摄像头驱动程序编写过程

1. 使用video_device_alloc()分配video_device

2. 设置结构体成员

.fops

.ioctl_ops(里面需要设置11项)

如果要用内核提供的缓冲区操作函数,还需要构造一个videobuf_queue_ops

3. 使用video_register_device()注册video_device

我们可以仿照v4l2.c写一个虚拟摄像头驱动程序

源代码:

 #include <linux/module.h>
#include <linux/delay.h>
#include <linux/errno.h>
#include <linux/fs.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/mm.h>
#include <linux/ioport.h>
#include <linux/init.h>
#include <linux/sched.h>
#include <linux/pci.h>
#include <linux/random.h>
#include <linux/version.h>
#include <linux/mutex.h>
#include <linux/videodev2.h>
#include <linux/dma-mapping.h>
#include <linux/interrupt.h>
#include <linux/kthread.h>
#include <linux/highmem.h>
#include <linux/freezer.h>
#include <media/videobuf-vmalloc.h>
#include <media/v4l2-device.h>
#include <media/v4l2-ioctl.h> #define USE_DMA 0 static struct video_device *myvivi;
static struct v4l2_format myvivi_format;
static struct videobuf_queue myvivi_vb_vidq;
static spinlock_t myvivi_slock;
static struct timer_list myvivi_timer;
static struct list_head myvivi_list_head; /**
* videobuf_queue_ops
**/
/* ioctl VIDIOC_REQBUFS导致此函数被调用
* 用于重新调整count和size
*/
static int myvivi_buf_setup(struct videobuf_queue *vq, unsigned int *count, unsigned int *size)
{
*size = myvivi_format.fmt.pix.sizeimage; if (*count < )
*count = ; return ;
} /* ioctl VIDIOC_QBUF导致此函数被调用
* 它会填充video_buffer结构体并调用videobuf_iolock来分配内存
*/
static int myvivi_buf_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb, enum v4l2_field field)
{
vb->size = myvivi_format.fmt.pix.sizeimage; /* XXX: These properties only change when queue is idle */
vb->width = myvivi_format.fmt.pix.width;
vb->height = myvivi_format.fmt.pix.height;
vb->bytesperline = myvivi_format.fmt.pix.bytesperline;
vb->field = field;
#if USE_DMA
if (vb->state == VIDEOBUF_NEEDS_INIT) {
int rc = videobuf_iolock(vq, vb, NULL);
if (rc < ) {
struct videobuf_dmabuf *dma = videobuf_to_dma(vb);
videobuf_dma_unmap(vq->dev, dma);
videobuf_dma_free(dma);
vb->state = VIDEOBUF_NEEDS_INIT;
return rc;
}
}
#endif
vb->state = VIDEOBUF_PREPARED; return ;
} /* ioctl VIDIOC_QBUF时:
* 1. 调用buf_prepare()做准备工作
* 2. 把buf放入stream队列
* 3. 调用buf_queue(起通知、记录作用)
*/
static void myvivi_buf_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb)
{
vb->state = VIDEOBUF_QUEUED;
list_add_tail(&vb->queue, &myvivi_list_head);
/* 使用的定时器,不需要唤醒 */
// wake_up_interruptible(&solo_dev->disp_thread_wait);
} /* APP不再使用队列时,用它来释放内存 */
static void myvivi_buf_release(struct videobuf_queue *vq, struct videobuf_buffer *vb)
{
#if USE_DMA
struct videobuf_dmabuf *dma = videobuf_to_dma(vb); videobuf_dma_unmap(vq->dev, dma);
videobuf_dma_free(dma);
#endif
vb->state = VIDEOBUF_NEEDS_INIT;
} static struct videobuf_queue_ops solo_video_qops = {
.buf_setup = myvivi_buf_setup,
.buf_prepare = myvivi_buf_prepare,
.buf_queue = myvivi_buf_queue,
.buf_release = myvivi_buf_release,
}; /**
* v4l2_file_operations
**/
static int myvivi_open(struct file *file)
{
videobuf_queue_vmalloc_init(&myvivi_vb_vidq, &solo_video_qops,
NULL, &myvivi_slock,
V4L2_BUF_TYPE_VIDEO_CAPTURE,
V4L2_FIELD_INTERLACED,
sizeof(struct videobuf_buffer), NULL, NULL);
/* 其他程序在此处执行thread,本程序在此处执行定时器 */
myvivi_timer.expires = jiffies + ;
add_timer(&myvivi_timer); return ;
} static int myvivi_close(struct file *file)
{
del_timer(&myvivi_timer);
videobuf_stop(&myvivi_vb_vidq);
videobuf_mmap_free(&myvivi_vb_vidq); return ;
} static ssize_t myvivi_read(struct file *file, char __user *data, size_t count, loff_t *ppos)
{
return videobuf_read_stream(&myvivi_vb_vidq, data, count, ppos, , file->f_flags & O_NONBLOCK);
} static unsigned int myvivi_poll(struct file *file, struct poll_table_struct *wait)
{
return videobuf_poll_stream(file, &myvivi_vb_vidq, wait);
} extern int videobuf_mmap_mapper(struct videobuf_queue *, struct vm_area_struct *);
static int myvivi_mmap(struct file *file, struct vm_area_struct *vma)
{
return videobuf_mmap_mapper(&myvivi_vb_vidq, vma);
} static const struct v4l2_file_operations myvivi_fops = {
.owner = THIS_MODULE,
.open = myvivi_open,
.release = myvivi_close,
.read = myvivi_read,
.poll = myvivi_poll,
.unlocked_ioctl = video_ioctl2,
.mmap = myvivi_mmap,
}; /**
* v4l2_ioctl_ops
**/
static int myvivi_querycap(struct file *file, void *priv, struct v4l2_capability *cap)
{
strcpy(cap->driver, "myvivi");
strcpy(cap->card, "myvivi");
cap->version = 0x01;
cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
return ;
} static int myvivi_enum_fmt_vid_cap(struct file *file, void *priv, struct v4l2_fmtdesc *f)
{
if (f->index)
return -EINVAL; f->pixelformat = V4L2_PIX_FMT_YUYV;
strlcpy(f->description, "4:2:2, packed, YUYV", sizeof(f->description)); return ;
} static int myvivi_g_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f)
{
memcpy(f, &myvivi_format, sizeof(myvivi_format));
return ;
} static int myvivi_try_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f)
{
enum v4l2_field field;
unsigned int max_width, max_height; if (f->fmt.pix.pixelformat != V4L2_PIX_FMT_YUYV)
return -EINVAL; field = f->fmt.pix.field;
if (field == V4L2_FIELD_ANY) {
field = V4L2_FIELD_INTERLACED;
}
else if (V4L2_FIELD_INTERLACED != field) {
return -EINVAL;
}
f->fmt.pix.field = field; max_width = ;
max_height = ; v4l_bound_align_image(&f->fmt.pix.width, , max_width, ,
&f->fmt.pix.height, , max_height, , );
f->fmt.pix.bytesperline =
(f->fmt.pix.width * ) >> ;
f->fmt.pix.sizeimage =
f->fmt.pix.height * f->fmt.pix.bytesperline;
f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; return ;
} static int myvivi_s_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f)
{
int ret;
if (videobuf_queue_is_busy(&myvivi_vb_vidq))
return -EBUSY; ret = myvivi_try_fmt_vid_cap(file, priv, f);
if (ret < )
return ret; memcpy(&myvivi_format, f, sizeof(myvivi_format)); return ;
} static int myvivi_reqbufs(struct file *file, void *priv, struct v4l2_requestbuffers *p)
{
return videobuf_reqbufs(&myvivi_vb_vidq, p);
} static int myvivi_querybuf(struct file *file, void *priv, struct v4l2_buffer *p)
{
return videobuf_querybuf(&myvivi_vb_vidq, p);
} static int myvivi_qbuf(struct file *file, void *priv, struct v4l2_buffer *p)
{
return videobuf_qbuf(&myvivi_vb_vidq, p);
} static int myvivi_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p)
{
return videobuf_dqbuf(&myvivi_vb_vidq, p, file->f_flags & O_NONBLOCK);
} static int myvivi_streamon(struct file *file, void *priv, enum v4l2_buf_type i)
{
return videobuf_streamon(&myvivi_vb_vidq);
} static int myvivi_streamoff(struct file *file, void *priv, enum v4l2_buf_type i)
{
videobuf_streamoff(&myvivi_vb_vidq);
return ;
} static const struct v4l2_ioctl_ops myvivi_ioctl_ops = {
.vidioc_querycap = myvivi_querycap,
.vidioc_enum_fmt_vid_cap = myvivi_enum_fmt_vid_cap,
.vidioc_g_fmt_vid_cap = myvivi_g_fmt_vid_cap,
.vidioc_try_fmt_vid_cap = myvivi_try_fmt_vid_cap,
.vidioc_s_fmt_vid_cap = myvivi_s_fmt_vid_cap,
.vidioc_reqbufs = myvivi_reqbufs,
.vidioc_querybuf = myvivi_querybuf,
.vidioc_qbuf = myvivi_qbuf,
.vidioc_dqbuf = myvivi_dqbuf,
.vidioc_streamon = myvivi_streamon,
.vidioc_streamoff = myvivi_streamoff,
}; static void myvivi_release(struct video_device *vdev)
{
/* myvivi_exit()中调用了video_device_release()
* 因此myvivi->release不需要设置为video_device_release
*/
} /* 此函数用于实现其他文件的thread() */
static void myvivi_timerfunc(unsigned long arg)
{
struct videobuf_buffer *vb;
void *vbuf; if (list_empty(&myvivi_list_head))
goto fail; vb = list_first_entry(&myvivi_list_head, struct videobuf_buffer,
queue); if (!waitqueue_active(&vb->done))
goto fail; /* Fill buffer */
vbuf = videobuf_to_vmalloc(vb);
if (!vbuf)
goto fail; /* 由于是虚拟摄像头驱动,所以假装有数据 */
memset(vbuf, , vb->size); vb->field_count++;
do_gettimeofday(&vb->ts);
vb->state = VIDEOBUF_DONE; /* 把videobuf从队列中删除 */
list_del(&vb->queue); /* 唤醒videobuf->done上的进程 */
wake_up(&vb->done); fail:
/* 30帧/秒 */
mod_timer(&myvivi_timer, jiffies + HZ / );
} static int __init myvivi_init(void)
{
int ret; myvivi = video_device_alloc(); myvivi->release = myvivi_release,
myvivi->fops = &myvivi_fops;
myvivi->ioctl_ops = &myvivi_ioctl_ops; /* 初始化spinlock */
spin_lock_init(&myvivi_slock);
/* 初始化timer_list */
init_timer(&myvivi_timer);
myvivi_timer.function = myvivi_timerfunc;
/* 初始化list_head */
INIT_LIST_HEAD(&myvivi_list_head); ret = video_register_device(myvivi, VFL_TYPE_GRABBER, -);
if (ret < ) {
video_device_release(myvivi);
return ret;
} return ;
} static void __exit myvivi_exit(void)
{
video_unregister_device(myvivi);
video_device_release(myvivi);
} module_init(myvivi_init);
module_exit(myvivi_exit);
MODULE_LICENSE("GPL");

Makefile:

 KERN_DIR = /work/itop4412/tools/linux-3.5

 all:
make -C $(KERN_DIR) M=`pwd` modules clean:
make -C $(KERN_DIR) M=`pwd` modules clean
rm -rf modules.order obj-m += videobuf-vmalloc.o
obj-m += myvivi.o

由于是虚拟摄像头,所以源代码中的摄像头参数和数据格式是根据内核规则随意选择的,参数的确定和选取会在下一章进行介绍

本章内容较为混乱,推荐读者自行分析linux/drivers/staging/media/solo6x10/v4l2.c第八节:数据的获取过程体验各个过程的调用关系

上一篇:Selenium WebDriver原理(一):Selenium WebDriver 是怎么工作的?


下一篇:Android开发之下载Tomcat服务器的文件到模拟器的SD卡