v4l2虚拟驱动的应用测试程序讲解

简介

  在前面我们已经完成了myvivi这个虚拟的v4l2摄像头驱动程序的编写。这里继续编写一个该驱动的应用测试程序来加深一下该驱动的工作原理。

具体代码

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <getopt.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/mman.h>
#include <sys/ioctl.h>
#include <asm/types.h>
#include <linux/videodev2.h>
#include <linux/fb.h>
#define CLEAR(x) memset (&(x), 0, sizeof (x))  

#define SET_WIDTH 320
#define SET_HEIGHT 240

struct buffer {
	void * start;
	size_t length;
};  

static char * dev_name = NULL;
static int fd = -1;
struct buffer * buffers = NULL;
static unsigned int n_buffers = 0;
static int time_in_sec_capture=5;
static int fbfd = -1;
static struct fb_var_screeninfo vinfo;
static struct fb_fix_screeninfo finfo;
static char *fbp=NULL;
static long screensize=0;  

static void errno_exit (const char * s)
{
	fprintf (stderr, "%s error %d, %s/n",s, errno, strerror (errno));
	exit (EXIT_FAILURE);
}  

static int xioctl (int fd,int request,void * arg)
{
	int r;
	do r = ioctl (fd, request, arg);
	while (-1 == r && EINTR == errno);
	return r;
}  

inline int clip(int value, int min, int max) {
	return (value > max ? max : value < min ? min : value);
}  

static void process_image (const void * p){
	unsigned char* in=(char*)p;
	int width=SET_WIDTH;
	int height=SET_HEIGHT;
	int istride=SET_WIDTH * 2;
	int x,y,j;
	int y0,u,y1,v,r,g,b;
	long location=0;  

	for ( y = 100; y < height + 100; ++y) {
		for (j = 0, x=100; j < width * 2 ; j += 4,x +=2) {
			location = (x+vinfo.xoffset) * (vinfo.bits_per_pixel/8) +
				(y+vinfo.yoffset) * finfo.line_length;  

			y0 = in[j];
			u = in[j + 1] - 128;
			y1 = in[j + 2];
			v = in[j + 3] - 128;          

			r = (298 * y0 + 409 * v + 128) >> 8;
			g = (298 * y0 - 100 * u - 208 * v + 128) >> 8;
			b = (298 * y0 + 516 * u + 128) >> 8;  

			fbp[ location + 0] = clip(b, 0, 255);
			fbp[ location + 1] = clip(g, 0, 255);
			fbp[ location + 2] = clip(r, 0, 255);
			fbp[ location + 3] = 255;      

			r = (298 * y1 + 409 * v + 128) >> 8;
			g = (298 * y1 - 100 * u - 208 * v + 128) >> 8;
			b = (298 * y1 + 516 * u + 128) >> 8;  

			fbp[ location + 4] = clip(b, 0, 255);
			fbp[ location + 5] = clip(g, 0, 255);
			fbp[ location + 6] = clip(r, 0, 255);
			fbp[ location + 7] = 255;
		}
		in +=istride;
	}
}  

static int read_frame (void)
{
	struct v4l2_buffer buf;
	unsigned int i;  

	CLEAR (buf);
	buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
	buf.memory = V4L2_MEMORY_MMAP;  

	if (-1 == xioctl (fd, VIDIOC_DQBUF, &buf)) {
		switch (errno) {
			case EAGAIN:
				return 0;
			case EIO:      

			default:
				errno_exit ("VIDIOC_DQBUF");
		}
	}  

	assert (buf.index < n_buffers);
	process_image(buffers[buf.index].start);
	if (-1 == xioctl (fd, VIDIOC_QBUF, &buf))
		errno_exit ("VIDIOC_QBUF");  

	return 1;
}  

