二十四、V4L2框架分析和虚拟摄像头驱动编写

一、V4L2框架分析

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

V4L2整体框架如下图:

图中主要包括四个部分:

1. 字符设备驱动程序核心:V4L2本身就是一个字符设备,上层连接用户空间

2. V4L2驱动核心:构造通用的视频设备驱动框架,为上层操作提供统一接口

3. 平台V4L2驱动:在V4L2框架下,根据平台自身特性实现与平台相关的V4L2驱动部分,包括注册video_device和v4l2_dev

4. 具体的sensor驱动:主要用于上电、提供工作时钟、视频图像裁剪、流IO开启等和注册v4l2_subdev

图中各个结构体定义如下:

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[32];
    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的父设备

在层次上,v4l2_device和video_device平等,video_device用于在/dev目录下生成设备节点文件

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

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, 0, 0, 0, 0)
        -> 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, -1);
  -> __video_register_device(vdev, type, nr, 1, vdev->fops->owner);
    -> case VFL_TYPE_GRABBER:
      -> name_base = "video";
    -> case VFL_TYPE_GRABBER:
      -> minor_offset = 0;
      -> minor_cnt = 64;
    -> vdev->minor = i + minor_offset;
    -> vdev->cdev = cdev_alloc();
    -> vdev->cdev->ops = &v4l2_fops;
    -> cdev_add(vdev->cdev, MKDEV(VIDEO_MAJOR, vdev->minor), 1);
    -> dev_set_name(&vdev->dev, "%s%d", name_base, vdev->num);    /* 设备名为video%d */
    -> device_register(&vdev->dev);
      -> device_add(dev);

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

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

1. chrdev_region()和class()在哪里被调用的?

2. v4l2_fops函数都做了什么?

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

 1 static int __init videodev_init(void)
 2 {
 3     dev_t dev = MKDEV(VIDEO_MAJOR, 0);        /* VIDEO_MAJOR = 81 */
 4     int ret;
 5
 6     ret = register_chrdev_region(dev, VIDEO_NUM_DEVICES, VIDEO_NAME);
 7 ...
 8     ret = class_register(&video_class);
 9 ...
10     return 0;
11 }

现在我们来看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()函数分析:

 1 static int v4l2_open(struct inode *inode, struct file *filp)
 2 {
 3     struct video_device *vdev;
 4     int ret = 0;
 5 ...
 6     vdev = video_devdata(filp);
 7 ...
 8     if (vdev->fops->open) {
 9 ...
10         if (video_is_registered(vdev))
11             ret = vdev->fops->open(filp);    /* 最终会调用video_device->fops的open()函数 */
12 ...
13     }
14 ...
15 }

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

 1 static ssize_t v4l2_read(struct file *filp, char __user *buf, size_t sz, loff_t *off)
 2 {
 3     struct video_device *vdev = video_devdata(filp);
 4 ...
 5     if (video_is_registered(vdev))
 6         ret = vdev->fops->read(filp, buf, sz, off);
 7 ...
 8 }
 9
10 static long v4l2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
11 {
12     struct video_device *vdev = video_devdata(filp);
13     int ret = -ENODEV;
14
15     if (vdev->fops->unlocked_ioctl) {
16 ...
17         if (video_is_registered(vdev))
18             ret = vdev->fops->unlocked_ioctl(filp, cmd, arg);
19 ...
20     }
21 ...
22 }

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

在此我以vivi.c文件为例分析:

static const struct v4l2_file_operations vivi_fops = {
    .owner        = THIS_MODULE,
    .open           = v4l2_fh_open,
    .release        = vivi_close,
    .read           = vivi_read,
    .poll        = vivi_poll,
    .unlocked_ioctl = video_ioctl2, /* V4L2 ioctl handler */
    .mmap           = vivi_mmap,
};

首先从open()函数分析:

 1 int v4l2_fh_open(struct file *filp)
 2 {
 3     struct video_device *vdev = video_devdata(filp);
 4     struct v4l2_fh *fh = kzalloc(sizeof(*fh), GFP_KERNEL);
 5
 6     filp->private_data = fh;
 7 ...
 8     v4l2_fh_init(fh, vdev);    /* 初始化v4l2_fh */
 9     v4l2_fh_add(fh);           /* 注册v4l2_fh */
10     return 0;
11 }

函数中的struct v4l2_fh为每一个被打开的节点维护一个文件句柄:

struct v4l2_fh {
    struct list_head    list;
    struct video_device    *vdev;
    struct v4l2_ctrl_handler *ctrl_handler;
    enum v4l2_priority    prio;

    /* Events */
    wait_queue_head_t    wait;
    struct list_head    subscribed; /* Subscribed events */
    struct list_head    available; /* Dequeueable event */
    unsigned int        navailable;
    u32            sequence;
};

在此直接read()函数的调用关系:

vivi_read()
  -> vb2_read(&dev->vb_vidq, data, count, ppos, file->f_flags & O_NONBLOCK);
    -> __vb2_perform_fileio(q, data, count, ppos, nonblocking, 1);
      -> __vb2_init_fileio(q, read);
        /* 获取缓冲区的地址和大小 */
        -> fileio->bufs[i].vaddr = vb2_plane_vaddr(q->bufs[i], 0);
        -> fileio->bufs[i].size = vb2_plane_size(q->bufs[i], 0);
      -> buf = &fileio->bufs[index];
      -> buf->size = vb2_get_plane_payload(vb, 0);
      -> if (read)
        -> copy_to_user(data, buf->vaddr + buf->pos, count);
      -> else
        -> copy_from_user(buf->vaddr + buf->pos, data, count);

同样,在此直接ioctl()函数的调用关系:

video_ioctl2()
  /* 根据cmd,调用__video_do_ioctl()将数据拷贝到内核空间 */
  -> video_usercopy(file, cmd, arg, __video_do_ioctl);
    -> __video_do_ioctl()
      -> struct video_device *vfd = video_devdata(file);
      /* v4l2_ioctl_ops:控制函数结构体 */
      -> 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);

其中,struct v4l2_ioctl_ops定义如下:

struct v4l2_ioctl_ops {
    /* ioctl callbacks */

    /* VIDIOC_QUERYCAP handler */
    int (*vidioc_querycap)(struct file *file, void *fh, struct v4l2_capability *cap);

...
    /* VIDIOC_S_FMT handlers */
    int (*vidioc_s_fmt_vid_cap)    (struct file *file, void *fh, struct v4l2_format *f);
...
    /* Buffer handlers */
    int (*vidioc_reqbufs) (struct file *file, void *fh, struct v4l2_requestbuffers *b);
    int (*vidioc_querybuf)(struct file *file, void *fh, struct v4l2_buffer *b);
    int (*vidioc_qbuf)    (struct file *file, void *fh, struct v4l2_buffer *b);
    int (*vidioc_dqbuf)   (struct file *file, void *fh, struct v4l2_buffer *b);
...
        /* Stream on/off */
    int (*vidioc_streamon) (struct file *file, void *fh, enum v4l2_buf_type i);
    int (*vidioc_streamoff)(struct file *file, void *fh, enum v4l2_buf_type i);
...

分析完了这两个问题,下面我们来分析vivi.c文件

三、vivi.c文件分析

首先来分析init()函数的调用关系:

vivi_init(void)
  -> vivi_create_instance(i);
    -> v4l2_device_register(NULL, &dev->v4l2_dev);
    /* 设置ctrl属性(用于APP的ioctl) */
    -> v4l2_ctrl_handler_init(hdl, 11);
    -> dev->volume = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops, V4L2_CID_AUDIO_VOLUME, 0, 255, 1, 200);
    -> dev->brightness = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops, V4L2_CID_BRIGHTNESS, 0, 255, 1, 127);
    -> (struct vb2_queue *q)->ops = &vivi_video_qops;
    -> q->mem_ops = &vb2_vmalloc_memops;
    -> vb2_queue_init(q);
      -> init_waitqueue_head(&q->done_wq);  /* 初始化等待队列头 */
    -> struct video_device *vfd = video_device_alloc();
      -> kzalloc(sizeof(struct video_device), GFP_KERNEL);
    -> *vfd = vivi_template;                /* 设置video_device */
    -> video_register_device(vfd, VFL_TYPE_GRABBER, video_nr);

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

在init()函数中,struct vb2_queue是数据传输队列,结构体成员struct vb2_buffer是队列传输的基本单位

struct vb2_queue {
    enum v4l2_buf_type    type;                  /* buffer类型 */
    unsigned int        io_modes;                /* 传输类型 */
    unsigned int        io_flags;

    const struct vb2_ops        *ops;            /* buffer队列操作函数 */
    const struct vb2_mem_ops    *mem_ops;        /* buffer memory操作函数 */
    void                *drv_priv;
    unsigned int        buf_struct_size;

/* private: internal use only */
    enum v4l2_memory    memory;
    struct vb2_buffer    *bufs[VIDEO_MAX_FRAME]; /* buffer集合 */
    unsigned int        num_buffers;             /* buffer个数 */
...
};

struct vb2_ops和struct vb2_mem_ops定义如下,我们一般不需要编写其中函数

struct vb2_ops {
    /* 队列初始化 */
    int (*queue_setup)(struct vb2_queue *q, const struct v4l2_format *fmt, unsigned int *num_buffers, unsigned int *num_planes, unsigned int sizes[], void *alloc_ctxs[]);
    /* 释放/获取设备操作锁 */
    void (*wait_prepare)(struct vb2_queue *q);
    void (*wait_finish)(struct vb2_queue *q);
    /* buffer操作函数 */
    int (*buf_init)(struct vb2_buffer *vb);
    int (*buf_prepare)(struct vb2_buffer *vb);
    int (*buf_finish)(struct vb2_buffer *vb);
    void (*buf_cleanup)(struct vb2_buffer *vb);
    /* 打开/关闭视频流 */
    int (*start_streaming)(struct vb2_queue *q, unsigned int count);
    int (*stop_streaming)(struct vb2_queue *q);
    /* 把vb2_buffer传输给驱动程序 */
    void (*buf_queue)(struct vb2_buffer *vb);
};

struct vb2_mem_ops {
    /* 分配/释放视频缓冲 */
    void    *(*alloc)(void *alloc_ctx, unsigned long);
    void    (*put)(void *buf_priv);
    /* 获取/释放用户空间视频缓冲区指针 */
    void    *(*get_userptr)(void *alloc_ctx, unsigned long vaddr, unsigned long size, int write);
    void    (*put_userptr)(void *buf_priv);
    /* 用于缓存同步 */
    void    *(*vaddr)(void *buf_priv);
    void    *(*cookie)(void *buf_priv);
    /* 返回当前用户空间的buffer数 */
    unsigned int    (*num_users)(void *buf_priv);
    /* 把缓冲区映射到用户空间 */
    int        (*mmap)(void *buf_priv, struct vm_area_struct *vma);
};

细心的读者可以发现struct vb2_ops和struct v4l2_ioctl_ops在函数指针定义有重复的,下面我们以打开视频流函数为例分析:

/* struct vb2_ops */
static struct vb2_ops vivi_video_qops = {
    .start_streaming    = start_streaming,
...
};

