搜档网
当前位置:搜档网 › linux视频驱动移植

linux视频驱动移植

linux视频驱动移植
linux视频驱动移植

V4L2视频驱动的移植与应用

V4L2(video for linux) 可以支持多种设备,它可以有以下5种接口:

1、视频采集接口(video capture interface):这种应用的设备可以是高频头或者摄像头.V4L2的最初设计就是应用于这种功能的.下面也是着重讲解这种应用;

2、视频输出接口(video output interface):可以驱动计算机的外围视频图像设备——像可以输出电视信号格式的设备;

3、直接传输视频接口(video overlay interface):它的主要工作是把从视频采集设备采集过来的信号直接输出到输出设备之上,而不用经过系统的CPU;

4、视频间隔消隐信号接口(VBI interface):它可以使应用可以访问传输消隐期的视频信号;

5、收音机接口(radio interface):可用来处理从AM或FM高频头设备接收来的音频流;

V4L2驱动的主要功能是使程序有发现设备的能力和操作设备.它主要是用过一系列的回调函数来实现这些功能.像设置高频头的频率,帧频,视频压缩格式和图像像参数等等.

一、V4L2的移植

V4L2提供了三种不同的API来传输外围设备和用户空间的数据。下面就vivi(drivers/media/video/vivi.c)来讲解一个V4L2驱动的编写。注意它是一个虚拟的设备驱动,没有与实际的硬件打交道。

1、分析几个重要数据结构:

vivi.c包含头文件v4l2-device.h和v4l2-ioctl.h,其中v4l2-device.h中包含了v4l2-subdev.h,v4l2-subdev.h中又包含了v4l2-common.h,v4l2-common.h中包含了v4l2-dev.h。

在v4l2-dev.h中定义了结构体video_device和v4l2_file_operations;

在v4l2-ioctl.h中定义了结构体v4l2_ioctl_ops;

在v4l2-device.h中定义了结构体v4l2_device;

1) vivi_fops

static const struct v4l2_file_operations vivi_fops = {

.owner = THIS_MODULE,

.open = vivi_open,

.release = vivi_close,

.read = vivi_read,

.poll = vivi_poll,

.ioctl = video_ioctl2, /* V4L2 ioctl handler */

.mmap = vivi_mmap,

};

2) vivi_ioctl_ops

static const struct v4l2_ioctl_ops vivi_ioctl_ops = {

.vidioc_querycap = vidioc_querycap,

.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,

.vidioc_s_std = vidioc_s_std,

.vidioc_enum_input = vidioc_enum_input,

.vidioc_g_input = vidioc_g_input,

.vidioc_s_input = vidioc_s_input,

.vidioc_queryctrl = vidioc_queryctrl,

.vidioc_g_ctrl = vidioc_g_ctrl,

.vidioc_s_ctrl = vidioc_s_ctrl,

.vidioc_streamon = vidioc_streamon,

.vidioc_streamoff = vidioc_streamoff,

#ifdef CONFIG_VIDEO_V4L1_COMPAT

.vidiocgmbuf = vidiocgmbuf,

#endif

};

3) vivi_template

static struct video_device vivi_template = {

.name = "vivi",

.fops = &vivi_fops,

.ioctl_ops = &vivi_ioctl_ops,

.minor = -1,

.release = video_device_release,

.tvnorms = V4L2_STD_525_60,

.current_norm = V4L2_STD_NTSC_M,

};

其中函数vivi_xxx和vidioc_xxx都是在vivi.c中实现的。如果要基于某个硬件来实现V4L2的接口,那这些函数就需要调用硬件的驱动去实现。

4) vivi_dev

struct vivi_dev {

struct list_head vivi_devlist; //内核双向链表,在内核数据结构里有描述

struct semaphore lock; //信号量,防止竞态访问

int users; //用户数量计数

/* various device info */

unsigned int resources;

struct video_device video_dev; //这个成员是这个结构的核心,用面向对象的话来说就是基类

struct vivi_dmaqueue vidq; //DMA队列

/* Several counters */

int h,m,s,us,jiffies; //定时器定义

char timestr[13]; //其它一些资源变量.

};

像这样变义的结构在Linux C 中很普遍,这也是利用C来实现面向对象编程的强大方法。建立这个结构对象之后,所有的操作都是基于这个结构,或者这个结构派生出的来的其它结构。

5) vivi_fh

struct vivi_fh {

struct vivi_dev *dev;

/* video capture */

struct vivi_fmt *fmt;

unsigned int width,height;

struct videobuf_queue vb_vidq;

enum v4l2_buf_type type;

};

这个结构即是vivi_dev结构的更深层次封装,基于那个结构加入了更多的描述信息,如视频制式、视频画面大小、视频缓冲队列等等。在open的时候,会把这个结构赋给file结构中的private_data域。在释放设备时注销.其它的像ioctl,mmap,read,write等等都会用到这个结构,其实整个模块的编写的cdev差不多。只是视频设备的基类是video_device,而字符设备的基类是cdev而已。

2、数据传输方式:

在设备与应用程序之间有三种数据传输方式:

1)read与write这种方式,它像其它设备驱动一样,但是这种方式很慢,对于数据视频流不能满足其要求;

2)直接的内存访问,可以通过其映射方式来传输(IO数据流,交换指向缓冲区指针的方法);这是视频设备通常用的方法,采用mmap()的方法,即有内核空间里开辟内存,再在程序里把这部分的内存映射到程序空间。如果有设备内存,即直接映射到设备的内核,这种性能更高。

3)异步IO口访问,但是这种方法在V4L2模块中还没有实现。(重要:需要确认)

vivi中的mma p是利用第二种方法来实现的,这也是视频设备常用的方法:

static int

vivi_mmap(struct file *file, struct vm_area_struct * vma)

{

struct vivi_fh *fh = file->private_data;

int ret;

dprintk (1,"mmap called, vma=0x%08lx\n",(unsigned long)vma);

ret=videobuf_mmap_mapper(&fh->vb_vidq, vma);

dprintk (1,"vma start=0x%08lx, size=%ld, ret=%d\n",

(unsigned long)vma->vm_start,

(unsigned long)vma->vm_end-(unsigned long)vma->vm_start,

ret);

return ret;

}

videobuf_mmap_mapper(&fh->vb_vidq, vma); 这个核心函数把设备的I/O内存或者设备内存映射到系统为它开辟的虚拟内存。

3、操控设备的实现: ioctl

static int vivi_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) {

return video_usercopy(inode, file, cmd, arg, vivi_do_ioctl);

}

vivi_do_ioctl 这个函数里调用一些命令来设备V4L2模块中的一些结构参数来改变或者获取设备的参数。

二、V4L2的应用

下面简单介绍一下V4L2驱动的应用流程。

1、视频采集的基本流程

一般的,视频采集都有如下流程:

2、打开视频设备

在V4L2中,视频设备被看做一个文件。使用open函数打开这个设备:

// 用非阻塞模式打开摄像头设备

int cameraFd;

cameraFd = open("/dev/video0", O_RDWR | O_NONBLOCK, 0);

// 如果用阻塞模式打开摄像头设备,上述代码变为:

//cameraFd = open("/dev/video0", O_RDWR, 0);

关于阻塞模式和非阻塞模式:应用程序能够使用阻塞模式或非阻塞模式打开视频设备,如果使用非阻塞模式调用视频设备,即使尚未捕获到信息,驱动依旧会把缓存(DQBUFF)里的东西返回给应用程序。

3、设定属性及采集方式

打开视频设备后,可以设置该视频设备的属性,例如裁剪、缩放等。这一步是可选的。在Linux编程中,一般使用ioctl函数来对设备的I/O通道进行管理:

extern int ioctl (int __fd, unsigned long int __request, ...) __THROW;

__fd:设备的ID,例如刚才用open函数打开视频通道后返回的cameraFd;

__request:具体的命令标志符。

在进行V4L2开发中,一般会用到以下的命令标志符:

VIDIOC_REQBUFS:分配内存

VIDIOC_QUERYBUF:把VIDIOC_REQBUFS中分配的数据缓存转换成物理地址