static void run (void)
{
	unsigned int count;
	int frames;
	frames = 30 * time_in_sec_capture;  

	while (frames-- > 0) {
		for (;;) {
			fd_set fds;
			struct timeval tv;
			int r;
			FD_ZERO (&fds);
			FD_SET (fd, &fds);  

			tv.tv_sec = 2;
			tv.tv_usec = 0;  

			r = select (fd + 1, &fds, NULL, NULL, &tv);  

			if (-1 == r) {
				if (EINTR == errno)
					continue;
				errno_exit ("select");
			}else if (0 == r) {
				fprintf (stderr, "select timeout/n");
				exit (EXIT_FAILURE);
			}
			if(read_frame())
				break;
		}
	}
}  

static void stop_capturing (void)
{
	enum v4l2_buf_type type;  

	type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
	if (-1 == xioctl (fd, VIDIOC_STREAMOFF, &type))
		errno_exit ("VIDIOC_STREAMOFF");
}  

static void start_capturing (void)
{
	unsigned int i;
	enum v4l2_buf_type type;  

	for (i = 0; i < n_buffers; ++i) {
		struct v4l2_buffer buf;
		CLEAR (buf);  

		buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
		buf.memory = V4L2_MEMORY_MMAP;
		buf.index = i;  

		if (-1 == xioctl (fd, VIDIOC_QBUF, &buf))
			errno_exit ("VIDIOC_QBUF");
	}  

	type = V4L2_BUF_TYPE_VIDEO_CAPTURE;  

	if (-1 == xioctl (fd, VIDIOC_STREAMON, &type))
		errno_exit ("VIDIOC_STREAMON");
}  

static void uninit_device (void)
{
	unsigned int i;  

	for (i = 0; i < n_buffers; ++i)
		if (-1 == munmap (buffers[i].start, buffers[i].length))
			errno_exit ("munmap");  

	if (-1 == munmap(fbp, screensize)) {
		printf(" Error: framebuffer device munmap() failed./n");
		exit (EXIT_FAILURE) ;
	}
	free (buffers);
}  

static void init_mmap (void)
{
	struct v4l2_requestbuffers req;  

	//mmap framebuffer
	fbp = (char *)mmap(NULL,screensize,PROT_READ | PROT_WRITE,MAP_SHARED ,fbfd, 0);
	if ((int)fbp == -1) {
		printf("Error: failed to map framebuffer device to memory./n");
		exit (EXIT_FAILURE) ;
	}
	memset(fbp, 0, screensize);
	CLEAR (req);  

	req.count = 4;
	req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
	req.memory = V4L2_MEMORY_MMAP;  

	if (-1 == xioctl (fd, VIDIOC_REQBUFS, &req)) {
		if (EINVAL == errno) {
			fprintf (stderr, "%s does not support memory mapping/n", dev_name);
			exit (EXIT_FAILURE);
		} else {
			errno_exit ("VIDIOC_REQBUFS");
		}
	}  

	if (req.count < 4) {    //if (req.count < 2)
		fprintf (stderr, "Insufficient buffer memory on %s/n",dev_name);
		exit (EXIT_FAILURE);
	}  

	buffers = calloc (req.count, sizeof (*buffers));  

	if (!buffers) {
		fprintf (stderr, "Out of memory/n");
		exit (EXIT_FAILURE);
	}  

	for (n_buffers = 0; n_buffers < req.count; ++n_buffers) {
		struct v4l2_buffer buf;  

		CLEAR (buf);  

		buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
		buf.memory = V4L2_MEMORY_MMAP;
		buf.index = n_buffers;  

		if (-1 == xioctl (fd, VIDIOC_QUERYBUF, &buf))
			errno_exit ("VIDIOC_QUERYBUF");  

		buffers[n_buffers].length = buf.length;
		buffers[n_buffers].start =mmap (NULL,buf.length,PROT_READ | PROT_WRITE ,MAP_SHARED,fd, buf.m.offset);  

		if (MAP_FAILED == buffers[n_buffers].start)
			errno_exit ("mmap");
	}  

}  

