扬州城市论坛 百科 linux编写简单的驱动程序(Linux设备驱动程序)

linux编写简单的驱动程序(Linux设备驱动程序)

前言

Linux是Unix操作系统的变体。Linux下写驱动的原理和思路和其他Unix系统完全类似,但是dos或window环境下的驱动就大不相同了。在Linux环境下设计驱动程序简洁、易操作、功能强大,但支持的函数很少,只能依赖内核中的函数。一些常见的操作还得自己写,调试不方便。在过去的几周里,我为我们实验室开发的一个多媒体卡编写了一个驱动程序,获得了一些经验,想和Linux爱好者分享一下。如有不当之处,请指正。

下面的话主要来自khg,johnsonm的写linux设备驱动,Brennan的内联汇编指南,Linux A-Z,还有清华BBS上一些关于设备驱动的资料。这些材料有些过时,有些有错误,我根据自己的实验结果进行了修正。

1.Linux设备驱动程序的概念

系统调用是操作系统内核和应用程序之间的接口,设备驱动是操作系统内核和机器硬件之间的接口。设备驱动为应用屏蔽了硬件的细节,这样在应用看来,硬件设备只是一个设备文件,应用可以像普通文件一样操作硬件设备。设备驱动程序是内核的一部分,它执行以下功能:

1.初始化并释放设备。

2.从内核传输数据到硬件,从硬件读取数据。

3.将应用程序传输的数据读取到设备文件中,并将应用程序请求的数据发送回来。

4.检测和处理设备中的错误。

linux操作系统下的设备文件主要有两种类型,一种是字符设备,一种是块设备。字符设备和块设备的主要区别在于,当字符设备发送读/写请求时,实际的硬件I/O通常会立即发生,而块设备则不会。它使用一块系统内存作为缓冲区。当用户进程请求设备满足用户的要求时,它返回所请求的数据。如果没有,它调用请求函数来执行实际的I/O操作。Block主要是为磁盘等速度较慢的设备设计的,这样不会花费太多的CPU时间等待。

前面已经提到,用户进程通过设备文件处理实际的硬件。每个设备文件都有其文件属性(c/b ),该属性指示字符设备是强还是弱。此外,每个文件都有两个设备号。第一个是主设备号,用于标识驱动程序,第二个是从设备号,用于标识使用相同设备驱动程序的不同硬件设备。例如,如果有两张软盘,它们可以通过从属设备号来区分。文件的主设备号必须与设备驱动程序注册时申请的主设备号一致,否则用户进程将无法访问驱动程序。

最后必须提到的是,当用户进程调用驱动时,系统进入核心状态,不再是抢占式调度。也就是说,在你的驱动的子程序返回之前,系统不能做任何其他的工作。如果你的驱动陷入死循环,很不幸,你要重启机器,然后就是很长的fsck。//呵呵

当读/写时,它首先查看缓冲区的内容。如果缓冲器中的数据

如何在Linux操作系统下编写设备驱动程序

二、案例分析

让我们写一个最简单的字符设备驱动程序。虽然不做什么,但是可以帮助你理解Linux设备驱动的工作原理。把下面的C代码输入机器,你会得到一个真正的设备驱动。不过我的内核是2.0.34,在更低版本的内核上可能会有问题。我还没测试过。//西西

#define __NO_VERSION__

#包含linux/modules.h

#包含linux/version.h

char kernel _ version[]=UTS _发布;

这段定义了一些版本信息,不是很有用,但也是必不可少的。Johnsonm说所有的驱动一开始就应该包含linux/config.h,但我不这么认为。

由于用户进程通过设备文件处理硬件,设备文件的操作方式无非就是一些系统调用,比如open,read,write,close…,注意不是fopen,fread,而是如何将系统调用与驱动关联起来?这需要理解一个非常重要的数据结构:

结构文件_操作{

int (*seek) (struct inode *,struct file *,off_t,int);

int (*read) (struct inode *,struct file *,char,int);

int (*write) (struct inode *,struct file *,off_t,int);

int (*readdir) (struct inode *、struct file *、struct dirent *、int);

int (*select) (struct inode *,struct file *,int,select _ table *);

int (*ioctl) (struct inode *、struct file *、unsined int、unsigned long);

int (*mmap) (struct inode *、struct file *、struct VM _ area _ struct *);

int (*open) (struct inode *,struct file *);

int (*release) (struct inode *,struct file *);

int (*fsync)(结构inode *,结构文件*);

int (*fasync) (struct inode *,struct file *,int);

int(* check _ media _ change)(struct inode *,struct file *);

int(* revalidate)(dev _ t dev);

}

这个结构的每个成员的名字对应一个系统调用。当用户使用系统调用对设备文件进行读写操作时,系统调用通过设备文件的主设备号找到对应的设备驱动,然后读取数据结构对应的函数指针,再将控制权交给函数。这是linux设备驱动的基本原理。在这种情况下,写设备驱动的主要工作就是写子函数,填写file_operations的字段。

很简单,不是吗?

让我们开始写子程序。

#包含linux/types.h

#包含linux/fs.h

#包含linux/mm.h

#包含linux/errno.h

#包括装配/分段. h

unsigned int test _ major=0;

静态int read_test(struct inode *node,struct file *file,

char *buf,int count)

{

int left

if (verify_area(VERIFY_WRITE,buf,count)==-EFAULT)

返回-默认;

for(左=计数;左0;左-)

{

__put_user(1,buf,1);

buf

}

返回计数;

}

这个函数是为read调用准备的。当read被调用时,read_test()被调用,它写入所有用户的缓冲区。BUF是read调用的参数。它是用户进程空间的地址。但是当read_test被调用时,系统进入核心状态。所以地址buf不能用,必须用__put_user(),这是内核提供的一个向用户传输数据的函数。类似的功能还有很多其他功能。请参考。在将数据复制到用户空间之前,需要验证buf是否可用。

这使用了函数verify_area。

静态int write _ Tibet(struct inode * inode,struct file *file,

const char *buf,int count)

{

返回计数;

}

static int open _ Tibet(struct inode * inode,struct file *file)

{

MOD _ INC _ USE _ COUNT

返回0;

}

静态void release _ Tibet(struct inode * inode,struct file *file)

{

MOD _ DEC _ usecount;

}

这些函数都是空操作。当实际调用发生时,它们什么也不做,只是为下面的结构提供函数指针。

结构文件_操作测试_fops={

空,

read_test

写_测试,

NULL,/* test_readdir */

空,

NULL,/* test_ioctl */

NULL,/* test_mmap */

open_test,

release_test,NULL,/* test_fsync */

NULL,/* test_fasync */

/*仅此而已,用空值填充*/

};

设备驱动的主体可以说是写出来的。现在我们需要嵌入驱动程序。

本文来自网络,不代表本站立场,转载请注明出处:https://baike.yzcslt.com/n/a2441.html

linux编写简单的驱动程序(Linux设备驱动程序)

扬州城市论坛后续将为您提供丰富、全面的关于linux编写简单的驱动程序(Linux设备驱动程序)内容,让您第一时间了解到关于linux编写简单的驱动程序(Linux设备驱动程序)的热门信息。小编将持续从百度新闻、搜狗百科、微博热搜、知乎热门问答以及部分合作站点渠道收集和补充完善信息。