VIDIOC_QUERYCAP:查询驱动功能

VIDIOC_ENUM_FMT:获取当前驱动支持的视频格式

VIDIOC_S_FMT:设置当前驱动的频捕获格式

VIDIOC_G_FMT:读取当前驱动的频捕获格式

VIDIOC_TRY_FMT:验证当前驱动的显示格式

VIDIOC_CROPCAP:查询驱动的修剪能力

VIDIOC_S_CROP:设置视频信号的边框

VIDIOC_G_CROP:读取视频信号的边框

VIDIOC_QBUF:把数据从缓存中读取出来

VIDIOC_DQBUF:把数据放回缓存队列

VIDIOC_STREAMON:开始视频显示函数

VIDIOC_STREAMOFF:结束视频显示函数

VIDIOC_QUERYSTD:检查当前视频设备支持的标准,例如PAL或NTSC。

这些IO调用,有些是必须的,有些是可选择的。

4、检查当前视频设备支持的标准

在亚洲,一般使用PAL(720X576)制式的摄像头,而欧洲一般使用NTSC(720X480),使用VIDIOC_QUERYSTD来检测:

v4l2_std_id std;

do {

ret = ioctl(fd, VIDIOC_QUERYSTD, &std);

} while (ret == -1 && errno == EAGAIN);

switch (std) {

case V4L2_STD_NTSC:

//……

case V4L2_STD_PAL:

//……

}

5、设置视频捕获格式

当检测完视频设备支持的标准后,还需要设定视频捕获格式:

struct v4l2_format fmt;

memset ( &fmt, 0, sizeof(fmt) );

fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;

fmt.fmt.pix.width = 720;

fmt.fmt.pix.height = 576;

fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;

fmt.fmt.pix.field = V4L2_FIELD_INTERLACED;

if (ioctl(fd, VIDIOC_S_FMT, &fmt) == -1) {

return -1;

}

v4l2_format结构体定义如下:

struct v4l2_format

{

enum v4l2_buf_type type; // 数据流类型,必须永远是V4L2_BUF_TYPE_VIDEO_CAPTURE

union

{

struct v4l2_pix_format pix;

struct v4l2_window win;

struct v4l2_vbi_format vbi;

__u8 raw_data[200];

} fmt;

};

struct v4l2_pix_format

{

__u32 width; // 宽,必须是16的倍数

__u32 height; // 高,必须是16的倍数

__u32 pixelformat; // 视频数据存储类型,例如是YUV4:2:2还是RGB

enum v4l2_field field;

__u32 bytesperline;

__u32 sizeimage;

enum v4l2_colorspace colorspace;

__u32 priv;

};

6、分配内存

接下来可以为视频捕获分配内存:

struct v4l2_requestbuffers req;

if (ioctl(fd, VIDIOC_REQBUFS, &req) == -1) {

return -1;

}

v4l2_requestbuffers定义如下:

struct v4l2_requestbuffers

{

__u32 count; // 缓存数量,也就是说在缓存队列里保持多少张照片enum v4l2_buf_type type; // 数据流类型,必须永远是V4L2_BUF_TYPE_VIDEO_CAPTURE

enum v4l2_memory memory; // V4L2_MEMORY_MMAP 或V4L2_MEMORY_USERPTR

__u32 reserved[2];

};

7、获取并记录缓存的物理空间

使用VIDIOC_REQBUFS,我们获取了req.count个缓存,下一步通过调用

VIDIOC_QUERYBUF命令来获取这些缓存的地址,然后使用mmap函数转换成应用程序中的绝对地址,最后把这段缓存放入缓存队列:

typedef struct VideoBuffer {

void *start;

size_t length;

} VideoBuffer;

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

struct v4l2_buffer buf;

for (numBufs = 0; numBufs < req.count; numBufs++) {

memset( &buf, 0, sizeof(buf) );

buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;

buf.memory = V4L2_MEMORY_MMAP;

buf.index = numBufs;

// 读取缓存

if (ioctl(fd, VIDIOC_QUERYBUF, &buf) == -1) {

return -1;

}

buffers[numBufs].length = buf.length;

// 转换成相对地址

buffers[numBufs].start = mmap(NULL, buf.length,

PROT_READ | PROT_WRITE,

MAP_SHARED,

fd, buf.m.offset);

if (buffers[numBufs].start == MAP_FAILED) {

return -1;

}

// 放入缓存队列

if (ioctl(fd, VIDIOC_QBUF, &buf) == -1) {

return -1;

}

}

8、关于视频采集方式

操作系统一般把系统使用的内存划分成用户空间和内核空间,分别由应用程序管理和操作系统管理。应用程序可以直接访问内存的地址,而内核空间存放的是供内核访问的代码和数据,用户不能直接访问。v4l2捕获的数据,最初是存放在内核空间的,这意味着用户不能直接访问该段内存,必须通过某些手段来转换地址。

一共有三种视频采集方式:

1)使用read、write方式:直接使用read和write函数进行读写。这种方式最简单,但是这种方式会在用户空间和内核空间不断拷贝数据,同时在用户空间和内核空间占用了大量内存,效率不高。

2)内存映射方式(mmap):把设备里的内存映射到应用程序中的内存控件,直接处理设备内存,这是一种有效的方式。上面的mmap函数就是使用这种方式。

3)用户指针模式:内存由用户空间的应用程序分配,并把地址传递到内核中的驱动程序,然后由v4l2驱动程序直接将数据填充到用户空间的内存中。这点需要在

v4l2_requestbuffers里将memory字段设置成V4L2_MEMORY_USERPTR。

第一种方式效率是最低的,后面两种方法都能提高执行的效率,但是对于mmap 方式,文档中有这样一句描述--Remember the buffers are allocated in physical memory, as opposed to virtual memory which can be swapped out to disk. Applications should free the buffers as soon as

possible with the munmap () function .(使用mmap方法的时候,buffers相当于是在内核空间中分配的,这种情况下,这些buffer是不能被交换到虚拟内存中,虽然这种方法不怎么影响读写效率,但是它一直占用着内核空间中的内存,当系统的内存有限的时候,如果同时运行有大量的进程,则对系统的整体性能会有一定的影响。)

所以,对于三种视频采集方式的选择,推荐的顺序是userptr 、mmap 、read-write 。当使用mmap 或userptr 方式的时候,有一个环形缓冲队列的概念,这个队列中,有n 个buffer ,驱动程序采集到的视频帧数据,就是存储在每个buffer 中。在每次用VIDIOC_DQBUF 取出一个buffer ,并且处理完数据后,一定要用VIDIOC_QBUF 将这个buffer 再次放回到环形缓冲队列中。环形缓冲队列,也使得这两种视频采集方式的效率高于直接read/write 。

9、处理采集数据

V4L2有一个数据缓存,存放req.count数量的缓存数据。数据缓存采用FIFO的方式,当应用程序调用缓存数据时,缓存队列将最先采集到的视频数据缓存送出,并重新采集一张视频数据。这个过程需要用到两个ioctl命令,VIDIOC_DQBUF和VIDIOC_QBUF:

struct v4l2_buffer buf;

memset(&buf,0,sizeof(buf));

buf.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;

buf.memory=V4L2_MEMORY_MMAP;

buf.index=0;

//读取缓存

if (ioctl(cameraFd, VIDIOC_DQBUF, &buf) == -1)

{

return -1;

}

//…………视频处理算法

//重新放入缓存队列

if (ioctl(cameraFd, VIDIOC_QBUF, &buf) == -1) {

return -1;

}

10、关闭视频设备

使用close函数关闭一个视频设备

close(cameraFd)

三、V4L2的demo

capture.c是官方示例程序。

capture.c 程序中的process_image 函数:

capture.c 程序主要是用来演示怎样使用v4l2 接口,并没有对采集到的视频帧数据做任何实际的处理,仅仅用process_image 函数表示了处理图像的代码位置。

process_image 函数只有一个参数,就是存储视频帧的内存的地址指针,但是在真正的应用中,通常还需要知道该指针指向的数据的大小。

因此可以修改函数,改成void process_image ( const void * p, int len ) ,但是每次调用