static void init_device (void)
{
	struct v4l2_capability cap;
	struct v4l2_cropcap cropcap;
	struct v4l2_crop crop;
	struct v4l2_format fmt;
	unsigned int min;  

	// Get fixed screen information
	if (-1==xioctl(fbfd, FBIOGET_FSCREENINFO, &finfo)) {
		printf("Error reading fixed information.\n");
		exit (EXIT_FAILURE);
	}  

	// Get variable screen information
	if (-1==xioctl(fbfd, FBIOGET_VSCREENINFO, &vinfo)) {
		printf("Error reading variable information.\n");
		exit (EXIT_FAILURE);
	}
	screensize = vinfo.xres * vinfo.yres * vinfo.bits_per_pixel / 8;  

	if (-1 == xioctl (fd, VIDIOC_QUERYCAP, &cap)) {
		if (EINVAL == errno) {
			fprintf (stderr, "%s is no V4L2 device\n",dev_name);
			exit (EXIT_FAILURE);
		} else {
			errno_exit ("VIDIOC_QUERYCAP");
		}
	}  

	if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)) {
		fprintf (stderr, "%s is no video capture device\n",dev_name);
		exit (EXIT_FAILURE);
	}  

	if (!(cap.capabilities & V4L2_CAP_STREAMING)) {
		fprintf (stderr, "%s does not support streaming i/o\n",dev_name);
		exit (EXIT_FAILURE);
	}  

	CLEAR (fmt);  

	fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
	fmt.fmt.pix.width = SET_WIDTH;
	fmt.fmt.pix.height = SET_HEIGHT;
	fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
	fmt.fmt.pix.field = V4L2_FIELD_INTERLACED;  

	if (-1 == xioctl (fd, VIDIOC_S_FMT, &fmt))
		errno_exit ("VIDIOC_S_FMT");  

	init_mmap();
}  

static void close_device (void)
{
	if (-1 == close(fd))
		errno_exit ("close");
	fd = -1;
	close(fbfd);
}  

static void open_device (void)
{
	//open framebuffer
	fbfd = open("/dev/fb0", O_RDWR);
	if (fbfd==-1) {
		printf("Error: cannot open framebuffer device./n");
		exit (EXIT_FAILURE);
	}  

	//open camera
	fd = open (dev_name, O_RDWR| O_NONBLOCK, 0);
	if (-1 == fd) {
		fprintf (stderr, "Cannot open '%s': %d, %s/n",dev_name, errno, strerror (errno));
		exit (EXIT_FAILURE);
	}
}  

int main (int argc,char ** argv){
	dev_name = "/dev/video0";  

	open_device();
	init_device();
	start_capturing();
	run();
	stop_capturing();
	uninit_device();
	close_device();
	exit (EXIT_SUCCESS);  

	return 0;
}  

效果演示

  用gcc编译好这个应用程序之后,使用ctrl+Alt+F1切换控制台,然后在运行这个应用程序,才能看到效果。。当然,运行这个程序之前,需要保证myvivi.ko
这个驱动正常加载成功了。最后演示效果如下:
                                    

代码分析

open_device

static void open_device (void)
{
    //open framebuffer
    fbfd = open("/dev/fb0", O_RDWR);
    if (fbfd==-1) {
        printf("Error: cannot open framebuffer device./n");
        exit (EXIT_FAILURE);
    }

    //open camera
    fd = open (dev_name, O_RDWR| O_NONBLOCK, 0);
    if (-1 == fd) {
        fprintf (stderr, "Cannot open '%s': %d, %s/n",dev_name, errno, strerror (errno));
        exit (EXIT_FAILURE);
    }
}

  这里打开了一个用来在电脑上显示的framebuffer,和我们对应的myvivi.ko生成的/dev/video0设备节点。
这里的open /dev/video0操作,就是对应调用到myvivi驱动程序中的myvivi_fops-->myvivi_open。

init_devices