/* struct v4l2_ioctl_ops */
static const struct v4l2_ioctl_ops vivi_ioctl_ops = {
    .vidioc_streamon      = vidioc_streamon,
...
};

static int vidioc_streamon()
 -> vb2_streamon(&dev->vb_vidq, i);
   -> call_qop(q, start_streaming, q, atomic_read(&q->queued_count));
     /* 调用struct vb2_ops->start_streaming()函数 */
     -> start_streaming()

通过分析可以确定struct v4l2_ioctl_ops是对struct vb2_ops的上层抽象。

struct vb2_buffer是队列传输的基本单位,结构体成员struct v4l2_buffer是传输核心,两结构体定义如下:

struct vb2_buffer {
    struct v4l2_buffer    v4l2_buf;
    struct v4l2_plane    v4l2_planes[VIDEO_MAX_PLANES];
    struct vb2_queue    *vb2_queue;
    unsigned int        num_planes;

/* Private: internal use only */
    enum vb2_buffer_state    state;
    struct list_head    queued_entry;
    struct list_head    done_entry;
    struct vb2_plane    planes[VIDEO_MAX_PLANES];
};

struct v4l2_buffer {
    __u32            index;            /* buffer序号 */
    enum v4l2_buf_type      type;      /* buffer类型 */
    __u32            bytesused;        /* 缓缓冲区已经使用的byte数 */
    __u32            flags;
    enum v4l2_field        field;
    struct timeval        timestamp;   /* 捕捉图像的时间戳 */
    struct v4l2_timecode    timecode;
    __u32            sequence;

    /* memory location */
    enum v4l2_memory        memory;    /* 表示使内存映射缓冲区还是用户空间缓冲区 */
    union {
        __u32           offset;        /* 内核缓冲区位置 */
        unsigned long   userptr;       /* 用户空间缓冲区位置 */
        struct v4l2_plane *planes;
    } m;
    __u32            length;           /* 缓冲区大小 */
    __u32            input;
    __u32            reserved;
};
 

当开始流I/O时,图像帧会以struct v4l2_buffer的格式在应用和驱动间传输,它可能会有三个状态:

1. 在驱动的传入队列中,驱动对缓冲区进行处理,用户空间通过ioctl(..., VIDIOC_QBUF, ...)把缓冲区放入队列。对于视频捕获设备,传入队列中的缓冲区是空的,驱动会填充视频数据

2. 在驱动的传出队列中,驱动已经完成对缓冲区的处理。对于视频捕获设备,缓冲区已经填充了视频数据,在等待用户空间读取

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

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

分析完init()后,我们来看看struct video_device和struct v4l2_ioctl_ops的定义示例:

static struct video_device vivi_template = {
    .name        = "vivi",
    .fops           = &vivi_fops,
    .ioctl_ops     = &vivi_ioctl_ops,
    .release    = video_device_release,

    .tvnorms              = V4L2_STD_525_60,
    .current_norm         = V4L2_STD_NTSC_M,
};

vivi_fops在第二节分析过了,struct v4l2_ioctl_ops留在第五节分析。

四、vivi.c文件虚拟机测试

由于上面的分析涉及了太多结构体,那对于我们哪些才是重点呢?

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

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/下载同版本的内核

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文件和后续所需文件:

文件链接:

https://pan.baidu.com/s/1sBgzVHQlDdYCAvR793uMhw

提取码为:2mqw

我们来猜测一下调用过程:

1. open()打开摄像头字符设备