process_image 的时候,第2 个参数该传递什么值?

考虑到程序中对buffer 的定义

struct buffer {

void * start;

size_t length};

如果将buffer.length 作为第2 个参数传递到修改后的process_image 函数中,这样做是不正确的。process_image 需要的第二个参数应该是每帧图像的大小,仔细阅读代码后会发现,buffer.length 并不一定就等于图像帧的大小。(buffer 的大小,还需要考虑其他的一些因素,比如内存对齐等)。

capture.c只是一个示例程序,仅仅是演示怎样使用v4l2中最基本的接口。尤其是在main 函数中的那几个函数调用,表明了在使用v4l2时的最基本的一个流程,包括open_device,init_device,start_capturing,mainloop,stop_capturing,uninit_device,close_device。在写程序的时候,可以充分的利用这几个基本模块,把他们分散在不同的代码位置上,灵活的调用,有兴趣的可以看一下gstreamer中v4l2src的源代码或者其他的大型程序的相关部分。

总之一句话,capture.c仅仅是一个演示程序,不要局限于它的代码结构,要灵活的使用。

下面是capture.c的源代码:

#include

#include

#include

#include

#include /* getopt_long() */

#include /* low-level i/o */

#include

#include

#include

#include

#include

#include

#include

#include

#include /* for videodev2.h */

#include

#define CLEAR(x) memset (&(x), 0, sizeof (x))

typedef enum {

IO_METHOD_READ, IO_METHOD_MMAP, IO_METHOD_USERPTR,

} io_method;

struct buffer {

void * start;

size_t length;//buffer's length is different from cap_image_size

};

static char * dev_name = NULL;

static io_method io = IO_METHOD_MMAP;//IO_METHOD_READ;//IO_METHOD_MMAP; static int fd = -1;

struct buffer * buffers = NULL;

static unsigned int n_buffers = 0;

static FILE * outf = 0;

static unsigned int cap_image_size = 0;//to keep the real image size!!

//////////////////////////////////////////

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;

}

static void process_image(const void * p, int len) {

// static char[115200] Outbuff ;

fputc('.', stdout);

if (len > 0) {

fputc('.', stdout);

fwrite(p, 1, len, outf);

}

fflush(stdout);

}

static int read_frame(void) {

struct v4l2_buffer buf;

unsigned int i;

switch (io) {

case IO_METHOD_READ:

if (-1 == read(fd, buffers[0].start, buffers[0].length)) {

switch (errno) {

case EAGAIN:

return 0;

case EIO:

/* Could ignore EIO, see spec. */

/* fall through */

default:

errno_exit("read");

}

}

// printf("length = %d\r", buffers[0].length);

// process_image(buffers[0].start, buffers[0].length);

printf("image_size = %d,\t IO_METHOD_READ buffer.length=%d\r",

cap_image_size, buffers[0].length);

process_image(buffers[0].start, cap_image_size);

break;

case IO_METHOD_MMAP:

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:

/* Could ignore EIO, see spec. */

/* fall through */

default:

errno_exit("VIDIOC_DQBUF");

}

}

assert(buf.index < n_buffers);

// printf("length = %d\r", buffers[buf.index].length);

// process_image(buffers[buf.index].start, buffers[buf.index].length); printf("image_size = %d,\t IO_METHOD_MMAP buffer.length=%d\r", cap_image_size, buffers[0].length);

process_image(buffers[0].start, cap_image_size);

if (-1 == xioctl(fd, VIDIOC_QBUF, &buf))

errno_exit("VIDIOC_QBUF");

break;

case IO_METHOD_USERPTR:

CLEAR (buf);

buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;

buf.memory = V4L2_MEMORY_USERPTR;

if (-1 == xioctl(fd, VIDIOC_DQBUF, &buf)) {

switch (errno) {

case EAGAIN:

return 0;

case EIO:

/* Could ignore EIO, see spec. */

/* fall through */

default:

errno_exit("VIDIOC_DQBUF");

}

}

for (i = 0; i < n_buffers; ++i)

if (https://www.sodocs.net/doc/c12716549.html,erptr == (unsigned long) buffers[i].start && buf.length

== buffers[i].length)

break;

assert(i < n_buffers);

// printf("length = %d\r", buffers[i].length);

// process_image((void *) https://www.sodocs.net/doc/c12716549.html,erptr, buffers[i].length);

printf("image_size = %d,\t IO_METHOD_USERPTR buffer.length=%d\r", cap_image_size, buffers[0].length);

process_image(buffers[0].start, cap_image_size);

if (-1 == xioctl(fd, VIDIOC_QBUF, &buf))

errno_exit("VIDIOC_QBUF");

break;

}

return 1;

}

static void mainloop(void) {

unsigned int count;

count = 100;

while (count-- > 0) {

for (;;) {

fd_set fds;

struct timeval tv;

int r;

FD_ZERO(&fds);

FD_SET(fd, &fds);

/* Timeout. */

https://www.sodocs.net/doc/c12716549.html,_sec = 2;

https://www.sodocs.net/doc/c12716549.html,_usec = 0;

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

if (-1 == r) {

if (EINTR == errno)

continue;

errno_exit("select");

}

if (0 == r) {

fprintf(stderr, "select timeout\n");

exit(EXIT_FAILURE);

}

if (read_frame())

break;

/* EAGAIN - continue select loop. */

}

}

}

static void stop_capturing(void) {

enum v4l2_buf_type type;

switch (io) {

case IO_METHOD_READ:

/* Nothing to do. */

break;

case IO_METHOD_MMAP:

case IO_METHOD_USERPTR:

type = V4L2_BUF_TYPE_VIDEO_CAPTURE;

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

break;

}

}

static void start_capturing(void) {

unsigned int i;

enum v4l2_buf_type type;

switch (io) {

case IO_METHOD_READ:

/* Nothing to do. */

break;

case IO_METHOD_MMAP:

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");

break;

case IO_METHOD_USERPTR:

for (i = 0; i < n_buffers; ++i) {

struct v4l2_buffer buf;

CLEAR (buf);

buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; buf.memory = V4L2_MEMORY_USERPTR;

buf.index = i;

https://www.sodocs.net/doc/c12716549.html,erptr = (unsigned long) buffers[i].start;

buf.length = buffers[i].length;

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");

break;

}

}

static void uninit_device(void) {

unsigned int i;

switch (io) {

case IO_METHOD_READ:

free(buffers[0].start);

break;

case IO_METHOD_MMAP:

for (i = 0; i < n_buffers; ++i)

if (-1 == munmap(buffers[i].start, buffers[i].length)) errno_exit("munmap");

break;

case IO_METHOD_USERPTR:

for (i = 0; i < n_buffers; ++i)

free(buffers[i].start);

break;

}

free(buffers);

}

static void init_read(unsigned int buffer_size) {

buffers = calloc(1, sizeof(*buffers));

if (!buffers) {

fprintf(stderr, "Out of memory\n");

exit(EXIT_FAILURE);

}

buffers[0].length = buffer_size;

buffers[0].start = malloc(buffer_size);

if (!buffers[0].start) {

fprintf(stderr, "Out of memory\n");

exit(EXIT_FAILURE);

}

}

static void init_mmap(void) {

struct v4l2_requestbuffers req;

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 (EINV AL == errno) {

fprintf(stderr, "%s does not support "

"memory mapping\n", dev_name);

exit(EXIT_FAILURE);

} else {

errno_exit("VIDIOC_REQBUFS");

}

}

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 /* start anywhere */, buf.length, PROT_READ | PROT_WRITE /* required */,

MAP_SHARED /* recommended */, fd, buf.m.offset);

if (MAP_FAILED == buffers[n_buffers].start)

errno_exit("mmap");

}

}