static void init_device (void)
{
	// Get fixed screen information
	if (-1==xioctl(fbfd, FBIOGET_FSCREENINFO, &finfo)) {
	}  

	// Get variable screen information
	if (-1==xioctl(fbfd, FBIOGET_VSCREENINFO, &vinfo)) {
	}
	screensize = vinfo.xres * vinfo.yres * vinfo.bits_per_pixel / 8;  

	if (-1 == xioctl (fd, VIDIOC_QUERYCAP, &cap)) {
	}  

	if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)) {
	}  

	if (!(cap.capabilities & V4L2_CAP_STREAMING)) {
	}  

	CLEAR (fmt);  

	fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
	fmt.fmt.pix.width = SET_WIDTH;
	fmt.fmt.pix.height = SET_HEIGHT;
	fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
	fmt.fmt.pix.field = V4L2_FIELD_INTERLACED;  

	if (-1 == xioctl (fd, VIDIOC_S_FMT, &fmt))
		errno_exit ("VIDIOC_S_FMT");  

	init_mmap();
}  

  在init_device中,最开始是获得fb0相关信息,这个我们暂时不关系,然后ioctl-->VIDIOC_QUERYCAP来判断/dev/video0是否是一个video设备,对应到myvivi
驱动中的函数:myvivi_ioctl_ops-->myvivi_ioctl_ops。在这个函数中我们还设置了它的属性为V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;这两个属性,也在
这里检查了一遍。接着该函数使用ioctl-->VIDIOC_S_FMT来设置video格式为V4L2_PIX_FMT_YUYV。对应到myvivi驱动中函数为:
myvivi_ioctl_ops-->myvivi_vidioc_s_fmt_vid_cap,我们还记得这个函数会首先利用函数myvivi_vidioc_try_fmt_vid_cap来检查一遍应用程序这里传入的video格式
是否被myvivi驱动支持,如果支持就保存到驱动的一个结构体:myvivi_format中去。显然fmt.fmt.pix.width=320,fmt.fmt.pix.height=240,以及格式为
V4L2_PIX_FMT_YUYV,正好是被myvivi这个设备驱动程序支持的。

init_mmap

  同时在init_device函数的最后还调用了init_mmap来做buffer队列的初始化。
static void init_mmap (void)
{
	struct v4l2_requestbuffers req;  

	//mmap framebuffer
	fbp = (char *)mmap(NULL,screensize,PROT_READ | PROT_WRITE,MAP_SHARED ,fbfd, 0);
	if ((int)fbp == -1) {
		printf("Error: failed to map framebuffer device to memory./n");
		exit (EXIT_FAILURE) ;
	}
	memset(fbp, 0, screensize);
	CLEAR (req);  

	req.count = 4;
	req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
	req.memory = V4L2_MEMORY_MMAP;  

	if (-1 == xioctl (fd, VIDIOC_REQBUFS, &req)) {
		if (EINVAL == errno) {
			fprintf (stderr, "%s does not support memory mapping/n", dev_name);
			exit (EXIT_FAILURE);
		} else {
			errno_exit ("VIDIOC_REQBUFS");
		}
	}  

	if (req.count < 4) {    //if (req.count < 2)
		fprintf (stderr, "Insufficient buffer memory on %s/n",dev_name);
		exit (EXIT_FAILURE);
	}  

	buffers = calloc (req.count, sizeof (*buffers));  

	if (!buffers) {
		fprintf (stderr, "Out of memory/n");
		exit (EXIT_FAILURE);
	}  

	for (n_buffers = 0; n_buffers < req.count; ++n_buffers) {
		struct v4l2_buffer buf;  

		CLEAR (buf);  

		buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
		buf.memory = V4L2_MEMORY_MMAP;
		buf.index = n_buffers;  

		if (-1 == xioctl (fd, VIDIOC_QUERYBUF, &buf))
			errno_exit ("VIDIOC_QUERYBUF");  

		buffers[n_buffers].length = buf.length;
		buffers[n_buffers].start =mmap (NULL,buf.length,PROT_READ | PROT_WRITE ,MAP_SHARED,fd, buf.m.offset);  

		if (MAP_FAILED == buffers[n_buffers].start)
			errno_exit ("mmap");
	}  

}
  最开始也是fb0相关的内存操作,我们这里不关心。然后接着是ioctl--->VIDIOC_REQBUFS,对应到驱动中被调用到的函数为:
myvivi_ioctl_ops-->myvivi_vidioc_reqbufs,然后之前我们说过在函数myvivi_vidioc_reqbufs中最终会调用到myvivi_video_qops-->myvivi_buffer_setup。
在函数myvivi_buffer_setup中,因为传入的块数不为0,所以继续保持为4块,然后将myvivi_format.fmt.pix.sizeimage这个图像大小保存到myvivi_vb_vidqueue
队列中。
  接着init_mmap中,循环req.count次,也就是4次,来分配和映射数据块内存。
  具体方式为,首先利用ioctl-->VIDIOC_QUERYBUF,对应调用到驱动中函数:myvivi_ioctl_ops-->myvivi_vidioc_querybuf-->videobuf_querybuf.
然后会返回buffer数据块长度和对应的offset位置。接着通过mmap函数来分配这块数据内存并映射到buffers[n_buffers].start,如此循环req.count次之后,
也就设置好了对应的数据块。mmap函数就是对应到驱动中的:myvivi_fops-->myvivi_mmap-->videobuf_mmap_mapper-->__videobuf_mmap_mapper。然后在
__videobuf_mmap_mapper函数中根据传入的参数,进行内存分配和映射。

start_capturing

static void start_capturing (void)
{
    unsigned int i;
    enum v4l2_buf_type type;  

    for (i = 0; i < n_buffers; ++i) {
        struct v4l2_buffer buf;
        CLEAR (buf);  

        buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
        buf.memory = V4L2_MEMORY_MMAP;
        buf.index = i;  

        if (-1 == xioctl (fd, VIDIOC_QBUF, &buf))
            errno_exit ("VIDIOC_QBUF");
    }  

    type = V4L2_BUF_TYPE_VIDEO_CAPTURE;  

    if (-1 == xioctl (fd, VIDIOC_STREAMON, &type))
        errno_exit ("VIDIOC_STREAMON");
} 

  循环了块的次数,这里也就是4次,然后在里面进行四次ioctl-->VIDIOC_QBUF。对应的也就是驱动函数:myvivi_ioctl_ops--->myvivi_vidioc_qbuf。
这个函数最终调用的是v4l2内部实现的函数videobuf_qbuf。这个函数中,依次调用了函数:myvivi_video_qops-->myvivi_buffer_prepare 和
myvivi_video_qops-->myvivi_buffer_queue,在myvivi_buffer_prepare 函数中往videobuf_queue队列中填入了imagesize、field之列的关键信息。接着
用myvivi_buffer_queue函数将当前的myvivi_vb_vidqueue->queue放入到了驱动的本地队列myvivi_vb_local_queue。这样循环4次之后,就将myvivi_vb_vidqueue
中所有的queue都放入了本地队列myvivi_vb_local_queue中。
  接着应用程序ioctl-->VIDIOC_STREAMON,对应到驱动函数myvivi_vidioc_streamon中,表示开始启动使用。

run

static void run (void)
{
    unsigned int count;
    int frames;
    frames = 30 * 5;  

    while (frames-- > 0) {
        for (;;) {
            fd_set fds;
            struct timeval tv;
            int r;
            FD_ZERO (&fds);
            FD_SET (fd, &fds);  

            tv.tv_sec = 2;
            tv.tv_usec = 0;  

            r = select (fd + 1, &fds, NULL, NULL, &tv);
            ...........
            if(read_frame())
                break;
        }
    }
}

  这里相当于是循环了5秒,一秒30fps。然后循环中用select来检控。前面提到过select对应到驱动的poll中,如果queue中没有数据,就会睡眠等待。