2. 进行一系列ioctl(),ioctl()使用的宏是我们确定v4l2_ioctl_ops必需函数指针的重点

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

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

  1 fstat64(4, {st_mode=S_IFREG|0600, st_size=57, ...}) = 0
  2 read(4, "\1\0\0\fbook-desktop\0\0010\0\22MIT-MAGIC-C"..., 4096) = 57
  3 read(4, "", 4096)                       = 0
  4 fstat64(4, {st_mode=S_IFREG|0644, st_size=82376, ...}) = 0
  5 read(4, "#\t$XdotOrg: lib/X11/nls/locale.a"..., 4096) = 4096
  6 read(4, "\t\t\t\t\tbg_BG.UTF-8\nbn_IN.utf8\t\t\t\t\t"..., 4096) = 4096
  7 read(4, "8859-15\t\t\t\tde_CH.ISO8859-15\nde_C"..., 4096) = 4096
  8 read(4, "_ZA.ISO8859-1\nen_ZA.ISO-8859-1\t\t"..., 4096) = 4096
  9 read(4, "85915\t\t\t\teu_ES.ISO8859-15\neu_ES."..., 4096) = 4096
 10 read(4, "14\ngv_GB.ISO-8859-14\t\t\t\tgv_GB.IS"..., 4096) = 4096
 11 read(4, "w_GB.ISO8859-1\nkw_GB.ISO-8859-1\t"..., 4096) = 4096
 12 read(4, ".ISO8859-1\noc\t\t\t\t\t\toc_FR.ISO8859"..., 4096) = 4096
 13 read(4, "r_RS.UTF-8\[email protected]\t"..., 4096) = 4096
 14 read(4, "yi_US.CP1255\nzh_CN\t\t\t\t\t\tzh_CN.gb"..., 4096) = 4096
 15 read(4, ":00 dawes Exp $\n#\n\nPOSIX:\t\t\t\t\t\tC"..., 4096) = 4096
 16 read(4, "4:\t\t\t\tbr_FR.ISO8859-14\nbr_FR.ISO"..., 4096) = 4096
 17 read(4, "de_DE.88591.en:\t\t\t\t\tde_DE.ISO885"..., 4096) = 4096
 18 read(4, "59-1:\t\t\t\ten_ZA.ISO8859-1\nen_ZA.I"..., 4096) = 4096
 19 read(4, "_ES.ISO8859-1\neu_ES.iso88591:\t\t\t"..., 4096) = 4096
 20 read(4, "F-8\ngu_IN.utf8:\t\t\t\t\tgu_IN.UTF-8\n"..., 4096) = 4096
 21 read(4, "o_KR.utf8:\t\t\t\t\tko_KR.UTF-8\nKO_KR"..., 4096) = 4096
 22 read(4, "o_NO.utf8:\t\t\t\t\tno_NO.UTF-8\nnr:\t\t"..., 4096) = 4096
 23 read(4, "O8859-2\nsl_SI.iso88592:\t\t\t\t\tsl_S"..., 4096) = 4096
 24 read(4, "N\nvi_VN.tcvn5712:\t\t\t\t\tvi_VN.TCVN"..., 4096) = 4096
 25 read(4, "9-9\nturkish.iso88599:\t\t\t\ttr_TR.I"..., 4096) = 456
 26 read(4, "", 4096)                       = 0
 27 fstat64(4, {st_mode=S_IFREG|0644, st_size=40344, ...}) = 0
 28 read(4, "#\t$XdotOrg: lib/X11/nls/locale.d"..., 4096) = 4096
 29 read(4, "59-1/XLC_LOCALE\t\t\tes_AR.ISO8859-"..., 4096) = 4096
 30 read(4, "_FR.ISO8859-1\niso8859-15/XLC_LOC"..., 4096) = 4096
 31 read(4, "8/XLC_LOCALE\t\t\tbe_BY.UTF-8\nen_US"..., 4096) = 4096
 32 read(4, "en_US.UTF-8/XLC_LOCALE\t\t\tlo_LA.U"..., 4096) = 4096
 33 fstat64(4, {st_mode=S_IFREG|0644, st_size=772, ...}) = 0
 34 read(4, "#  $Xorg: C,v 1.3 2000/08/17 19:"..., 4096) = 772
 35 read(4, "", 4096)                       = 0
 36 fstat64(4, {st_mode=S_IFREG|0644, st_size=82376, ...}) = 0
 37 read(4, "#\t$XdotOrg: lib/X11/nls/locale.a"..., 4096) = 4096
 38 read(4, "\t\t\t\t\tbg_BG.UTF-8\nbn_IN.utf8\t\t\t\t\t"..., 4096) = 4096
 39 read(4, "8859-15\t\t\t\tde_CH.ISO8859-15\nde_C"..., 4096) = 4096
 40 read(4, "_ZA.ISO8859-1\nen_ZA.ISO-8859-1\t\t"..., 4096) = 4096
 41 read(4, "85915\t\t\t\teu_ES.ISO8859-15\neu_ES."..., 4096) = 4096
 42 read(4, "14\ngv_GB.ISO-8859-14\t\t\t\tgv_GB.IS"..., 4096) = 4096
 43 read(4, "w_GB.ISO8859-1\nkw_GB.ISO-8859-1\t"..., 4096) = 4096
 44 read(4, ".ISO8859-1\noc\t\t\t\t\t\toc_FR.ISO8859"..., 4096) = 4096
 45 read(4, "r_RS.UTF-8\[email protected]\t"..., 4096) = 4096
 46 read(4, "yi_US.CP1255\nzh_CN\t\t\t\t\t\tzh_CN.gb"..., 4096) = 4096
 47 read(4, ":00 dawes Exp $\n#\n\nPOSIX:\t\t\t\t\t\tC"..., 4096) = 4096
 48 read(4, "4:\t\t\t\tbr_FR.ISO8859-14\nbr_FR.ISO"..., 4096) = 4096
 49 read(4, "de_DE.88591.en:\t\t\t\t\tde_DE.ISO885"..., 4096) = 4096
 50 read(4, "59-1:\t\t\t\ten_ZA.ISO8859-1\nen_ZA.I"..., 4096) = 4096
 51 read(4, "_ES.ISO8859-1\neu_ES.iso88591:\t\t\t"..., 4096) = 4096
 52 read(4, "F-8\ngu_IN.utf8:\t\t\t\t\tgu_IN.UTF-8\n"..., 4096) = 4096
 53 read(4, "o_KR.utf8:\t\t\t\t\tko_KR.UTF-8\nKO_KR"..., 4096) = 4096
 54 read(4, "o_NO.utf8:\t\t\t\t\tno_NO.UTF-8\nnr:\t\t"..., 4096) = 4096
 55 read(4, "O8859-2\nsl_SI.iso88592:\t\t\t\t\tsl_S"..., 4096) = 4096
 56 read(4, "N\nvi_VN.tcvn5712:\t\t\t\t\tvi_VN.TCVN"..., 4096) = 4096
 57 read(4, "9-9\nturkish.iso88599:\t\t\t\ttr_TR.I"..., 4096) = 456
 58 read(4, "", 4096)                       = 0
 59 fstat64(4, {st_mode=S_IFREG|0644, st_size=40344, ...}) = 0
 60 read(4, "#\t$XdotOrg: lib/X11/nls/locale.d"..., 4096) = 4096
 61 read(4, "59-1/XLC_LOCALE\t\t\tes_AR.ISO8859-"..., 4096) = 4096
 62 read(4, "_FR.ISO8859-1\niso8859-15/XLC_LOC"..., 4096) = 4096
 63 read(4, "8/XLC_LOCALE\t\t\tbe_BY.UTF-8\nen_US"..., 4096) = 4096
 64 read(4, "en_US.UTF-8/XLC_LOCALE\t\t\tlo_LA.U"..., 4096) = 4096
 65 fstat64(4, {st_mode=S_IFREG|0644, st_size=772, ...}) = 0
 66 read(4, "#  $Xorg: C,v 1.3 2000/08/17 19:"..., 4096) = 772
 67 read(4, "", 4096)                       = 0
 68 fstat64(4, {st_mode=S_IFREG|0644, st_size=2570, ...}) = 0
 69 read(4, "# Locale name alias data base.\n#"..., 4096) = 2570
 70 read(4, "", 4096)                       = 0
 71 fstat64(4, {st_mode=S_IFREG|0644, st_size=373, ...}) = 0
 72 fstat64(4, {st_mode=S_IFREG|0644, st_size=26048, ...}) = 0
 73 fstat64(4, {st_mode=S_IFREG|0644, st_size=23, ...}) = 0
 74 fstat64(4, {st_mode=S_IFREG|0644, st_size=59, ...}) = 0
 75 fstat64(4, {st_mode=S_IFREG|0644, st_size=155, ...}) = 0
 76 fstat64(4, {st_mode=S_IFREG|0644, st_size=77, ...}) = 0
 77 fstat64(4, {st_mode=S_IFREG|0644, st_size=34, ...}) = 0
 78 fstat64(4, {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
 79 fstat64(4, {st_mode=S_IFREG|0644, st_size=52, ...}) = 0
 80 fstat64(4, {st_mode=S_IFREG|0644, st_size=286, ...}) = 0
 81 fstat64(4, {st_mode=S_IFREG|0644, st_size=966938, ...}) = 0
 82 fstat64(4, {st_mode=S_IFREG|0644, st_size=2454, ...}) = 0
 83 fstat64(4, {st_mode=S_IFREG|0644, st_size=54, ...}) = 0
 84 fstat64(4, {st_mode=S_IFREG|0644, st_size=256316, ...}) = 0
 85 fstat64(4, {st_mode=S_IFREG|0644, st_size=82376, ...}) = 0
 86 read(4, "#\t$XdotOrg: lib/X11/nls/locale.a"..., 4096) = 4096
 87 read(4, "\t\t\t\t\tbg_BG.UTF-8\nbn_IN.utf8\t\t\t\t\t"..., 4096) = 4096
 88 read(4, "8859-15\t\t\t\tde_CH.ISO8859-15\nde_C"..., 4096) = 4096
 89 read(4, "_ZA.ISO8859-1\nen_ZA.ISO-8859-1\t\t"..., 4096) = 4096
 90 read(4, "85915\t\t\t\teu_ES.ISO8859-15\neu_ES."..., 4096) = 4096
 91 read(4, "14\ngv_GB.ISO-8859-14\t\t\t\tgv_GB.IS"..., 4096) = 4096
 92 read(4, "w_GB.ISO8859-1\nkw_GB.ISO-8859-1\t"..., 4096) = 4096
 93 read(4, ".ISO8859-1\noc\t\t\t\t\t\toc_FR.ISO8859"..., 4096) = 4096
 94 read(4, "r_RS.UTF-8\[email protected]\t"..., 4096) = 4096
 95 read(4, "yi_US.CP1255\nzh_CN\t\t\t\t\t\tzh_CN.gb"..., 4096) = 4096
 96 read(4, ":00 dawes Exp $\n#\n\nPOSIX:\t\t\t\t\t\tC"..., 4096) = 4096
 97 read(4, "4:\t\t\t\tbr_FR.ISO8859-14\nbr_FR.ISO"..., 4096) = 4096
 98 read(4, "de_DE.88591.en:\t\t\t\t\tde_DE.ISO885"..., 4096) = 4096
 99 read(4, "59-1:\t\t\t\ten_ZA.ISO8859-1\nen_ZA.I"..., 4096) = 4096
100 read(4, "_ES.ISO8859-1\neu_ES.iso88591:\t\t\t"..., 4096) = 4096
101 read(4, "F-8\ngu_IN.utf8:\t\t\t\t\tgu_IN.UTF-8\n"..., 4096) = 4096
102 read(4, "o_KR.utf8:\t\t\t\t\tko_KR.UTF-8\nKO_KR"..., 4096) = 4096
103 read(4, "o_NO.utf8:\t\t\t\t\tno_NO.UTF-8\nnr:\t\t"..., 4096) = 4096
104 read(4, "O8859-2\nsl_SI.iso88592:\t\t\t\t\tsl_S"..., 4096) = 4096
105 read(4, "N\nvi_VN.tcvn5712:\t\t\t\t\tvi_VN.TCVN"..., 4096) = 4096
106 read(4, "9-9\nturkish.iso88599:\t\t\t\ttr_TR.I"..., 4096) = 456
107 read(4, "", 4096)                       = 0
108 fstat64(4, {st_mode=S_IFREG|0644, st_size=82376, ...}) = 0
109 read(4, "#\t$XdotOrg: lib/X11/nls/locale.a"..., 4096) = 4096
110 read(4, "\t\t\t\t\tbg_BG.UTF-8\nbn_IN.utf8\t\t\t\t\t"..., 4096) = 4096
111 read(4, "8859-15\t\t\t\tde_CH.ISO8859-15\nde_C"..., 4096) = 4096
112 read(4, "_ZA.ISO8859-1\nen_ZA.ISO-8859-1\t\t"..., 4096) = 4096
113 read(4, "85915\t\t\t\teu_ES.ISO8859-15\neu_ES."..., 4096) = 4096
114 read(4, "14\ngv_GB.ISO-8859-14\t\t\t\tgv_GB.IS"..., 4096) = 4096
115 read(4, "w_GB.ISO8859-1\nkw_GB.ISO-8859-1\t"..., 4096) = 4096
116 read(4, ".ISO8859-1\noc\t\t\t\t\t\toc_FR.ISO8859"..., 4096) = 4096
117 read(4, "r_RS.UTF-8\[email protected]\t"..., 4096) = 4096
118 read(4, "yi_US.CP1255\nzh_CN\t\t\t\t\t\tzh_CN.gb"..., 4096) = 4096
119 read(4, ":00 dawes Exp $\n#\n\nPOSIX:\t\t\t\t\t\tC"..., 4096) = 4096
120 read(4, "4:\t\t\t\tbr_FR.ISO8859-14\nbr_FR.ISO"..., 4096) = 4096
121 read(4, "de_DE.88591.en:\t\t\t\t\tde_DE.ISO885"..., 4096) = 4096
122 fstat64(4, {st_mode=S_IFREG|0644, st_size=40344, ...}) = 0
123 read(4, "#\t$XdotOrg: lib/X11/nls/locale.d"..., 4096) = 4096
124 read(4, "59-1/XLC_LOCALE\t\t\tes_AR.ISO8859-"..., 4096) = 4096
125 read(4, "_FR.ISO8859-1\niso8859-15/XLC_LOC"..., 4096) = 4096
126 read(4, "8/XLC_LOCALE\t\t\tbe_BY.UTF-8\nen_US"..., 4096) = 4096
127 read(4, "en_US.UTF-8/XLC_LOCALE\t\t\tlo_LA.U"..., 4096) = 4096
128 read(4, "LE:\t\t\taf_ZA.ISO8859-1\niso8859-15"..., 4096) = 4096
129 read(4, "LE:\t\t\tes_MX.ISO8859-1\niso8859-1/"..., 4096) = 4096
130 read(4, "9-2/XLC_LOCALE:\t\t\tpl_PL.ISO8859-"..., 4096) = 4096
131 read(4, "           byn_ER.UTF-8\nen_US.UT"..., 4096) = 4096
132 fstat64(4, {st_mode=S_IFREG|0644, st_size=4287, ...}) = 0
133 read(4, "#  $XFree86: xc/nls/XLC_LOCALE/e"..., 4096) = 4096
134 read(4, "GB2312.1980-0:GL; GB2312.1980-0:"..., 4096) = 191
135 read(4, "", 4096)                       = 0
136 fstat64(4, {st_mode=S_IFREG|0644, st_size=18236, ...}) = 0
137 read(4, "\n! -----------------------------"..., 18236) = 18236
138 fcntl64(4, F_GETFD)                     = 0x1 (flags FD_CLOEXEC)
139 getdents64(4, /* 19 entries */, 32768)  = 640
140 getdents64(4, /* 0 entries */, 32768)   = 0
141 read(4, "\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"..., 512) = 512
142 fstat64(4, {st_mode=S_IFREG|0644, st_size=5396, ...}) = 0
143 read(4, "\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"..., 512) = 512
144 fstat64(4, {st_mode=S_IFREG|0644, st_size=13944, ...}) = 0
145 read(4, "\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"..., 512) = 512
146 fstat64(4, {st_mode=S_IFREG|0644, st_size=5396, ...}) = 0
147 read(4, "\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"..., 512) = 512
148 fstat64(4, {st_mode=S_IFREG|0644, st_size=22128, ...}) = 0
149 read(4, "\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"..., 512) = 512
150 fstat64(4, {st_mode=S_IFREG|0644, st_size=75356, ...}) = 0
151 fstat64(4, {st_mode=S_IFREG|0644, st_size=71399, ...}) = 0
152 read(4, "\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"..., 512) = 512
153 fstat64(4, {st_mode=S_IFREG|0644, st_size=38144, ...}) = 0
154 read(4, "\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3\0\3\0\1\0\0\[email protected]\22\0\0004\0\0\0"..., 512) = 512
155 fstat64(4, {st_mode=S_IFREG|0644, st_size=103964, ...}) = 0
156 read(4, "\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"..., 512) = 512
157 fstat64(4, {st_mode=S_IFREG|0644, st_size=44184, ...}) = 0
158 read(4, "\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"..., 512) = 512
159 fstat64(4, {st_mode=S_IFREG|0644, st_size=5640, ...}) = 0
160 read(4, "\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"..., 512) = 512
161 fstat64(4, {st_mode=S_IFREG|0644, st_size=5496, ...}) = 0
162 read(4, "\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"..., 512) = 512
163 fstat64(4, {st_mode=S_IFREG|0644, st_size=5400, ...}) = 0
164 read(4, "\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"..., 512) = 512
165 fstat64(4, {st_mode=S_IFREG|0644, st_size=9640, ...}) = 0
166 read(4, "\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"..., 512) = 512
167 fstat64(4, {st_mode=S_IFREG|0644, st_size=5400, ...}) = 0
168 read(4, "\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"..., 512) = 512
169 fstat64(4, {st_mode=S_IFREG|0644, st_size=5400, ...}) = 0
170 read(4, "\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"..., 512) = 512
171 fstat64(4, {st_mode=S_IFREG|0644, st_size=9640, ...}) = 0
172 read(4, "\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"..., 512) = 512
173 fstat64(4, {st_mode=S_IFREG|0644, st_size=9704, ...}) = 0
174 fstat64(4, {st_mode=S_IFREG|0644, st_size=71399, ...}) = 0
175 read(4, "\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"..., 512) = 512
176 fstat64(4, {st_mode=S_IFREG|0644, st_size=121640, ...}) = 0
177 read(4, "\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"..., 512) = 512
178 fstat64(4, {st_mode=S_IFREG|0644, st_size=13992, ...}) = 0
179 read(4, "\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"..., 512) = 512
180 fstat64(4, {st_mode=S_IFREG|0644, st_size=13776, ...}) = 0
181 read(4, "\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"..., 512) = 512
182 fstat64(4, {st_mode=S_IFREG|0644, st_size=9588, ...}) = 0
183 fstat64(4, {st_mode=S_IFREG|0644, st_size=8982, ...}) = 0
184 read(4, "! $Xorg: XKeysymDB,v 1.3 2000/08"..., 8982) = 8982
185 ioctl(4, VIDIOC_QUERYCAP or VT_OPENQRY, 0x9b40998) = -1 EINVAL (Invalid argument)
186 ioctl(4, VIDIOC_QUERYCAP or VT_OPENQRY, 0xbfaccd44) = 0
187 ioctl(4, VIDIOC_G_FMT or VT_SENDSIG, 0xbfaccc78) = 0
188 ioctl(4, VIDIOC_ENUM_FMT or VT_SETMODE, 0xbfaccbec) = 0
189 ioctl(4, 0xc02c564a, 0xbfaccb58)        = -1 EINVAL (Invalid argument)
190 ioctl(4, VIDIOC_ENUM_FMT or VT_SETMODE, 0xbfaccbec) = 0
191 ioctl(4, 0xc02c564a, 0xbfaccb58)        = -1 EINVAL (Invalid argument)
192 ioctl(4, VIDIOC_ENUM_FMT or VT_SETMODE, 0xbfaccbec) = 0
193 ioctl(4, 0xc02c564a, 0xbfaccb58)        = -1 EINVAL (Invalid argument)
194 ioctl(4, VIDIOC_ENUM_FMT or VT_SETMODE, 0xbfaccbec) = 0
195 ioctl(4, VIDIOC_ENUM_FMT or VT_SETMODE, 0xbfaccbec) = 0
196 ioctl(4, VIDIOC_ENUM_FMT or VT_SETMODE, 0xbfaccbec) = 0
197 ioctl(4, VIDIOC_ENUM_FMT or VT_SETMODE, 0xbfaccbec) = -1 EINVAL (Invalid argument)
198 ioctl(4, VIDIOC_QUERYCAP or VT_OPENQRY, 0xbfaccb84) = 0
199 ioctl(4, VIDIOC_G_INPUT, 0xbfacca2c)    = 0
200 ioctl(4, VIDIOC_ENUMINPUT, 0xbfacca2c)  = 0
201 fstat64(4, {st_mode=S_IFCHR|0660, st_rdev=makedev(81, 0), ...}) = 0
202 ioctl(4, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0xbfacca78) = -1 EINVAL (Invalid argument)
203 ioctl(4, VIDIOC_QUERYCAP or VT_OPENQRY, 0x9b40998) = 0
204 fcntl64(4, F_SETFD, FD_CLOEXEC)         = 0
205 ioctl(4, VIDIOC_ENUMINPUT, 0x9b40acc)   = 0
206 ioctl(4, VIDIOC_ENUMINPUT, 0x9b40b18)   = 0
207 ioctl(4, VIDIOC_ENUMINPUT, 0x9b40b64)   = 0
208 ioctl(4, VIDIOC_ENUMINPUT, 0x9b40bb0)   = 0
209 ioctl(4, VIDIOC_ENUMINPUT, 0x9b40bfc)   = -1 EINVAL (Invalid argument)
210 ioctl(4, VIDIOC_ENUMSTD, 0x9b40f8c)     = 0
211 ioctl(4, VIDIOC_ENUMSTD, 0x9b40fcc)     = 0
212 ioctl(4, VIDIOC_ENUMSTD, 0x9b4100c)     = 0
213 ioctl(4, VIDIOC_ENUMSTD, 0x9b4104c)     = 0
214 ioctl(4, VIDIOC_ENUMSTD, 0x9b4108c)     = 0
215 ioctl(4, VIDIOC_ENUMSTD, 0x9b410cc)     = 0
216 ioctl(4, VIDIOC_ENUMSTD, 0x9b4110c)     = 0
217 ioctl(4, VIDIOC_ENUMSTD, 0x9b4114c)     = -1 EINVAL (Invalid argument)
218 ioctl(4, VIDIOC_ENUM_FMT or VT_SETMODE, 0x9b4138c) = 0
219 ioctl(4, VIDIOC_ENUM_FMT or VT_SETMODE, 0x9b413cc) = 0
220 ioctl(4, VIDIOC_ENUM_FMT or VT_SETMODE, 0x9b4140c) = 0
221 ioctl(4, VIDIOC_ENUM_FMT or VT_SETMODE, 0x9b4144c) = 0
222 ioctl(4, VIDIOC_ENUM_FMT or VT_SETMODE, 0x9b4148c) = 0
223 ioctl(4, VIDIOC_ENUM_FMT or VT_SETMODE, 0x9b414cc) = 0
224 ioctl(4, VIDIOC_G_PARM, 0x9b40a00)      = 0
225 ioctl(4, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b41b8c) = 0
226 ioctl(4, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b41bd0) = 0
227 ioctl(4, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b41c14) = 0
228 ioctl(4, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b41c58) = 0
229 ioctl(4, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b41c9c) = -1 EINVAL (Invalid argument)
230 ioctl(4, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b41ce0) = 0
231 ioctl(4, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b41d24) = -1 EINVAL (Invalid argument)
232 ioctl(4, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b41d68) = -1 EINVAL (Invalid argument)
233 ioctl(4, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b41dac) = -1 EINVAL (Invalid argument)
234 ioctl(4, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b41df0) = -1 EINVAL (Invalid argument)
235 ioctl(4, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b41e34) = -1 EINVAL (Invalid argument)
236 ioctl(4, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b41e78) = -1 EINVAL (Invalid argument)
237 ioctl(4, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b41ebc) = -1 EINVAL (Invalid argument)
238 ioctl(4, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b41f00) = -1 EINVAL (Invalid argument)
239 ioctl(4, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b41f44) = -1 EINVAL (Invalid argument)
240 ioctl(4, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b41f88) = -1 EINVAL (Invalid argument)
241 ioctl(4, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b41fcc) = -1 EINVAL (Invalid argument)
242 ioctl(4, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b42010) = -1 EINVAL (Invalid argument)
243 ioctl(4, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b42054) = -1 EINVAL (Invalid argument)
244 ioctl(4, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b42098) = -1 EINVAL (Invalid argument)
245 ioctl(4, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b420dc) = -1 EINVAL (Invalid argument)
246 ioctl(4, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b42120) = -1 EINVAL (Invalid argument)
247 ioctl(4, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b42164) = -1 EINVAL (Invalid argument)
248 ioctl(4, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b421a8) = -1 EINVAL (Invalid argument)
249 ioctl(4, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b421ec) = -1 EINVAL (Invalid argument)
250 ioctl(4, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b42230) = -1 EINVAL (Invalid argument)
251 ioctl(4, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b42274) = -1 EINVAL (Invalid argument)
252 ioctl(4, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b422b8) = -1 EINVAL (Invalid argument)
253 ioctl(4, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b422fc) = -1 EINVAL (Invalid argument)
254 ioctl(4, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b42340) = -1 EINVAL (Invalid argument)
255 ioctl(4, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b42384) = -1 EINVAL (Invalid argument)
256 ioctl(4, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b423c8) = -1 EINVAL (Invalid argument)
257 ioctl(4, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b4240c) = -1 EINVAL (Invalid argument)
258 ioctl(4, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b42450) = -1 EINVAL (Invalid argument)
259 ioctl(4, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b42494) = -1 EINVAL (Invalid argument)
260 ioctl(4, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b424d8) = -1 EINVAL (Invalid argument)
261 ioctl(4, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b4251c) = -1 EINVAL (Invalid argument)
262 ioctl(4, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b42560) = -1 EINVAL (Invalid argument)
263 ioctl(4, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b425a4) = -1 EINVAL (Invalid argument)
264 ioctl(4, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b425e8) = -1 EINVAL (Invalid argument)
265 ioctl(4, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b4262c) = -1 EINVAL (Invalid argument)
266 ioctl(4, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b42670) = -1 EINVAL (Invalid argument)
267 ioctl(4, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b426b4) = -1 EINVAL (Invalid argument)
268 ioctl(4, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b426f8) = -1 EINVAL (Invalid argument)
269 ioctl(4, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b4273c) = -1 EINVAL (Invalid argument)
270 ioctl(4, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b42780) = -1 EINVAL (Invalid argument)
271 ioctl(4, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b427c4) = -1 EINVAL (Invalid argument)
272 ioctl(4, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b42808) = -1 EINVAL (Invalid argument)
273 ioctl(4, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b4284c) = -1 EINVAL (Invalid argument)
274 ioctl(4, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b42890) = -1 EINVAL (Invalid argument)
275 ioctl(4, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b428d4) = -1 EINVAL (Invalid argument)
276 ioctl(4, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b42918) = -1 EINVAL (Invalid argument)
277 ioctl(4, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b4295c) = -1 EINVAL (Invalid argument)
278 ioctl(4, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b429a0) = -1 EINVAL (Invalid argument)
279 ioctl(4, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b429e4) = -1 EINVAL (Invalid argument)
280 ioctl(4, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b42a28) = -1 EINVAL (Invalid argument)
281 ioctl(4, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b42a6c) = -1 EINVAL (Invalid argument)
282 ioctl(4, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b42ab0) = -1 EINVAL (Invalid argument)
283 ioctl(4, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b42af4) = -1 EINVAL (Invalid argument)
284 ioctl(4, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b42b38) = -1 EINVAL (Invalid argument)
285 ioctl(4, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b42b7c) = -1 EINVAL (Invalid argument)
286 ioctl(4, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b42bc0) = -1 EINVAL (Invalid argument)
287 ioctl(4, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b42c04) = -1 EINVAL (Invalid argument)
288 ioctl(4, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b42c48) = -1 EINVAL (Invalid argument)
289 ioctl(4, VIDIOC_G_STD, 0xbfacce08)      = 0
290 ioctl(4, VIDIOC_G_INPUT, 0xbfacce1c)    = 0
291 ioctl(4, MATROXFB_G_TVOCTRL or VIDIOC_G_CTRL, 0xbfacce14) = 0
292 ioctl(4, MATROXFB_G_TVOCTRL or VIDIOC_G_CTRL, 0xbfacce14) = 0
293 ioctl(4, MATROXFB_G_TVOCTRL or VIDIOC_G_CTRL, 0xbfacce14) = 0
294 ioctl(4, MATROXFB_G_TVOCTRL or VIDIOC_G_CTRL, 0xbfacce14) = 0
295 ioctl(4, MATROXFB_G_TVOCTRL or VIDIOC_G_CTRL, 0xbfacce24) = 0
296 ioctl(4, VIDIOC_TRY_FMT, 0x9b42ca4)     = 0
297 ioctl(4, VIDIOC_S_FMT or VT_RELDISP, 0xbfacc754) = 0
298 ioctl(4, VIDIOC_TRY_FMT, 0x9b42ca4)     = 0
299 ioctl(4, VIDIOC_REQBUFS or VT_DISALLOCATE, 0x9b42d80) = 0
300 ioctl(4, VIDIOC_QUERYBUF or VT_RESIZE, 0x9b42d94) = 0
301 ioctl(4, VIDIOC_QUERYBUF or VT_RESIZE, 0x9b42dd8) = 0
302 ioctl(4, VIDIOC_QBUF, 0x9b42d94)        = 0
303 ioctl(4, VIDIOC_QBUF, 0x9b42dd8)        = 0
304 ioctl(4, VIDIOC_STREAMON, 0xbfacca2c)   = 0
305 ioctl(4, MATROXFB_S_TVOCTRL or VIDIOC_S_CTRL, 0xbfacce10) = 0
306 ioctl(4, MATROXFB_S_TVOCTRL or VIDIOC_S_CTRL, 0xbfacce10) = 0
307 ioctl(4, MATROXFB_S_TVOCTRL or VIDIOC_S_CTRL, 0xbfacce10) = 0
308 ioctl(4, MATROXFB_S_TVOCTRL or VIDIOC_S_CTRL, 0xbfacce10) = 0
309 ioctl(4, VIDIOC_S_INPUT, 0xbfacce84)    = 0
310 ioctl(4, VIDIOC_S_STD, 0x9b40f90)       = 0
311 ioctl(4, VIDIOC_DQBUF, 0xbfaccd00)      = 0
312 ioctl(4, VIDIOC_QBUF, 0x9b42d94)        = 0
313 ioctl(4, VIDIOC_DQBUF, 0xbfaccd00)      = 0
314 ioctl(4, VIDIOC_QBUF, 0x9b42dd8)        = 0
315 ioctl(4, VIDIOC_DQBUF, 0xbfaccd00)      = 0
316 ioctl(4, VIDIOC_QBUF, 0x9b42d94)        = 0
317 ioctl(4, VIDIOC_DQBUF, 0xbfaccd00)      = 0
318 ioctl(4, VIDIOC_QBUF, 0x9b42dd8)        = 0
319 ioctl(4, VIDIOC_DQBUF, 0xbfaccd00)      = 0
320 ioctl(4, VIDIOC_QBUF, 0x9b42d94)        = 0
321 ioctl(4, VIDIOC_DQBUF, 0xbfaccd00)      = 0
322 ioctl(4, VIDIOC_QBUF, 0x9b42dd8)        = 0
323 ioctl(4, VIDIOC_DQBUF, 0xbfaccd00)      = 0
324 ioctl(4, VIDIOC_QBUF, 0x9b42d94)        = 0
325 ioctl(4, VIDIOC_DQBUF, 0xbfaccd00)      = 0
326 ioctl(4, VIDIOC_QBUF, 0x9b42dd8)        = 0
327 ioctl(4, VIDIOC_DQBUF, 0xbfaccd00)      = 0
328 ioctl(4, VIDIOC_QBUF, 0x9b42d94)        = 0
329 ioctl(4, VIDIOC_DQBUF, 0xbfaccd00)      = 0
330 ioctl(4, VIDIOC_QBUF, 0x9b42dd8)        = 0
331 ioctl(4, VIDIOC_DQBUF, 0xbfaccd00)      = 0
332 ioctl(4, VIDIOC_QBUF, 0x9b42d94)        = 0
333 ioctl(4, VIDIOC_DQBUF, 0xbfaccd00)      = 0
334 ioctl(4, VIDIOC_QBUF, 0x9b42dd8)        = 0
335 ioctl(4, VIDIOC_DQBUF, 0xbfaccd00)      = 0
336 ioctl(4, VIDIOC_QBUF, 0x9b42d94)        = 0
337 ioctl(4, VIDIOC_DQBUF, 0xbfaccd00)      = 0
338 ioctl(4, VIDIOC_QBUF, 0x9b42dd8)        = 0
339 ioctl(4, VIDIOC_DQBUF, 0xbfaccd00)      = 0
340 ioctl(4, VIDIOC_QBUF, 0x9b42d94)        = 0
341 ioctl(4, VIDIOC_DQBUF, 0xbfaccd00)      = 0
342 ioctl(4, VIDIOC_QBUF, 0x9b42dd8)        = 0
343 ioctl(4, VIDIOC_DQBUF, 0xbfaccd00)      = 0
344 ioctl(4, VIDIOC_QBUF, 0x9b42d94)        = 0
345 ioctl(4, VIDIOC_DQBUF, 0xbfaccd00)      = 0
346 ioctl(4, VIDIOC_QBUF, 0x9b42dd8)        = 0
347 ioctl(4, VIDIOC_DQBUF, 0xbfaccd00)      = 0
348 ioctl(4, VIDIOC_QBUF, 0x9b42d94)        = 0
349 ioctl(4, VIDIOC_DQBUF, 0xbfaccd00)      = 0
350 ioctl(4, VIDIOC_QBUF, 0x9b42dd8)        = 0
351 ioctl(4, VIDIOC_DQBUF, 0xbfaccd00)      = 0
352 ioctl(4, VIDIOC_QBUF, 0x9b42d94)        = 0
353 ioctl(4, VIDIOC_DQBUF, 0xbfaccd00)      = 0
354 ioctl(4, VIDIOC_QBUF, 0x9b42dd8)        = 0
355 ioctl(4, VIDIOC_DQBUF, 0xbfaccd00)      = 0
356 ioctl(4, VIDIOC_QBUF, 0x9b42d94)        = 0
357 ioctl(4, VIDIOC_DQBUF, 0xbfaccd00)      = 0
358 ioctl(4, VIDIOC_QBUF, 0x9b42dd8)        = 0
359 ioctl(4, VIDIOC_DQBUF, 0xbfaccd00)      = 0
360 ioctl(4, VIDIOC_QBUF, 0x9b42d94)        = 0
361 ioctl(4, VIDIOC_DQBUF, 0xbfaccd00)      = 0
362 ioctl(4, VIDIOC_QBUF, 0x9b42dd8)        = 0
363 ioctl(4, VIDIOC_DQBUF, 0xbfaccd00)      = 0
364 ioctl(4, VIDIOC_QBUF, 0x9b42d94)        = 0
365 ioctl(4, VIDIOC_DQBUF, 0xbfaccd00)      = 0
366 ioctl(4, VIDIOC_QBUF, 0x9b42dd8)        = 0
367 ioctl(4, VIDIOC_DQBUF, 0xbfaccd00)      = 0
368 ioctl(4, VIDIOC_QBUF, 0x9b42d94)        = 0
369 ioctl(4, VIDIOC_DQBUF, 0xbfaccd00)      = 0
370 ioctl(4, VIDIOC_QBUF, 0x9b42dd8)        = 0
371 ioctl(4, VIDIOC_DQBUF, 0xbfaccd00)      = 0
372 ioctl(4, VIDIOC_QBUF, 0x9b42d94)        = 0
373 ioctl(4, VIDIOC_DQBUF, 0xbfaccd00)      = 0
374 ioctl(4, VIDIOC_QBUF, 0x9b42dd8)        = 0
375 ioctl(4, VIDIOC_DQBUF, 0xbfaccd00)      = 0
376 ioctl(4, VIDIOC_QBUF, 0x9b42d94)        = 0
377 ioctl(4, VIDIOC_DQBUF, 0xbfaccd00)      = 0
378 ioctl(4, VIDIOC_QBUF, 0x9b42dd8)        = 0
379 ioctl(4, VIDIOC_DQBUF, 0xbfaccd00)      = 0
380 ioctl(4, VIDIOC_QBUF, 0x9b42d94)        = 0
381 ioctl(4, VIDIOC_DQBUF, 0xbfaccd00)      = 0
382 ioctl(4, VIDIOC_QBUF, 0x9b42dd8)        = 0
383 ioctl(4, VIDIOC_DQBUF, 0xbfaccd00)      = 0
384 ioctl(4, VIDIOC_QBUF, 0x9b42d94)        = 0
385 ioctl(4, VIDIOC_DQBUF, 0xbfaccd00)      = 0
386 ioctl(4, VIDIOC_QBUF, 0x9b42dd8)        = 0
387 ioctl(4, VIDIOC_DQBUF, 0xbfaccd00)      = 0
388 ioctl(4, VIDIOC_QBUF, 0x9b42d94)        = 0
389 ioctl(4, VIDIOC_DQBUF, 0xbfaccd00)      = 0
390 ioctl(4, VIDIOC_QBUF, 0x9b42dd8)        = 0
391 ioctl(4, VIDIOC_DQBUF, 0xbfaccd00)      = 0
392 ioctl(4, VIDIOC_QBUF, 0x9b42d94)        = 0
393 ioctl(4, VIDIOC_DQBUF, 0xbfaccd00)      = 0
394 ioctl(4, VIDIOC_QBUF, 0x9b42dd8)        = 0
395 ioctl(4, VIDIOC_DQBUF, 0xbfaccd00)      = 0
396 ioctl(4, VIDIOC_QBUF, 0x9b42d94)        = 0
397 ioctl(4, VIDIOC_DQBUF, 0xbfaccd00)      = 0
398 ioctl(4, VIDIOC_QBUF, 0x9b42dd8)        = 0
399 ioctl(4, VIDIOC_DQBUF, 0xbfaccd00)      = 0
400 ioctl(4, VIDIOC_QBUF, 0x9b42d94)        = 0
401 ioctl(4, VIDIOC_DQBUF, 0xbfaccd00)      = 0
402 ioctl(4, VIDIOC_QBUF, 0x9b42dd8)        = 0
403 ioctl(4, VIDIOC_DQBUF, 0xbfaccd00)      = 0
404 ioctl(4, VIDIOC_QBUF, 0x9b42d94)        = 0
405 ioctl(4, VIDIOC_DQBUF, 0xbfaccd00)      = 0
406 ioctl(4, VIDIOC_QBUF, 0x9b42dd8)        = 0
407 ioctl(4, VIDIOC_DQBUF, 0xbfaccd00)      = 0
408 ioctl(4, VIDIOC_QBUF, 0x9b42d94)        = 0
409 ioctl(4, VIDIOC_DQBUF, 0xbfaccd00)      = 0
410 ioctl(4, VIDIOC_QBUF, 0x9b42dd8)        = 0
411 ioctl(4, VIDIOC_DQBUF, 0xbfaccd00)      = 0
412 ioctl(4, VIDIOC_QBUF, 0x9b42d94)        = 0
413 ioctl(4, VIDIOC_DQBUF, 0xbfaccd00)      = 0
414 ioctl(4, VIDIOC_QBUF, 0x9b42dd8)        = 0
415 ioctl(4, VIDIOC_DQBUF, 0xbfaccd00)      = 0
416 ioctl(4, VIDIOC_QBUF, 0x9b42d94)        = 0
417 ioctl(4, VIDIOC_DQBUF, 0xbfaccd00)      = 0
418 ioctl(4, VIDIOC_QBUF, 0x9b42dd8)        = 0
419 ioctl(4, VIDIOC_DQBUF, 0xbfaccd00)      = 0
420 ioctl(4, VIDIOC_QBUF, 0x9b42d94)        = 0
421 ioctl(4, VIDIOC_DQBUF, 0xbfaccd00)      = 0
422 ioctl(4, VIDIOC_QBUF, 0x9b42dd8)        = 0
423 ioctl(4, VIDIOC_DQBUF, 0xbfaccd00)      = 0
424 ioctl(4, VIDIOC_QBUF, 0x9b42d94)        = 0
425 ioctl(4, VIDIOC_DQBUF, 0xbfaccd00)      = 0
426 ioctl(4, VIDIOC_QBUF, 0x9b42dd8)        = 0
427 ioctl(4, VIDIOC_DQBUF, 0xbfaccd00)      = 0
428 ioctl(4, VIDIOC_QBUF, 0x9b42d94)        = 0
429 ioctl(4, VIDIOC_DQBUF, 0xbfaccd00)      = 0
430 ioctl(4, VIDIOC_QBUF, 0x9b42dd8)        = 0
431 ioctl(4, VIDIOC_DQBUF, 0xbfaccd00)      = 0
432 ioctl(4, VIDIOC_QBUF, 0x9b42d94)        = 0
433 ioctl(4, VIDIOC_DQBUF, 0xbfaccd00)      = 0
434 ioctl(4, VIDIOC_QBUF, 0x9b42dd8)        = 0
435 ioctl(4, VIDIOC_DQBUF, 0xbfaccd00)      = 0
436 ioctl(4, VIDIOC_QBUF, 0x9b42d94)        = 0
437 ioctl(4, VIDIOC_DQBUF, 0xbfaccd00)      = 0
438 ioctl(4, VIDIOC_QBUF, 0x9b42dd8)        = 0
439 ioctl(4, VIDIOC_DQBUF, 0xbfaccd00)      = 0
440 ioctl(4, VIDIOC_QBUF, 0x9b42d94)        = 0
441 ioctl(4, VIDIOC_DQBUF, 0xbfaccd00)      = 0
442 ioctl(4, VIDIOC_QBUF, 0x9b42dd8)        = 0
443 ioctl(4, VIDIOC_DQBUF, 0xbfaccd00)      = 0
444 ioctl(4, VIDIOC_QBUF, 0x9b42d94)        = 0
445 ioctl(4, VIDIOC_DQBUF, 0xbfaccd00)      = 0
446 ioctl(4, VIDIOC_QBUF, 0x9b42dd8)        = 0
447 ioctl(4, VIDIOC_DQBUF, 0xbfaccd00)      = 0
448 ioctl(4, VIDIOC_QBUF, 0x9b42d94)        = 0
449 ioctl(4, VIDIOC_DQBUF, 0xbfaccd00)      = 0
450 ioctl(4, VIDIOC_QBUF, 0x9b42dd8)        = 0
451 ioctl(4, VIDIOC_DQBUF, 0xbfaccd00)      = 0
452 ioctl(4, VIDIOC_QBUF, 0x9b42d94)        = 0
453 ioctl(4, VIDIOC_DQBUF, 0xbfaccd00)      = 0
454 ioctl(4, VIDIOC_QBUF, 0x9b42dd8)        = 0
455 ioctl(4, VIDIOC_DQBUF, 0xbfaccd00)      = 0
456 ioctl(4, VIDIOC_QBUF, 0x9b42d94)        = 0
457 ioctl(4, VIDIOC_DQBUF, 0xbfaccd00)      = 0
458 ioctl(4, VIDIOC_QBUF, 0x9b42dd8)        = 0
459 ioctl(4, VIDIOC_DQBUF, 0xbfaccd00)      = 0
460 ioctl(4, VIDIOC_QBUF, 0x9b42d94)        = 0
461 ioctl(4, VIDIOC_DQBUF, 0xbfaccd00)      = 0
462 ioctl(4, VIDIOC_QBUF, 0x9b42dd8)        = 0
463 ioctl(4, VIDIOC_DQBUF, 0xbfaccd00)      = 0
464 ioctl(4, VIDIOC_QBUF, 0x9b42d94)        = 0
465 ioctl(4, VIDIOC_DQBUF, 0xbfaccd00)      = 0
466 ioctl(4, VIDIOC_QBUF, 0x9b42dd8)        = 0
467 ioctl(4, VIDIOC_DQBUF, 0xbfaccd00)      = 0
468 ioctl(4, VIDIOC_QBUF, 0x9b42d94)        = 0
469 ioctl(4, VIDIOC_DQBUF, 0xbfaccd00)      = 0
470 ioctl(4, VIDIOC_QBUF, 0x9b42dd8)        = 0
471 ioctl(4, VIDIOC_DQBUF, 0xbfaccd00)      = 0
472 ioctl(4, VIDIOC_QBUF, 0x9b42d94)        = 0
473 ioctl(4, VIDIOC_DQBUF, 0xbfaccd00)      = 0
474 ioctl(4, VIDIOC_QBUF, 0x9b42dd8)        = 0
475 ioctl(4, VIDIOC_DQBUF, 0xbfaccd00)      = 0
476 ioctl(4, VIDIOC_QBUF, 0x9b42d94)        = 0
477 ioctl(4, VIDIOC_DQBUF, 0xbfaccd00)      = 0
478 ioctl(4, VIDIOC_QBUF, 0x9b42dd8)        = 0
479 ioctl(4, VIDIOC_DQBUF, 0xbfaccd00)      = 0
480 ioctl(4, VIDIOC_QBUF, 0x9b42d94)        = 0
481 ioctl(4, VIDIOC_DQBUF, 0xbfaccd00)      = 0
482 ioctl(4, VIDIOC_STREAMOFF, 0xbfacc53c)  = 0
483 ioctl(4, VIDIOC_QUERYBUF or VT_RESIZE, 0xbfacc48c) = 0
484 ioctl(4, VIDIOC_QUERYBUF or VT_RESIZE, 0xbfacc48c) = 0
485 ioctl(4, VIDIOC_REQBUFS or VT_DISALLOCATE, 0x9b42d80) = -1 EINVAL (Invalid argument)
486 ioctl(4, VIDIOC_TRY_FMT, 0x9b42ca4)     = -1 EINVAL (Invalid argument)
487 ioctl(4, VIDIOC_TRY_FMT, 0xbfacc000)    = 0
488 ioctl(4, VIDIOC_TRY_FMT, 0xbfacc000)    = 0
489 ioctl(4, VIDIOC_TRY_FMT, 0xbfacc000)    = 0
490 ioctl(4, VIDIOC_TRY_FMT, 0xbfacc000)    = 0
491 ioctl(4, VIDIOC_TRY_FMT, 0xbfacc000)    = 0
492 ioctl(4, VIDIOC_TRY_FMT, 0xbfacc000)    = 0
493 ioctl(4, VIDIOC_REQBUFS or VT_DISALLOCATE, 0xbfacc70c) = 0
494 ioctl(4, VIDIOC_QUERYBUF or VT_RESIZE, 0xbfacc61c) = 0
495 ioctl(4, VIDIOC_QUERYBUF or VT_RESIZE, 0xbfacc61c) = 0
496 ioctl(4, VIDIOC_QBUF, 0xbfacc61c)       = 0
497 ioctl(4, VIDIOC_QBUF, 0xbfacc61c)       = 0
498 ioctl(4, VIDIOC_STREAMON, 0xbfacc65c)   = 0
499 ioctl(4, VIDIOC_DQBUF, 0xbfacc6c8)      = 0
500 ioctl(4, VIDIOC_QBUF, 0xbfacc61c)       = 0

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

/** 3~10没有看到调用过程,可能源码和应用程序不一致,略 */

/** 11~15被get_device_capabilities(h)调用 */
for ()
  -> ioctl(4, VIDIOC_ENUMINPUT   /* 11. */
for ()
  -> ioctl(4, VIDIOC_ENUMSTD     /* 12.列举标准 */
for ()
  -> ioctl(4, VIDIOC_ENUM_FMT    /* 13. */
ioctl(4, VIDIOC_G_PARM           /* 14. */
for ()
  -> ioctl(4, VIDIOC_QUERYCTRL   /* 15. */

/** 16~18被v4l2_read_attr()调用 */
ioctl(4, VIDIOC_G_STD            /* 16.获取当前使用的标准 */
ioctl(4, VIDIOC_G_INPUT          /* 17. */
ioctl(4, VIDIOC_G_CTRL           /* 18.获取当前所使用的属性 */

/** 19~20被v4l2_overlay()调用 */
ioctl(4, VIDIOC_TRY_FMT          /* 19.测试摄像头支持的格式 */
ioctl(4, VIDIOC_S_FMT            /* 20.设置摄像头格式 */

/** 21~24被v4l2_start_streaming()调用 */
ioctl(4, VIDIOC_REQBUFS          /* 21.请求系统分配缓冲区 */
ioctl(4, VIDIOC_QUERYBUF         /* 22.查询所分配的缓冲区 */
for ()
  -> ioctl(4, VIDIOC_QBUF        /* 23.把缓冲区放入队列 */
  -> h->buf_me[i].data = mmap(...);
ioctl(4, VIDIOC_STREAMON         /* 24.启动摄像头 */

/** 25~27被v4l2_write_attr()调用 */
ioctl(4, VIDIOC_S_CTRL           /* 25.设置属性 */
ioctl(4, VIDIOC_S_INPUT          /* 26.设置输入源 */
ioctl(4, VIDIOC_S_STD            /* 27.设置标准 */

/** 28~29被v4l2_waiton()调用
 *  30被v4l2_queue_buffer()调用
 *  v4l2_waiton()和v4l2_queue_buffer()被v4l2_nextframe()调用
 */
for ()
  -> select(5, [4]               /* 28.查询文件描述符4是否有数据 */
  -> ioctl(4, VIDIOC_DQBUF       /* 29.把缓冲区从队列中取出 */
  -> /* 处理数据,之前使用mmap()获得了缓冲区的地址 */
  -> ioctl(4, VIDIOC_QBUF        /* 30.把缓冲区放入队列 */
/** 31被v4l2_stop_streaming()调用  */
ioctl(4, 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
};

六、数据的获取过程

(本节使用vivi.c文件分析,推荐读者使用百度网盘分享的linux/drivers/staging/media/solo6x10/v4l2.c分析,因为Linux-4.5所使用的vivi.c文件与老版内核差距较大)

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

ioctl(4, VIDIOC_REQBUFS
  /* 通过应用层传入的v4l2_requestbuffers构造并分配vb2_buffer */
  -> vidioc_reqbufs
    -> vb2_reqbufs
      -> call_qop(q, queue_setup, q, ...);
        -> queue_setup                /* 确定分配的buffer个数 */
      -> __vb2_queue_alloc(q, ...);   /* 分配vb2_buffer */

2. 查询映射缓冲区

ioctl(4, VIDIOC_QUERYBUF
  /* 通过vb2_buffer设置v4l2_buffer */
  -> vb2_querybuf
    -> __fill_v4l2_buffer(vb, b);
      -> memcpy(b, &vb->v4l2_buf, offsetof(struct v4l2_buffer, m));
      -> b->input = vb->v4l2_buf.input;
      -> b->flags &= ~V4L2_BUFFER_STATE_FLAGS;

3. 真正分配缓冲区

mmap(NULL, h->buf_v4l2[i].length, ...)
  -> vivi_mmap
    -> vb2_mmap(&dev->vb_vidq, vma);
      -> call_memop(q, mmap, ...) /* 调用vb2_queue->mem_ops->mmap,定义在vivi_create_instance()中 */
        -> vb2_vmalloc_mmap
          -> remap_vmalloc_range(vma, buf->vaddr, 0);

4. 把缓冲区放入队列

ioctl(4, VIDIOC_QBUF
  -> vidioc_qbuf
    -> vb2_qbuf(&dev->vb_vidq, p);
      -> list_add_tail(&vb->queued_entry, &q->queued_list);
      -> __fill_v4l2_buffer(vb, b);    /* 设置v4l2_buffer */

5. 启动摄像头

ioctl(4, VIDIOC_STREAMON
  -> vidioc_streamon
    -> vb2_streamon(&dev->vb_vidq, i);
      -> __enqueue_in_driver(vb);         /* vb2_buffer入队 */
        -> q->ops->buf_queue(vb);
          -> buffer_queue
            -> list_add_tail(&buf->list, &vidq->active);
      -> call_qop(q, start_streaming,...) /* 调用vb2_queue->ops->start_streaming */
        -> start_streaming
          -> vivi_start_generating(dev);
            -> kthread_run(vivi_thread,...)
              -> vivi_sleep(dev);
              -> add_wait_queue(&dma_q->wq, &wait);
              -> vivi_thread_tick(dev);
                -> buf = list_entry(dma_q->active.next, struct vivi_buffer,...)
                -> vivi_fillbuff(dev, buf);          /* 填充vivi_buffer数据,其中包含vb2_buffer */
                -> vb2_buffer_done(&buf->vb, VB2_BUF_STATE_DONE);
            -> wake_up_interruptible(&dma_q->wq);    /* 唤醒线程 */

6. 查询是否有数据

select(h->fd + 1, &rdset, NULL, NULL, &tv)
  -> vivi_poll
    -> vb2_poll(q, file, wait);
      -> poll_wait(file, &q->done_wq, wait);
          /* 唤醒使用的就是 5 中的wake_up_interruptible(&dma_q->wq) */

7. 从缓冲区中取数据

ioctl(4, VIDIOC_DQBUF
  -> vidioc_dqbuf
    -> vb2_dqbuf(&dev->vb_vidq, p,...)
      -> __vb2_get_done_vb(q, &vb, nonblocking);
        -> __vb2_wait_for_done_vb(q, nonblocking);
        /* 取出第一个数据 */
        -> *vb = list_first_entry(&q->done_list, struct vb2_buffer, done_entry);
      -> call_qop(q, buf_finish, vb); /* 调用vb2_queue->ops->start_streaming */
      -> __fill_v4l2_buffer(vb, b);
        -> memcpy(b, &vb->v4l2_buf, offsetof(struct v4l2_buffer, m));

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

1. 使用video_device_alloc()分配video_device

2. 设置结构体成员

.fops

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

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

3. 使用video_register_device()注册video_device

我们可以仿照vivi.c和百度网盘分享的linux/drivers/staging/media/solo6x10/v4l2.c(主要仿照此文件)写一个虚拟摄像头驱动程序

源代码:

  1 #include <linux/module.h>
  2 #include <linux/delay.h>
  3 #include <linux/errno.h>
  4 #include <linux/fs.h>
  5 #include <linux/kernel.h>
  6 #include <linux/slab.h>
  7 #include <linux/mm.h>
  8 #include <linux/ioport.h>
  9 #include <linux/init.h>
 10 #include <linux/sched.h>
 11 #include <linux/pci.h>
 12 #include <linux/random.h>
 13 #include <linux/version.h>
 14 #include <linux/mutex.h>
 15 #include <linux/videodev2.h>
 16 #include <linux/dma-mapping.h>
 17 #include <linux/interrupt.h>
 18 #include <linux/kthread.h>
 19 #include <linux/highmem.h>
 20 #include <linux/freezer.h>
 21 #include <media/videobuf-vmalloc.h>
 22 #include <media/v4l2-device.h>
 23 #include <media/v4l2-ioctl.h>
 24
 25 #define USE_DMA        0
 26
 27 static struct video_device *myvivi;
 28 static struct v4l2_format myvivi_format;
 29 static struct videobuf_queue myvivi_vb_vidq;
 30 static spinlock_t myvivi_slock;
 31 static struct timer_list myvivi_timer;
 32 static struct list_head myvivi_list_head;
 33
 34 /**
 35  *   videobuf_queue_ops
 36  **/
 37 /* ioctl VIDIOC_REQBUFS导致此函数被调用
 38  * 用于重新调整count和size
 39  */
 40 static int myvivi_buf_setup(struct videobuf_queue *vq, unsigned int *count, unsigned int *size)
 41 {
 42     *size = myvivi_format.fmt.pix.sizeimage;
 43
 44     if (*count < 4)
 45         *count = 4;
 46
 47     return 0;
 48 }
 49
 50 /* ioctl VIDIOC_QBUF导致此函数被调用
 51  * 它会填充video_buffer结构体并调用videobuf_iolock来分配内存
 52  */
 53 static int myvivi_buf_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb, enum v4l2_field field)
 54 {
 55     vb->size    = myvivi_format.fmt.pix.sizeimage;
 56
 57     /* XXX: These properties only change when queue is idle */
 58     vb->width    = myvivi_format.fmt.pix.width;
 59     vb->height    = myvivi_format.fmt.pix.height;
 60     vb->bytesperline = myvivi_format.fmt.pix.bytesperline;
 61     vb->field  = field;
 62 #if USE_DMA
 63     if (vb->state == VIDEOBUF_NEEDS_INIT) {
 64         int rc = videobuf_iolock(vq, vb, NULL);
 65         if (rc < 0) {
 66             struct videobuf_dmabuf *dma = videobuf_to_dma(vb);
 67             videobuf_dma_unmap(vq->dev, dma);
 68             videobuf_dma_free(dma);
 69             vb->state = VIDEOBUF_NEEDS_INIT;
 70             return rc;
 71         }
 72     }
 73 #endif
 74     vb->state = VIDEOBUF_PREPARED;
 75
 76     return 0;
 77 }
 78
 79 /* ioctl VIDIOC_QBUF时:
 80  * 1. 调用buf_prepare()做准备工作
 81  * 2. 把buf放入stream队列
 82  * 3. 调用buf_queue(起通知、记录作用)
 83  */
 84 static void myvivi_buf_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb)
 85 {
 86     vb->state = VIDEOBUF_QUEUED;
 87     list_add_tail(&vb->queue, &myvivi_list_head);
 88     /* 使用的定时器,不需要唤醒 */
 89 //    wake_up_interruptible(&solo_dev->disp_thread_wait);
 90 }
 91
 92 /* APP不再使用队列时,用它来释放内存 */
 93 static void myvivi_buf_release(struct videobuf_queue *vq, struct videobuf_buffer *vb)
 94 {
 95 #if USE_DMA
 96     struct videobuf_dmabuf *dma = videobuf_to_dma(vb);
 97
 98     videobuf_dma_unmap(vq->dev, dma);
 99     videobuf_dma_free(dma);
100 #endif
101     vb->state = VIDEOBUF_NEEDS_INIT;
102 }
103
104
105 static struct videobuf_queue_ops solo_video_qops = {
106     .buf_setup        = myvivi_buf_setup,
107     .buf_prepare    = myvivi_buf_prepare,
108     .buf_queue        = myvivi_buf_queue,
109     .buf_release    = myvivi_buf_release,
110 };
111
112 /**
113  *   v4l2_file_operations
114  **/
115 static int myvivi_open(struct file *file)
116 {
117     videobuf_queue_vmalloc_init(&myvivi_vb_vidq, &solo_video_qops,
118                    NULL, &myvivi_slock,
119                    V4L2_BUF_TYPE_VIDEO_CAPTURE,
120                    V4L2_FIELD_INTERLACED,
121                    sizeof(struct videobuf_buffer), NULL, NULL);
122     /* 其他程序在此处执行thread,本程序在此处执行定时器 */
123     myvivi_timer.expires = jiffies + 1;
124     add_timer(&myvivi_timer);
125
126     return 0;
127 }
128
129 static int myvivi_close(struct file *file)
130 {
131     del_timer(&myvivi_timer);
132     videobuf_stop(&myvivi_vb_vidq);
133     videobuf_mmap_free(&myvivi_vb_vidq);
134
135     return 0;
136 }
137
138 static ssize_t myvivi_read(struct file *file, char __user *data, size_t count, loff_t *ppos)
139 {
140     return videobuf_read_stream(&myvivi_vb_vidq, data, count, ppos, 0, file->f_flags & O_NONBLOCK);
141 }
142
143 static unsigned int myvivi_poll(struct file *file, struct poll_table_struct *wait)
144 {
145     return videobuf_poll_stream(file, &myvivi_vb_vidq, wait);
146 }
147
148 extern int videobuf_mmap_mapper(struct videobuf_queue *, struct vm_area_struct *);
149 static int myvivi_mmap(struct file *file, struct vm_area_struct *vma)
150 {
151     return videobuf_mmap_mapper(&myvivi_vb_vidq, vma);
152 }
153
154 static const struct v4l2_file_operations myvivi_fops = {
155     .owner            = THIS_MODULE,
156     .open           = myvivi_open,
157     .release        = myvivi_close,
158     .read           = myvivi_read,
159     .poll            = myvivi_poll,
160     .unlocked_ioctl = video_ioctl2,
161     .mmap           = myvivi_mmap,
162 };
163
164 /**
165  *   v4l2_ioctl_ops
166  **/
167 static int myvivi_querycap(struct file *file, void  *priv, struct v4l2_capability *cap)
168 {
169     strcpy(cap->driver, "myvivi");
170     strcpy(cap->card, "myvivi");
171     cap->version = 0x01;
172     cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
173     return 0;
174 }
175
176 static int myvivi_enum_fmt_vid_cap(struct file *file, void  *priv, struct v4l2_fmtdesc *f)
177 {
178     if (f->index)
179         return -EINVAL;
180
181     f->pixelformat = V4L2_PIX_FMT_YUYV;
182     strlcpy(f->description, "4:2:2, packed, YUYV", sizeof(f->description));
183
184     return 0;
185 }
186
187 static int myvivi_g_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f)
188 {
189     memcpy(f, &myvivi_format, sizeof(myvivi_format));
190     return 0;
191 }
192
193 static int myvivi_try_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f)
194 {
195     enum v4l2_field field;
196     unsigned int max_width, max_height;
197
198     if (f->fmt.pix.pixelformat != V4L2_PIX_FMT_YUYV)
199         return -EINVAL;
200
201     field = f->fmt.pix.field;
202     if (field == V4L2_FIELD_ANY) {
203         field = V4L2_FIELD_INTERLACED;
204     }
205     else if (V4L2_FIELD_INTERLACED != field) {
206         return -EINVAL;
207     }
208     f->fmt.pix.field = field;
209
210     max_width    = 1024;
211     max_height    = 768;
212
213     v4l_bound_align_image(&f->fmt.pix.width, 48, max_width, 2,
214                   &f->fmt.pix.height, 32, max_height, 0, 0);
215     f->fmt.pix.bytesperline =
216         (f->fmt.pix.width * 16) >> 3;
217     f->fmt.pix.sizeimage =
218         f->fmt.pix.height * f->fmt.pix.bytesperline;
219     f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
220
221     return 0;
222 }
223
224 static int myvivi_s_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f)
225 {
226     int ret;
227     if (videobuf_queue_is_busy(&myvivi_vb_vidq))
228         return -EBUSY;
229
230     ret = myvivi_try_fmt_vid_cap(file, priv, f);
231     if (ret < 0)
232         return ret;
233
234     memcpy(&myvivi_format, f, sizeof(myvivi_format));
235
236     return 0;
237 }
238
239 static int myvivi_reqbufs(struct file *file, void *priv, struct v4l2_requestbuffers *p)
240 {
241     return videobuf_reqbufs(&myvivi_vb_vidq, p);
242 }
243
244 static int myvivi_querybuf(struct file *file, void *priv, struct v4l2_buffer *p)
245 {
246     return videobuf_querybuf(&myvivi_vb_vidq, p);
247 }
248
249 static int myvivi_qbuf(struct file *file, void *priv, struct v4l2_buffer *p)
250 {
251     return videobuf_qbuf(&myvivi_vb_vidq, p);
252 }
253
254 static int myvivi_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p)
255 {
256     return videobuf_dqbuf(&myvivi_vb_vidq, p, file->f_flags & O_NONBLOCK);
257 }
258
259 static int myvivi_streamon(struct file *file, void *priv, enum v4l2_buf_type i)
260 {
261     return videobuf_streamon(&myvivi_vb_vidq);
262 }
263
264 static int myvivi_streamoff(struct file *file, void *priv, enum v4l2_buf_type i)
265 {
266     videobuf_streamoff(&myvivi_vb_vidq);
267     return 0;
268 }
269
270 static const struct v4l2_ioctl_ops myvivi_ioctl_ops = {
271     .vidioc_querycap      = myvivi_querycap,
272     .vidioc_enum_fmt_vid_cap  = myvivi_enum_fmt_vid_cap,
273     .vidioc_g_fmt_vid_cap     = myvivi_g_fmt_vid_cap,
274     .vidioc_try_fmt_vid_cap   = myvivi_try_fmt_vid_cap,
275     .vidioc_s_fmt_vid_cap     = myvivi_s_fmt_vid_cap,
276     .vidioc_reqbufs       = myvivi_reqbufs,
277     .vidioc_querybuf      = myvivi_querybuf,
278     .vidioc_qbuf          = myvivi_qbuf,
279     .vidioc_dqbuf         = myvivi_dqbuf,
280     .vidioc_streamon      = myvivi_streamon,
281     .vidioc_streamoff     = myvivi_streamoff,
282 };
283
284 static void myvivi_release(struct video_device *vdev)
285 {
286     /* myvivi_exit()中调用了video_device_release()
287      * 因此myvivi->release不需要设置为video_device_release
288      */
289 }
290
291 /* 此函数用于实现其他文件的thread() */
292 static void myvivi_timerfunc(unsigned long arg)
293 {
294     struct videobuf_buffer *vb;
295     void *vbuf;
296
297     if (list_empty(&myvivi_list_head))
298         goto fail;
299
300     vb = list_first_entry(&myvivi_list_head, struct videobuf_buffer,
301                   queue);
302
303     if (!waitqueue_active(&vb->done))
304         goto fail;
305
306     /* Fill buffer */
307     vbuf = videobuf_to_vmalloc(vb);
308     if (!vbuf)
309         goto fail;
310
311     /* 由于是虚拟摄像头驱动,所以假装有数据 */
312     memset(vbuf, 0, vb->size);
313
314     vb->field_count++;
315     do_gettimeofday(&vb->ts);
316     vb->state = VIDEOBUF_DONE;
317
318     /* 把videobuf从队列中删除 */
319     list_del(&vb->queue);
320
321     /* 唤醒videobuf->done上的进程 */
322     wake_up(&vb->done);
323
324 fail:
325     /* 30帧/秒 */
326     mod_timer(&myvivi_timer, jiffies + HZ / 30);
327 }
328
329 static int __init myvivi_init(void)
330 {
331     int ret;
332
333     myvivi = video_device_alloc();
334
335     myvivi->release        = myvivi_release,
336     myvivi->fops        = &myvivi_fops;
337     myvivi->ioctl_ops     = &myvivi_ioctl_ops;
338
339     /* 初始化spinlock */
340     spin_lock_init(&myvivi_slock);
341     /* 初始化timer_list */
342     init_timer(&myvivi_timer);
343     myvivi_timer.function = myvivi_timerfunc;
344     /* 初始化list_head */
345     INIT_LIST_HEAD(&myvivi_list_head);
346
347     ret = video_register_device(myvivi, VFL_TYPE_GRABBER, -1);
348     if (ret < 0) {
349         video_device_release(myvivi);
350         return ret;
351     }
352
353     return 0;
354 }
355
356 static void __exit myvivi_exit(void)
357 {
358     video_unregister_device(myvivi);
359     video_device_release(myvivi);
360 }
361
362 module_init(myvivi_init);
363 module_exit(myvivi_exit);
364 MODULE_LICENSE("GPL");

Makefile:

 1 KERN_DIR = /work/itop4412/tools/linux-3.5
 2
 3 all:
 4     make -C $(KERN_DIR) M=`pwd` modules
 5
 6 clean:
 7     make -C $(KERN_DIR) M=`pwd` modules clean
 8     rm -rf modules.order
 9
10 obj-m    += videobuf-vmalloc.o
11 obj-m    += myvivi.o

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

原文地址:https://www.cnblogs.com/Lioker/p/11323630.html

时间: 2024-10-29 19:09:48

二十四、V4L2框架分析和虚拟摄像头驱动编写的相关文章

全栈JavaScript之路( 二十四 )DOM2、DOM3, 不涉及XML命名空间的扩展

(一)DocumentType 类型的变化新增三个属性: publicId,systemId,internalSubset(内部子集) <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd" [<!ELEMENT name (#PCDATA)>] > 通过, document.doc

第三百二十四节,web爬虫,scrapy模块介绍与使用

第三百二十四节,web爬虫,scrapy模块介绍与使用 Scrapy是一个为了爬取网站数据,提取结构性数据而编写的应用框架. 其可以应用在数据挖掘,信息处理或存储历史数据等一系列的程序中.其最初是为了页面抓取 (更确切来说, 网络抓取 )所设计的, 也可以应用在获取API所返回的数据(例如 Amazon Associates Web Services ) 或者通用的网络爬虫.Scrapy用途广泛,可以用于数据挖掘.监测和自动化测试. Scrapy 使用了 Twisted异步网络库来处理网络通讯.

云计算设计模式(二十四)——仆人键模式

云计算设计模式(二十四)——仆人键模式 使用一个令牌或密钥,向客户提供受限制的直接访问特定的资源或服务,以便由应用程序代码卸载数据传输操作.这个模式是在使用云托管的存储系统或队列的应用中特别有用,并且可以最大限度地降低成本,最大限度地提高可扩展性和性能. 背景和问题 客户端程序和网络浏览器经常需要读取和写入文件或数据流,并从一个应用程序的存储空间.通常,应用程序将处理的运动数据,或者通过从存储读取它,并将其传输到客户端,或通过从客户机读取该载流并将其存储在数据存储中.然而,这种方法吸收了宝贵的资

Android学习路线(二十四)ActionBar Fragment运用最佳实践

通过前面的几篇博客,大家看到了Google是如何解释action bar和fragment以及推荐的用法.俗话说没有demo的博客不是好博客,下面我会介绍一下action bar和fragment在实战中的应用,以及相关demo源码,希望和大家相互交流. 了解过fragment的同学们应该都知道,fragment是android 3.0版本才出现的的,因此如果要在支持android 3.0一下版本的工程中使用fragment的话是需要添加Support Library的.具体如何添加我就不再赘述

QT开发(二十四)——QT文件操作

QT开发(二十四)--QT文件操作 一.QT文件操作简介 QT中的IO操作通过统一的接口简化了文件与外部设备的操作方式,QT中文件被当作一种特殊的外部设备,文件操作与外部设备操作相同. 1.IO操作的主要函数接口 打开设备:bool open(OpenMode mode) 读取数据:QByteArray read(qint64 maxSize) 写入数据:qint64 write(const QByteArray & byteArray) 关闭设备:void close() IO操作的本质是连续

JAVA之旅(二十四)——I/O流,字符流,FileWriter,IOException,文件续写,FileReader,小练习

JAVA之旅(二十四)--I/O流,字符流,FileWriter,IOException,文件续写,FileReader,小练习 JAVA之旅林林总总也是写了二十多篇了,我们今天终于是接触到了I/O了.如果你初学,不懂IO流,你可以从前往后慢慢看,但是你工作了一段时间你会发现,流的使用场景以及技术点是非常的强硬的,我们势必要掌握这个知识点,如果你觉得翻阅API比较鼓噪,看视频得不到精髓,看书看不到要点,你就跟随我的JAVA之旅,一起去探索吧! 一.I/O概述 I/O全名:Input Output

从零开始学android&lt;android事件的处理方式.二十四.&gt;

在android中一共有 多种事件,每种事件都有自己相对应的处理机制 如以下几种 1 单击事件 View.OnClickListener public abstract void onClick (View v) 单击组件时触发 2 单击事件 View.OnLongClickListener public abstract boolean onLongClick (View v) 长按组件时触发 3 键盘事件 View.OnKeyListener public abstract boolean

实验二十四:SD卡模块

  驱动SD卡是件容易让人抓狂的事情,驱动SD卡好比SDRAM执行页读写,SD卡虽然不及SDRAM的麻烦要求(时序参数),但是驱动过程却有猥琐操作.除此此外,描述语言只要稍微比较一下C语言,描述语言一定会泪流满面,因为嵌套循环,嵌套判断,或者嵌套函数等都是它的痛.. 史莱姆模块是多模块建模的通病,意指结构能力非常脆弱的模块,暴力的嵌套行为往往会击垮模块的美丽身躯,好让脆弱结构更加脆弱还有惨不忍睹,最终搞垮模块的表达能力.描述语言预想驾驭SD卡,关键的地方就是如何提升模块的结构能力.简单而言,描述

攻城狮在路上(叁)Linux(二十四)--- linux设置开机挂载及镜像文件挂载

虽然可以手动进行文件系统的挂载,但是每次都手动挂载就会很麻烦,开机挂载的目的就是实现文件系统的自动挂载. 一.开机挂载:/etc/fstab及/etc/mtab 主要是通过修改/etc/fstab文件的配置来实现. fstab是开机时的设置,实际文件系统的挂载是记录到/etc/mtab和/proc/mounts这两个文件中. 1.系统挂载的限制: A.根目录/必须挂载,而且一定是最先挂载的,要先于其他mount point. B.其他挂载点必须为已新建的目录,可以任意指定. C.所有挂载点在同一