static void init_userp(unsigned int buffer_size) {

struct v4l2_requestbuffers req;

unsigned int page_size;

page_size = getpagesize();

buffer_size = (buffer_size + page_size - 1) & ~(page_size - 1);

CLEAR (req);

req.count = 4;

req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;

req.memory = V4L2_MEMORY_USERPTR;

if (-1 == xioctl(fd, VIDIOC_REQBUFS, &req)) {

if (EINV AL == errno) {

fprintf(stderr, "%s does not support "

"user pointer i/o\n", dev_name);

exit(EXIT_FAILURE);

} else {

errno_exit("VIDIOC_REQBUFS");

}

}

buffers = calloc(4, sizeof(*buffers));

if (!buffers) {

fprintf(stderr, "Out of memory\n");

exit(EXIT_FAILURE);

}

for (n_buffers = 0; n_buffers < 4; ++n_buffers) {

buffers[n_buffers].length = buffer_size;

buffers[n_buffers].start = memalign(/* boundary */page_size, buffer_size);

if (!buffers[n_buffers].start) {

fprintf(stderr, "Out of memory\n");

exit(EXIT_FAILURE);

}

}

}

static void init_device(void) {

struct v4l2_capability cap;

struct v4l2_cropcap cropcap;

struct v4l2_crop crop;

struct v4l2_format fmt;

unsigned int min;

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

if (EINV AL == 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);

}

switch (io) {

case IO_METHOD_READ:

if (!(cap.capabilities & V4L2_CAP_READWRITE)) {

fprintf(stderr, "%s does not support read i/o\n", dev_name);

exit(EXIT_FAILURE);

}

break;

case IO_METHOD_MMAP:

case IO_METHOD_USERPTR:

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

fprintf(stderr, "%s does not support streaming i/o\n", dev_name); exit(EXIT_FAILURE);

}

break;

}

//////not all capture support crop!!!!!!!

/* Select video input, video standard and tune here. */

printf("-#-#-#-#-#-#-#-#-#-#-#-#-#-\n");

CLEAR (cropcap);

cropcap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;

if (0 == xioctl(fd, VIDIOC_CROPCAP, &cropcap)) {

crop.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;

#ifndef CROP_BY_JACK

crop.c = cropcap.defrect; /* reset to default */

#else

crop.c.left = cropcap.defrect.left;

crop.c.top = cropcap.defrect.top;

crop.c.width = 352;

crop.c.height = 288;

#endif

printf("----->has ability to crop!!\n");

printf("cropcap.defrect = (%d, %d, %d, %d)\n", cropcap.defrect.left, cropcap.defrect.top, cropcap.defrect.width,

cropcap.defrect.height);

if (-1 == xioctl(fd, VIDIOC_S_CROP, &crop)) {

switch (errno) {

case EINV AL:

/* Cropping not supported. */

break;

default:

/* Errors ignored. */

break;

}

printf("-----!!but crop to (%d, %d, %d, %d) Failed!!\n",

crop.c.left, crop.c.top, crop.c.width, crop.c.height);

} else {

printf("----->sussess crop to (%d, %d, %d, %d)\n", crop.c.left,

crop.c.top, crop.c.width, crop.c.height);

}

} else {

/* Errors ignored. */

printf("!! has no ability to crop!!\n");

}

printf("-#-#-#-#-#-#-#-#-#-#-#-#-#-\n");

printf("\n");

////////////crop finished!

//////////set the format

CLEAR (fmt);

fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;

fmt.fmt.pix.width = 640;

fmt.fmt.pix.height = 480;

//V4L2_PIX_FMT_YVU420, V4L2_PIX_FMT_YUV420 — Planar formats with 1/2 horizonta l and vertical chroma resolution, also known as YUV 4:2:0

//V4L2_PIX_FMT_YUYV — Packed format with 1/2 horizontal chroma resolution, also know n as YUV 4:2:2

fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;//V4L2_PIX_FMT_YUV420;//V4L2_PIX _FMT_YUYV;

fmt.fmt.pix.field = V4L2_FIELD_INTERLACED;

{

printf("-#-#-#-#-#-#-#-#-#-#-#-#-#-\n");

printf("=====will set fmt to (%d, %d)--", fmt.fmt.pix.width,

fmt.fmt.pix.height);

if (fmt.fmt.pix.pixelformat == V4L2_PIX_FMT_YUYV) {

printf("V4L2_PIX_FMT_YUYV\n");

} else if (fmt.fmt.pix.pixelformat == V4L2_PIX_FMT_YUV420) {

printf("V4L2_PIX_FMT_YUV420\n");

} else if (fmt.fmt.pix.pixelformat == V4L2_PIX_FMT_NV12) {

printf("V4L2_PIX_FMT_NV12\n");

}

}

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

errno_exit("VIDIOC_S_FMT");

{

printf("=====after set fmt\n");

printf(" fmt.fmt.pix.width = %d\n", fmt.fmt.pix.width);

printf(" fmt.fmt.pix.height = %d\n", fmt.fmt.pix.height);

printf(" fmt.fmt.pix.sizeimage = %d\n", fmt.fmt.pix.sizeimage);

cap_image_size = fmt.fmt.pix.sizeimage;

printf(" fmt.fmt.pix.bytesperline = %d\n", fmt.fmt.pix.bytesperline);

printf("-#-#-#-#-#-#-#-#-#-#-#-#-#-\n");

printf("\n");

}

cap_image_size = fmt.fmt.pix.sizeimage;

/* Note VIDIOC_S_FMT may change width and height. */

printf("-#-#-#-#-#-#-#-#-#-#-#-#-#-\n");

/* Buggy driver paranoia. */

min = fmt.fmt.pix.width * 2;

if (fmt.fmt.pix.bytesperline < min)

fmt.fmt.pix.bytesperline = min;

min = fmt.fmt.pix.bytesperline * fmt.fmt.pix.height;

if (fmt.fmt.pix.sizeimage < min)

fmt.fmt.pix.sizeimage = min;

printf("After Buggy driver paranoia\n");

printf(" >>fmt.fmt.pix.sizeimage = %d\n", fmt.fmt.pix.sizeimage);

printf(" >>fmt.fmt.pix.bytesperline = %d\n", fmt.fmt.pix.bytesperline); printf("-#-#-#-#-#-#-#-#-#-#-#-#-#-\n");

printf("\n");

switch (io) {

case IO_METHOD_READ:

init_read(fmt.fmt.pix.sizeimage);

break;

case IO_METHOD_MMAP:

init_mmap();

break;

case IO_METHOD_USERPTR:

init_userp(fmt.fmt.pix.sizeimage);

break;

}

}

static void close_device(void) {

if (-1 == close(fd))

errno_exit("close");

fd = -1;

}