直到定时器函数填充好数据,然后唤醒在poll中等待的进程。然后用read_frame来读取数据。

read_frame

static int read_frame (void)
{
    struct v4l2_buffer buf;
    unsigned int i;  

    CLEAR (buf);
    buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    buf.memory = V4L2_MEMORY_MMAP;  

    if (-1 == xioctl (fd, VIDIOC_DQBUF, &buf)) {
          .................
    }
    assert (buf.index < n_buffers);
    process_image(buffers[buf.index].start);
    if (-1 == xioctl (fd, VIDIOC_QBUF, &buf))
        errno_exit ("VIDIOC_QBUF");  

    return 1;
} 

  首先ioctl-->VIDIOC_DQBUF,对应到驱动中:myvivi_vidioc_dqbuf,取出已经比定时器函数填充好数据了的myvivi_vb_vidqueue->queue到buf中,然后将该数据
交给process_image进行转换和在fb0中进行显示出来。处理完数据之后接着又一次用ioctl-->VIDIOC_QBUF,对应到驱动中将当前myvivi_vb_vidqueue->queue重新
放回到本地队列myvivi_vb_local_queue,等待被下一次使用。这样不断循环就实现了数据的读取、显示和处理完数据之后的buffer从新放回队列。

stop_capturing

static void stop_capturing (void)                                                                                                    {
    enum v4l2_buf_type type;

    type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    if (-1 == xioctl (fd, VIDIOC_STREAMOFF, &type))
        errno_exit ("VIDIOC_STREAMOFF");
}

  对应到也就是驱动中:myvivi_ioctl_ops-->myvivi_vidioc_streamoff,表示停止使用。

uninit_device/close_device

  这两个函数对应的操作:接触所有的buffer映射和释放到buffer,然后调用myvivi_fops-->myvivi_close,来删除驱动中的定时器以及一些对应的清除工作。
这样一个v4l2的应用测试程序,以及它如何和驱动交互的分析就结束了。
时间: 2024-08-10 21:16:37

v4l2虚拟驱动的应用测试程序讲解的相关文章

重头写一个v4l2的虚拟驱动_1

简介 因为在qcom平台上和linux原生都是用的v4l2框架作为camera的驱动框架,所以本着学习记录的笔记,做了如下文档记录. 该文档是学习<卫东山老师视频教程第三期>的个人学习笔记,非常感谢老师的资料.该记录仅供学习交流,如有侵犯到大家利益,还望海涵,请联系博主删除. 注册video_device 代码演示 首先是驱动程序的入口.出口以及license,然后第一步就要分配,设置和注册一个video_device结构(基于内核版本:linux-3.13.1). #include <

重头写一个v4l2的虚拟驱动_2

简介 因为在qcom平台上和linux原生都是用的v4l2框架作为camera的驱动框架,所以本着学习记录的笔记,做了如下文档记录. 该文档是学习<卫东山老师视频教程第三期>的个人学习笔记,非常感谢老师的资料.该记录仅供学习交流,如有侵犯到大家利益,还望海涵,请联系博主删除. buffer队列操作 首先是填充了队列相关的4个函数: static int myvivi_vidioc_reqbufs(struct file *file, void *priv, struct v4l2_reques

重头写一个v4l2的虚拟驱动_3

简介 因为在qcom平台上和linux原生都是用的v4l2框架作为camera的驱动框架,所以本着学习记录的笔记,做了如下文档记录. 该文档是学习<卫东山老师视频教程第三期>的个人学习笔记,非常感谢老师的资料.该记录仅供学习交流,如有侵犯到大家利益,还望海涵,请联系博主删除. poll/select 在前一篇中我们说到,应用程序和驱动通过select/poll机制来进行交互,从而做到不停地qbuffer和dequebuffer. 所以驱动中还需要实现它的poll函数: static unsig

FTP的搭建与虚拟目录作用&lt;之简单讲解&gt;

操作系统:win7 VS2010编写WebService与在IIS的发布<之简单讲解>中我已经说了IIS安装与使用,不明白的可以跳过去看. 1.添加FTP站点 2. 3. 4. 5. zqz上的小黑点代表未启动,记得要启动! 6.打开浏览器 7.添加虚拟目录 8. 9.qq添加成功 10. 11.再次添加一个虚拟目录:aa 12. 13. 14.这里我为什么要添加两个虚拟目录呢?一个是qq一个是aa.这就引出了虚拟目录的重要作用. 虚拟目录就是将其他目录以映射的方式虚拟到该FTP服务器的主目录

VC Mirror Driver显示虚拟驱动经典开发

一个简单的显示驱动实例 windows wdk 7600的 mirror(镜像) 显示驱动部分 基本流程: Windows 2000 DDK包含了一个例子镜像驱动程序,在 上面3个目录中包括了组件源文件. 目录 包含的源文件 Video\displays\mirror\dll 镜像驱动程序 Video\miniport\mirror 微端口驱动程序 Video\displays\mirror\app 用户模式服务.也包含mirror.inf. 打开disp文件夹 C:\WinDDK\7600.1

ACL虚拟币金融理财综合讲解+ACL理财是传销吗+ACL虚拟币理财多久回本

ACL 国际金融理财平台 ACL(AMERICAN CAPITAL LEAGUE)国际金融理财平台由美国瑞杰(Raymond James Financial Inc., 纽交所股票代码RJF)公司和Brent Kessel 博士共同发起创立,依托北美精算师协会(SOA), 是互联网金融与实体经济相结合的稳健投资平台. ACL 拥有与众不同的合作团队,ACL 由经验丰富的投资方和运营团队联合发起,双方密切合 作又相互独立,这种独立运营的机制充分保障平台的公正公平,为会员带来实实在在的价值 回报.

v4l2驱动编写篇【转】

转自:http://blog.csdn.net/michaelcao1980/article/details/53008418 大部分所需的信息都在这里.作为一个驱动作者,当挖掘头文件的时候,你可能也得看看include/media/v4l2-dev.h,它定义了许多你将来要打交道的结构体. 一个视频驱动很可能要有处理PCI总线,或USB总线的部分.这里我们不会花什么时间还接触这些东西.通常会有一个内部一I2C接口,我们在这一系列的后续文章中会接触到它.然后还有一个V4L2的子系统接口.这个子系

摄像头驱动的使能配置、V4L2编程接口的设计应用

摄像头采集子系统 一.摄像头驱动的使能配置 摄像头软件驱动构架 摄像头采集系统由上图所示,硬件(摄像头) -> 驱动(Linux内核配置中,选择支持V4L2的驱动选项) -> V4L2接口设计 -> 图像采集. 硬件:选择USB摄像头,内置芯片ZC30系列,Linux包含的万能驱动兼容: 驱动:配置Linux内核,选择万能摄像头驱动中ZC30系列驱动文件.支持V4L2接口,编译下载内核: 内核下载至开发板并挂载后,摄像头开发环境以搭建完成,以下即为应用采集. V4L2接口:编写基于V4L

基于Linux的v4l2视频架构驱动编写(转载)

转自:http://www.linuxidc.com/Linux/2011-03/33022.htm 其实,我刚开始一直都不知道怎么写驱动,什么都不懂的,只知道我需要在做项目的过程中学习,所以,我就自己找了一个关于编写Linux下的视频采集监控项 目做,然后上学期刚开学的时候听师兄说,跟院长做项目,没做出来也没关系,所以直接退出博士的团队,投靠了院长的门下,呵呵,说到这里其实并不是我太见风 使驼了,而是····老是让我做单片机的东东,我嫌没意思,他也知道我一开始就要学嵌入式,所以,最后,我想了一