static void open_device(void) {

struct stat st;

if (-1 == stat(dev_name, &st)) {

fprintf(stderr, "Cannot identify '%s': %d, %s\n", dev_name, errno,

strerror(errno));

exit(EXIT_FAILURE);

}

if (!S_ISCHR(st.st_mode)) {

fprintf(stderr, "%s is no device\n", dev_name);

exit(EXIT_FAILURE);

linux网卡驱动的配置

RedHat5.1下安装Realtek8168网卡驱动 1.先识别机器上的网卡型号: [root@localhost kernel]#kudzu --probe --class=network 2、使用命令查看kernel包是否都装全了,具体如下: [root@localhost kernel]# rpm -qa | grep kernel 如果装全了,会显示如下几个包: kernel-xen-devel-2.6.18-8.el5 kernel-devel-2.6.18-8.el5 kernel-2.6.18-8.el5 kernel-xen-2.6.18-8.el5 kernel-headers-2.6.18-8.el5 如果没有装全,就在redhat enterprise Linux 5.0第一个ISO的Server文件目录下可以找到对应的包,拷贝到某一具体目录下,进入目录后,用如下命令就可以进行安装: [root@localhost kernel]# rpm -ivh kernel-devel-2.6.18-8.el5.x86_64.rpm

注意:如果不装全这些包,在网卡编译时就会报错说找不到src目录文件。 用如下命令查看GCC是否安装,通常都装上的,还是检查一下比较好。 [root@localhost 2.6.18-8.el5]# whereis -b gcc gcc : /usr/bin/gcc /usr/lib/gcc /usr/libexec/gcc 3、将r8168-8.aaa.bb.tar.bz2解压出来变成r8168-8.aaa.bb文件夹形式,拷到U盘 在redhat下挂载U盘: 插入U盘 [root@localhost kernel]#mount /dev/sdb1 /mnt/usb 4.将r8168-8.aaa.bb文件夹拷都本地,如/home目录下 首先将u盘中的驱动程序包拷贝到/home目录下 [root@localhost media]# cp r8168-8\[1\].011.00.tar.bz2 /home/ [root@localhost media]# cd /home/ [root@localhost home]# ls r8168-8[1].011.00.tar.bz2 5.其次解压驱动程序包 [root@localhost home]# tar -vjxf r8168-8\[1\].011.00.tar.bz2

基于linux的led驱动程序实现

基于linux的led驱动程序实现 一. 博创开发平台硬件LED的实现 博创开发平台设置了3个GPIO控制的LED和一个可直接产生外部硬件中断的按键,LED分别使用了S3C2410的GPC5,GPC6,GPC7三个GPIO,按键接到INT5中断。下面对S3C2410 GPIO的各个寄存器作出说明,用GPIO控制的LED就是通过操作GPIO的各个寄存器进行配置和操作的。S3C2410包含GPA 、GPB 、……、GPH 八个I/O端口。它们的寄存器是相似的:GPxCON 用于设置端口功能(00 表示输入、01表示输出、10 表示特殊功能、11 保留不用),GPxDAT 用于读/写数据,GPxUP 用于决定是否使用内部上拉电阻(某位为0 时,相应引脚无内部上拉;为1时,相应引脚使用内部上拉)。这里要稍微注意一点的地方是PORTA和其他几组端口的使用不太一样,这里不讨论A口,B到H组口的使用完全相同。以下是S3C2410手册上的数据[13]: 图1.1 S3C2410端口 GPC口有16个IO口,查datasheet《S3C2410》所用的地址为: 图1.2 C组GPIO的地址 即GPCCON 地址为0x56000020,GPCDAT地址为0x56000024,各位的设置具体见下图,则对应的GPCCON寄存器的位为:

图1.3 GPCCON寄存器相应的位 这里用到了5,6,7三个口,CON寄存器要完成对对应口的设置工作,将相应的口设置为输出状态,其他的口不用考虑,设置为输出的话就是0x15<<10,这样3个IO口就设置为了输出。下面就可以通过向DATA口写入低电平来点亮LED,GPCDAT的各位分布如下,每一个bit对应一个口。 图1.4 GPCDAT的位分布 GPCDAT有16位,我们这里要用到的就是5,6,7三位即将这3位设置为低电平点亮LED。具体使用情况见驱动的实现。 这三个LED的硬件原理图如下: 图1.5 GPIO控制的LED硬件原理图 二.通过GPIO控制的LED驱动程序 本驱动中没有用到内核提供的write_gpio宏,对硬件地址的操作完全自己实现,可分为以下几部分: ①模块的初始化和退出: int led_init(void)

linux下安装编译网卡驱动的方法

linux下安装编译网卡驱动的方法 你还在为不知道linux下安装编译网卡驱动的方法而不知所措么?下面来是小编为大家收集的linux下安装编译网卡驱动的方法,欢迎大家阅读: linux下安装编译网卡驱动的方法 安装linux操作系统后发现没有网卡驱动,表现为 system → Administration → Network下Hardware列表为空。 以下为安装编译网卡驱动的过程,本人是菜鸟,以下是我从网上找的资料进行整理,并实际操作的过程,仅供借鉴。 一.检测linux系统内核版本和网卡类型,相关命令如下: uname -r 查看linux内核版本(uname -a 可显示所有信息)

lsmod 设备加载情况 ls /usr/share/hwdata 查看硬件设备 lspci 查看pci网卡设备ethernet controller 厂商和型号,modprobe **** ****为网卡型号,例如modprobe RTL8101E ,如果出错,说明模块不存在,该型号不识别 我在这一步时查找不到网卡型号,无奈只能由同时采购的其他相同型号预装win7的电脑下查看网卡型号,是个笨办法,嘿嘿…… 找到网卡型号后就到驱动之家下载了相应网卡的linux驱动,这些需要根据自己的实际情况下载,不多说了,重点是后面。 二.下载网卡驱动 Intel_e1000e-1.9.5.zip 为我下载的所需的网卡驱动,这个在linux下需自己编译. 三.安装网卡驱动

1.检测编译需要用到内核的源代码包和编译程序gcc。所以如果没有的话,要先装。 [root@localhost ~]# rpm -qa|grep kernel kernel-xen-2.6.18-8.el5 kernel-xen-devel-2.6.18-8.el5 kernel-headers-2.6.18-8.el5 [root@localhost ~]# rpm -qa|grep gcc gcc-c++-4.1.1-52.el5 libgcc-4.1.1-52.el5 gcc-4.1.1-52.el5 gcc-gfortran-4.1.1-52.el5 如果缺少kernel-xen-devel-2.6.18-8.el5,可以去安装光

USB键盘驱动程序

/* * $Id: usbkbd.c,v 1.27 2001/12/27 10:37:41 vojtech Exp $ * * Copyright (c) 1999-2001 Vojtech Pavlik * * USB HIDBP Keyboard support */ /* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Should you need to contact me, the author, you can do so either by * e-mail - mail your message to <>, or by paper mail: * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic */ #include #include #include #include #include #include #include /* * Version Information */ #define DRIVER_VERSION "" #define DRIVER_AUTHOR "Vojtech Pavlik <>" #define DRIVER_DESC "USB HID Boot Protocol keyboard driver" #define DRIVER_LICENSE "GPL"

linux 设备输入子系统---源代码示例。自动捕获键盘鼠标等外设消息

Linux input 子系统详解与代码示例 李邦柱于杭州2014/01/09 Email:helpylee@https://www.sodocs.net/doc/c12716549.html, 由于linux的驱动模型增加了input层,导致几乎所有的底层驱动都把数据封装在event里上报给input子系统。由此看来,这种改变让kernel 更具有模块化,各个模块的耦合度更低了。下面我们一起来研究input 层^_^ 1.从用户层的角度看input(event事件) 了解linux的人一定会对/dev,/ sys, /proc这几个目录有所印象,这是从内核导出到用户层的接口(从这里几乎可以观览内核)。kernel为我们导出了input在用户态的接口,就是/dev/input/下的接口,所以我们只关注这个目录下的event*(event0/event1/……)字符设备。 那么这些event*是干什么用的?简单来说就是我们对计算机的输入(包括敲击键盘,移动鼠标等等操作)经过内核(底层驱动,input)处理最后就上报到这些event*里面了。 而这里event0,event1,..就是用来区分各个外设的,可以通过命令来查看外设具体和哪个event相关联:这个命令是:cat /proc/bus/input/devices 所以我们用此命令在linux系统查看外设信息。 2.在linux/input.h中有这些数据的结构: structinput_event { structtimeval time; //事件发生的时间 __u16 type; //事件类类型:按键和移动鼠标就是不同类型 __u16 code; __s32 value; //事件值:按键a和按键b就对应不同值 }; code: 事件的代码.如果事件的类型代码是EV_KEY,该代码code为设备键盘代码.代码植0~127为键盘上的按键代码,0x110~0x116 为鼠标上按键代码,其中0x110(BTN_ LEFT)为鼠标左键,0x111(BTN_RIGHT)为鼠标右键,0x112(BTN_ MIDDLE)为鼠标中键.其它代码含义请参

在linux系统下如何安装网卡驱动

2011年研发二部工作 周报月报 作者:赵玉武 时间:2012-6-13

目录 一、整理漏扫系统的结构 (1) 1、整理NetScan目录中的程序。 (1) 2、整理proftpd目录中内容(插件的检测信息)。 (4) 3、整理proxyd目录中的安管(安管平台)。 (8) 二、熟悉Nessus的工作原理 (8) 1、Nessus扫描引擎的工作原理... 错误!未定义书签。 三、整理运行的整个流程.............. 错误!未定义书签。 1、通过客户端下发策略,上传到服务器上。错误!未定 义书签。 2、服务端:接收客户端下发的策略。错误!未定义书签。 3、服务端进行身份的认证....... 错误!未定义书签。

一、整理漏扫系统的结构 网卡是Linux服务器中最重要网络设备。据统计,Linux网络故障有35%在物理层、25%在数据链路层、10%在网络层、10%在传输层、10%在对话、7%在表示层、3%在应用层。由此可以看出,网络故障通常发生在网络七层模型的下三层,即物理层、链路层和网络层。对应于实际网络也就是使用的网络线缆、网卡、交换机、路由器等设备故障。Linux的网络实现是模仿FreeBSD的,它支持FreeBSD 的带有扩展的Sockets(套接字)和TCP/IP协议。它支持两个主机间的网络连接和Sockets通讯模型,实现了两种类型的Sockets:BSD Sockets和INET Sockets。它为不同的通信模型和服务质量提供了两种传输协议,即不可靠的、基于消息的UDP传输协议和可靠的、基于流的传输协议TCP,并且都是在IP网络协议上实现的。INET sockets 是在以上两个协议及IP协议之上实现的。 由于交换机、路由器通常独立于Linux或者其他操作系统。网卡设置故障是造成Linux 服务器故障最主要原因。可能因为硬件的质量或性能、磨损老化、人为误操作、不正确的网络设置、管理问题、Linux软件的BUG、系统受到黑客攻击和Linux病毒等原因造成。 Linux 服务器网卡故障排除的思路是:应当遵循先硬件后软件的方法。因为硬件如果出现物理损坏那么如何设定网卡都不能解决故障。解决问题的方法可以从自身Linux计算机的网卡查起,如果确定硬件没有问题了,再来考虑软件的设定。 1、网卡的选择 一般来说,2.4版本以后的 Linux可以支持的网卡芯片组数量已经很完备了,包括著名厂商如:Intel 以及使用广泛的 RealTek, Via 等网卡芯片都已经被支持,所以使用者可以很轻易的设定好他们的网

linux驱动程序的编写

linux驱动程序的编写 一、实验目的 1.掌握linux驱动程序的编写方法 2.掌握驱动程序动态模块的调试方法 3.掌握驱动程序填加到内核的方法 二、实验内容 1. 学习linux驱动程序的编写流程 2. 学习驱动程序动态模块的调试方法 3. 学习驱动程序填加到内核的流程 三、实验设备 PentiumII以上的PC机,LINUX操作系统,EL-ARM860实验箱 四、linux的驱动程序的编写 嵌入式应用对成本和实时性比较敏感,而对linux的应用主要体现在对硬件的驱动程序的编写和上层应用程序的开发上。 嵌入式linux驱动程序的基本结构和标准Linux的结构基本一致,也支持模块化模式,所以,大部分驱动程序编成模块化形式,而且,要求可以在不同的体系结构上安装。linux是可以支持模块化模式的,但由于嵌入式应用是针对具体的应用,所以,一般不采用该模式,而是把驱动程序直接编译进内核之中。但是这种模式是调试驱动模块的极佳方法。 系统调用是操作系统内核和应用程序之间的接口,设备驱动程序是操作系统内核和机器硬件之间的接口。设备驱动程序为应用程序屏蔽了硬件的细节,这样在应用程序看来,硬件设备只是一个设备文件,应用程序可以像操作普通文件一样对硬件设备进行操作。同时,设备驱动程序是内核的一部分,它完成以下的功能:对设备初始化和释放;把数据从内核传送到硬件和从硬件读取数据;读取应用程序传送给设备文件的数据和回送应用程序请求的数据;检测和处理设备出现的错误。在linux操作系统下有字符设备和块设备,网络设备三类主要的设备文件类型。 字符设备和块设备的主要区别是:在对字符设备发出读写请求时,实际的硬件I/O一般就紧接着发生了;块设备利用一块系统内存作为缓冲区,当用户进程对设备请求满足用户要求时,就返回请求的数据。块设备是主要针对磁盘等慢速设备设计的,以免耗费过多的CPU时间来等待。 1 字符设备驱动结构 Linux字符设备驱动的关键数据结构是cdev和file_operations结构体。

Linux下网卡驱动程序

Linux下8019网卡驱动程序 福建鑫诺通信有限公司陈光平(chenggp_fj@https://www.sodocs.net/doc/c12716549.html,) 本文以S3C44B0的CPU为例,详细解析了linux下RTL8019网卡驱动程序工作原理,其间知识大多来源互联网络,特别是浙大潘纲的论文,在此不一一列出,此文目的只是让嵌入式linux爱好者得到更多网卡驱动的资料,并获得交流机会,不足之处请指正 (一)、硬件相关部份 1、CPU与网卡的连接方式 (s3c44B0 CPU) (RTL 8019网卡) CPU与网卡接线图 上图为S3c44b0CPU和网卡的接线图,此接法并非固定,如接法不同,则牵涉到很多相应的改动,下面会详细分析不同之处 从硬件部门得到:网卡在CPU的存储空间上接BANK4,即0x08000000(看44B0手册)外部中断号为:EXTINT3 (irqs.h文件获得值为22) 上面两个值可以查CPU手册,或询问硬件设计人员 由上图可以知道以下数据: (1)、网卡与CPU地址线连接错开8位(A0接A8) (2)、总共连线,其实4根就足够用了,因为每根线可以译码4个地址空间,总共是16个地址空间,每个地址空间对应一个寄存器地址,而8019总共就是16个寄存器(3)、一般是跳线模式,不使用9346芯片 1-1 基地址算法 首先8019的基地址是300H(见RTL8019芯片资料:选择IO总线地址),但是有些硬件已在芯片中做过了偏移,比如我们的网卡已做了处理,基址已偏移到0x08000000, 那么因为网卡A0接CPU的A8,表示基地址左移8位,下一个寄存器reg0的地址就是:0x08000100(0000,0000,0001 0001,0000,0000) 还不理解的话我们看另一种接法:

Linux下查看网卡驱动和版本信息

Linux下查看网卡驱动和版本信息 查看网卡生产厂商和信号 查看基本信息:lspci 查看详细信息:lspci -vvv # 3个小写的v 查看网卡信息:lspci | grep Ethernet 查看网卡驱动 查看网卡驱动信息:lspci -vvv # 找到网卡设备的详细信息,包括网卡驱动 # lsmod 列出加载的所有驱动,包括网卡驱动 查看网卡驱动版本 查看模块信息:modifo # 其中包含version信息 或# ethtool-i RHEL 6.3中的网卡驱动版本: # modinfo igb filename: /lib/modules/2.6.32-279.el6.x86_64/kernel/drivers/net/igb/igb.ko version: 3.2.10-k license: GPL description: Intel(R) Gigabit Ethernet Network Driver # modinfo e1000e filename: /lib/modules/2.6.32-279.el6.x86_64/kernel/drivers/net/e1000e/e1000e.ko version: 1.9.5-k license: GPL

description: Intel(R) PRO/1000 Network Driver author: Intel Corporation, # modinfo e1000 filename: /lib/modules/2.6.32-279.el6.x86_64/kernel/drivers/net/e1000/e1000.ko version: 8.0.35-NAPI license: GPL description: Intel(R) PRO/1000 Network Driver # modinfo ixgbe filename: /lib/modules/2.6.32-279.el6.x86_64/kernel/drivers/net/ixgbe/ixgbe.ko version: 3.6.7-k license: GPL description: Intel(R) 10 Gigabit PCI Express NetworkDriver # modinfo r8169 filename: /lib/modules/2.6.32-279.el6.x86_64/kernel/drivers/net/r8169.ko version: 2.3LK-NAPI license: GPL description: RealTek RTL-8169 Gigabit Ethernet driver 查看网络接口队列数 查看网卡接口的中断信息:#cat /proc/interrupts | grep eth0 或# ethtool-S eth0 查看网卡驱动源码的版本号 解压Intel网卡驱动源码,打开解压缩目录下的*.spec文件查看驱动的版本。 例如:解压e1000-8.0.35.tar.gz网卡驱动后,查看e1000.spec文件。

LINUX系统必备程序安装步骤

1.红帽企业版5获得root权限方法: su root

2. 红帽企业版5 启动samba的方法: a.在安装LINUX的过程中将所有选项都选择上,这样可以确保samba等软件 都已经安装好。 b. 修改/etc/samba/smb.conf,添加: [root] comment = Root Directories browseable = yes writeable = yes path = / valid users = smb(用户名) c.添加用户: RHEL5: Useradd smb //添加smb系统用户 Smbpasswd -a smb //修改密码 d.重新启动samba: /etc/init.d/smb restart e.windows访问LINUX 访问LINUX的IP地址,输入用户名smb及密码就可以正常访问linux了

3.建立tftp服务器: a.在安装LINUX的过程中将所有选项都选择上,这样可以确保tftp等软件都 已经安装好。 b.建立TFTP主工作目录: mkdir /tftpboot c.修改配置文件 vi /etc/xinet d.d/tftp修改内容如下: d.重新启动tftp /etc/init.d/xinetd restart e.确认TFTP启动的是否成功:netstat –a | grep tftp

4.NFS a.配置vi /etc/exports b.重新启动NFS服务器:/etc/init.d/nfs restart

5.升级安装LINUX内核: a.解压缩内核代码tar –xvzf linux-2.6.32.27 b.拷贝config 文件将目录boot下的原先LINUX内核的CONFIG文件复制到 新内核的根目录下名字为.config c.make menuconfig d.make bzImage e.make modules f. make modules_install g.制作init ramdisk: h.内核安装: i.升级内核后重新启动机器所遇到的问题解决办法: 方法:编译时修改.config文件中的“CONFIG_SYSFS_DEPRECATED_V2”,默认该选项为not set,被注释掉的,将其改为y。即修改为 “CONFIG_SYSFS_DEPRECATED_V2=y”,修改后,再编译,重启即正常了。

Linux驱动框架及驱动加载

本讲主要概述Linux设备驱动框架、驱动程序的配置文件及常用的加载驱动程序的方法;并且介绍Red Hat Linux安装程序是如何加载驱动的,通过了解这个过程,我们可以自己将驱动程序放到引导盘中;安装完系统后,使用kudzu自动配置硬件程序。 Linux设备驱动概述 1. 内核和驱动模块 操作系统是通过各种驱动程序来驾驭硬件设备,它为用户屏蔽了各种各样的设备,驱动硬件是操作系统最基本的功能,并且提供统一的操作方式。正如我们查看屏幕上的文档时,不用去管到底使用nVIDIA芯片,还是ATI芯片的显示卡,只需知道输入命令后,需要的文字就显示在屏幕上。硬件驱动程序是操作系统最基本的组成部分,在Linux内核源程序中也占有较高的比例。 Linux内核中采用可加载的模块化设计(LKMs ,Loadable Kernel Modules),一般情况下编译的Linux内核是支持可插入式模块的,也就是将最基本的核心代码编译在内核中,其它的代码可以选择是在内核中,或者编译为内核的模块文件。 如果需要某种功能,比如需要访问一个NTFS分区,就加载相应的NTFS模块。这种设计可以使内核文件不至于太大,但是又可以支持很多的功能,必要时动态地加载。这是一种跟微内核设计不太一样,但却是切实可行的内核设计方案。 我们常见的驱动程序就是作为内核模块动态加载的,比如声卡驱动和网卡驱动等,而Linux最基础的驱动,如CPU、PCI总线、TCP/IP协议、APM(高级电源管理)、VFS等驱动程序则编译在内核文件中。有时也把内核模块就叫做驱动程序,只不过驱动的内容不一定是硬件罢了,比如ext3文件系统的驱动。 理解这一点很重要。因此,加载驱动时就是加载内核模块。下面来看一下有关模块的命令,在加载驱动程序要用到它们:lsmod、modprob、insmod、rmmod、modinfo。 lsmod

linux下安装无线网卡驱动

换了linux系统后发现无线不能使用,很是苦恼,于是在网上找到各位大神的做法,终于成功安装。下面给大家介绍一下。本例以安装Fedora 14无线网卡驱动为例。参考文章 https://www.sodocs.net/doc/c12716549.html,/Linux/2011-05/35366.htm。 首先在https://www.sodocs.net/doc/c12716549.html,/support/802.11/linux_sta.php 上下载32位的驱动, 下载完成之后进入到下载的文件夹下 #cd ~/Downloads 按照readme的做法,新建一个文件夹,并且将该压缩文件移动到新建的文件夹当中 #mkdri hybrid_wl #mv hybrid-portsrc-x86_32-v5.60.48.36.tar.gz hybrid_wl/ 进入文件夹,进行解压, #cd hybrid_wl/ #tar -zxvf hybrid-portsrc-x86_32-v5.60.48.36.tar.gz 由于自述文件当中的做法是指定解压目录,因此为了保险,再把这个压缩文件移动到上一级目录 #mv hybrid-portsrc-x86_32-v5.60.48.36.tar.gz ../ 根据自述文件的说法(The cards with the following PCI Device IDs are supported with this driver.),查看支持的版本 # lspci -n | grep 14e4 0c:00.0 0280: 14e4:4315 (rev 01) BRCM PCI PCI DellProduct Name Vendor ID Device ID Product ID ------------- ---------- --------- ----------- 4311 2.4 Ghz 0x14e4 0x4311 Dell 1390 4311 Dualband 0x14e4 0x4312 Dell 1490

Linux网卡驱动程序详解

当网络上一台计算机准备发送数据时,他的网卡开始工作了,首先网卡的芯片侦听在网络上是否有数据在流动,如果没有,他就把数据发送到网络上,在侦听和发送之间有一段极小的时间延迟,在这段时间内,也有可能在网络上有其他的计算机也准备发送数据,也侦听到网络上没有数据在流动,这就可能两台甚至多台的数据一起发送到网络上,产生数据的碰撞,发送数据的计算机的网卡芯片当然要在发送完成后再校验返回的数据,如果发现和发送的数据不一致,那就是说产生了碰撞,所以在一个以太网络中的计算机数量不宜过多,他不但会增加广播包在网络中的数量,也请也会增加数据包的碰撞次数. 我们的计算机的网卡芯片在接收到一完整的数据包后,芯片的一引脚通知8259中断控制器,中断控制器再发出中断给CPU,由此,CPU随即调用该网卡的中断例程,如: DOS是这样的 屏蔽所有中断(cli) push any register 因为中断向量在段0 所以xor ax,ax mov ds,ax mul ax,中断号 那么在数据段的[ax]偏移处是该中断例程的指针了call [ax]就到该中断例程了...(DOS是比较遥远的事情了,我所描述的是他的原理,当然不会这么简单,如果那位网友有兴趣详细描述一下上面的原理,纠正或替换掉我所写的就感激不尽了) 总之,在本例程中,CPU将调用elintr中断例程,并带有参数unit即该种网卡的第几块(因为在计算机中,你有可能装了相同的网卡有几块),elintr的作用是把数据从网卡的数据存储器中读到我们在该网卡初始化时预先分配好的数据缓冲区中,他调用的函数就只有elread,同样elread也只调用了elget一个函数.elread函数比较简单,就是调用elget,elget则相对比较复杂一点,涉及到核心内存分配mbuf,mbuf是比较恐怖的东西,正如STEVEN所写的,为了节约当时"巨大"的4M内存,牺牲了性能搞出了这个mbuf东东,mbuf是必须要弄懂的,虽然在设备驱动程序中调用他的宏和函数不多,但在后面的IP协议,TCP协议中有不少涉及的地方. 关于数据发送方面和接收差不多,在上层协议放置好数据到mbuf链后,调用el_start函数,该函数把mbuf链中的数据放置到本块网卡的发送队列缓冲el_pktbuf中,然后再调用el_xmit 函数,此函数把发送队列缓冲el_pktbuf中的数据有传递到网卡的数据存储器中.我认为,这中间的内存拷贝是多于的,应该在el_start函数中直接把mbuf中的数据传递到网卡的数据存储器中,这样会使性能有较大幅度的提升,因为在驱动程序设计时,最好减少大量的内存拷贝,他占用的时间太多了. */ /* FreeBSD的3COM以太网设备驱动程序*/ /*本段头文件是在编译核心时产生的*/ #include "el.h" /*此三文件为编译时产生的头文件,内容是定制核心的一些常量*/ #include "opt_inet.h"

基于Linux系统的HHARM9电机驱动程序设计

收稿日期:2005-09-22 作者简介:朱华生(1965-),男,江西临川人,副教授. 文章编号:1006-4869(2005)04-0051-03 基于Linux 系统的HHARM9电机驱动程序设计 朱华生,胡凯利 (南昌工程学院计算机科学与技术系,江西南昌330099) 摘 要:对嵌入式Linux 操作系统驱动程序的组成进行分析,讨论了驱动程序的基本框架,以HHARM9电机控制为实例,详细论述了电机驱动程序的实现过程. 关键词:嵌入式;Linux;驱动程序 中图分类号:TP316 文献标识码:A Linux System -Based Design of HHARM 9Electromotor Driver ZHU Hua -sheng,HU Ka-i li (Department of Computer and Science,Nanchang Institute of Technology,Nanchang 330099,China) Abstract:The paper analyses the composition of driver in embedded linux system,disuses its basic frame of driver,and illustrales the process of driver design of HHARM9electromotor in detail. Key words:Embedded;Linux; driver 嵌入式Linux 操作系统因具有免费、开放源代码、强大的网络功能等 特点,在嵌入式产品中得到越来越广泛的应用.基于Linux 操作系统的嵌入 式产品结构[1]如图1所示.本文主要探讨嵌入式系统驱动程序的设计. 1 嵌入式Linux 操作系统驱动程序简介 1)驱动程序和应用程序的区别 驱动程序的设计和应用程序的设计有很大的区别[2].首先,驱动程序 的设计要对硬件的结构、信号的工作流程十分清楚,而在应用程序的设计 中,一般不需要了解这些.其次,应用程序一般有一个main 函数,从头到尾 执行一个任务;驱动程序却不同,它没有main 函数,通过使用宏module _init(初始化函数名),将初始化函数加入内核全局初始化函数列表中,在内核初始化时执行驱动的初始化函数,从而完成驱动的初始化和注册,之后驱动便停止等待被应用软件调用.应用程序可以和GLIB C 库连接,因此可以包含标准的头文件,比如等;在驱动程序中,不能使用标准C 库,因此不能调用所有的C 库函数,比如输出打印函数只能使用内核的printk 函数,包含的头文件只能是内核的头文件,比如. 2)Linux 系统设备文件 为了方便应用程序的开发,在Linux 操作系统中,使用了设备文件这一概念来管理硬件设备.Linux 操 第24卷 第4期 2005年12月南昌工程学院学报Journal of Nanchang Institute of Technology Vol.24No.4Dec.2005

Linux下的一个使用中断的按键驱动程序v

Linux下的一个使用中断的按键驱动程序(S3C2440)#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /*timer*/ #define BUTTON_IRQ IRQ_EINT8 //申请的中断号 #define DEVICE_NAME "button" //设备名字 #define BUTTONMINOR 0

#define DELAY (HZ/100) //延时100MS DECLARE_WAIT_QUEUE_HEAD(wq); //申请一个wq等待队列并初始化, //wait_queue_heat_t wq; //也可用两句来替换(申请初始化一个等待队列)//int_waitqueue_heat(&wq); static unsigned char key_down_cnt=0; //中断计数 static int ev_press=0; //唤醒等待的条件 static int button_Major; struct timer_list timer; //定时器链表 void timer_function()//定时产生后的处理函数 { int down; if(down = !s3c2410_gpio_getpin(BUTTON_IRQ)) //如果按键按下执行以下程序{ key_down_cnt++; //来一次,中断计数加一 ev_press = 1; //表示中断发生了 wake_up_interruptible(&wq); } //唤醒休眠的进程 return 0; } static irqreturn_t button_irq_ser(int irq,void *dev_id,struct pt_regs *regs) //中断服务程序 { //初始化定时器链表 init_timer(&timer); timer.data=0; timer.expires=jiffies+DELAY;

LINUX下安装无线网卡驱动程序

1.1下载驱动程序 一、首先弄清楚当前使用的是什么版本的Linux,如:Redhat9.0、Freda core5等。Linux用的是什么版本的内核,可以用uname –a得知内核版本号。 二、使用的是什么接口类型的无线网卡,常见的接口类型有PCI (台式机)、Cardbus/PCMCIA(笔记本)、USB(台式机、笔 记本)等。 三、无线网卡使用的是哪一种类型的芯片,判断方法如下,到相 关网站下载与其相对应的驱动程序。常用的无线网卡驱动及其支 持的芯片类型和接口类型如下表1所示。 PCMCIA卡:将无线网卡插入笔记本电脑中,用命令lspci可以看到芯片类型,如: Realtek Semiconductor Co.. Ltd, RTL8180L802.11b MAC (rev 20)

其中RTL8180L即为网卡芯片类型,用参数v可以看到更详细的信 息。 USB卡:用命令lsusb可以看到芯片类型(fc5没有此命令)。通过其它方法查找。 驱动程序芯片类型接口类型官方网站下载页 Linux-wlan-ng Prism2/2.5/3 Cardbus/PCI/USB https://www.sodocs.net/doc/c12716549.html,/download.shtml Madwifi Atheros系列Cardbus/PCI https://www.sodocs.net/doc/c12716549.html,/ islsm ISL38XX USB/PCI http://jbnote.free.fr/prism54usb/index.html 表1:网卡驱动程序有关信息 1.2驱动程序的安装 每款驱动的安装方法各不相同,阅读INSTALL文档,获得驱动程 序的具体安装方法及系统内核要求。阅读驱动程序附带的 README文档,获得有关此驱动的详细信息,如驱动所支持的网 络模式、接口类型、所包含的模块的功能及无线网卡的配置等。 下面是安装网卡驱动程序的一般步骤: 编译网卡驱动程序 #make

linux驱动程序进入内核

ARM-uClinux下编写加载驱动程序详细过程 本文主要介绍在uClinux下,通过加载模块的方式调试IO控制蜂鸣器的驱动程序。实验过程与上篇文章所讲的过程基本相似,更多注重细节及注意事项。 本文适合学习ARM—Linux的初学者。 //================================================================== 硬件平台:MagicARM2200教学试验开发平台(LPC2290) Linux version 2.4.24,gcc version 2.95.3 电路连接:P0.7——蜂鸣器,低电平发声。 实验条件:uClinux内核已经下载到开发板上,能够正常运行;与宿主机相连的网络、串口连接正常。 //================================================================== 编写蜂鸣器的驱动程序相对来说容易实现,不需要处理中断等繁琐的过程,本文以蜂鸣器的驱动程序为例,详细说明模块化驱动程序设计的主要过程和注意事项。 一、编写驱动程序 驱动程序的编写与上文所说的编写过程基本相同,这里再详细说明一下。 //========================================== //蜂鸣器驱动程序:beep.c文件 //------------------------------------------------------------------- #include /*模块相关*/ #include /*内核相关*/ #include /*linux定义类型*/ #include /*文件系统 file_opertions 结构体定义*/ #include /*出错信息*/ /*PINSEL0 注意:低2位是UART0复用口,不要改动*/ #define PINSEL0 (*((volatile unsigned*) 0xE002C000)) /*P0口控制寄存器*/ #define IO0PIN (*((volatile unsigned*) 0xE0028000))

嵌入式LINUX四按键驱动

对一个具有四个按键的按键驱动的分析 源代码: /*Headers-------------------------------------------------*/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef CONFIG_DEVFS_FS #include #endif /*V ars----------------------------------------------------*/ #define DEVICE_NAME "buttons" #define EXTINT_OFF (IRQ_EINT4 - 4) unsigned int buttons_major=0; unsigned int buttons_minor=0; unsigned int type = IRQT_FALLING; struct button_info { unsigned int irq_no; unsigned int gpio_port; unsigned int IN; int button_no; }; struct button_info realarm_button_info[4] = { { IRQ_EINT19, S3C2410_GPG11, S3C2410_GPG11_INP, 1 }, { IRQ_EINT8, S3C2410_GPG0, S3C2410_GPG0_INP, 2 },

相关主题