ImageVerifierCode 换一换
格式:DOCX , 页数:11 ,大小:23.66KB ,
资源ID:5473499      下载积分:3 金币
快捷下载
登录下载
邮箱/手机:
温馨提示:
快捷下载时,用户名和密码都是您填写的邮箱或者手机号,方便查询和重复下载(系统自动生成)。 如填写123,账号就是123,密码也是123。
特别说明:
请自助下载,系统不会自动发送文件的哦; 如果您已付费,想二次下载,请登录后访问:我的下载记录
支付方式: 支付宝    微信支付   
验证码:   换一换

加入VIP,免费下载
 

温馨提示:由于个人手机设置不同,如果发现不能下载,请复制以下地址【https://www.bdocx.com/down/5473499.html】到电脑端继续下载(重复下载不扣费)。

已注册用户请登录:
账号:
密码:
验证码:   换一换
  忘记密码?
三方登录: 微信登录   QQ登录  

下载须知

1: 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。
2: 试题试卷类文档,如果标题没有明确说明有答案则都视为没有答案,请知晓。
3: 文件的所有权益归上传用户所有。
4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
5. 本站仅提供交流平台,并不能对任何下载内容负责。
6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

版权提示 | 免责声明

本文(手把手要教你编写Linux设备驱动程序.docx)为本站会员(b****3)主动上传,冰豆网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。 若此文所含内容侵犯了您的版权或隐私,请立即通知冰豆网(发送邮件至service@bdocx.com或直接QQ联系客服),我们立即给予删除!

手把手要教你编写Linux设备驱动程序.docx

1、手把手要教你编写Linux设备驱动程序如何编写Linux设备驱动程序Linux是Unix操作系统的一种变种,在Linux下编写驱动程序的原理和思想完全类似于其他的Unix系统,但它dos或window环境下的驱动程序有很大的区别。在Linux环境下设计驱动程序,思想简洁,操作方便,功能也很强大,但是支持函数少,只能依赖kernel中的函数,有些常用的操作要自己来编写,而且调试也不方便。以下的一些文字主要来源于khg,johnsonm的Write linux device driver,Brennans Guide to Inline Assembly,The Linux A-Z,还有清华BBS

2、上的有关device driver的一些资料。一、Linux device driver 的概念 系统调用是操作系统内核和应用程序之间的接口,设备驱动程序是操作系统内核和机器硬件之间的接口。设备驱动程序为应用程序屏蔽了硬件的细节,这样在应用程序看来,硬件设备只是一个设备文件,应用程序可以象操作普通文件一样对硬件设备进行操作。设备驱动程序是内核的一部分,它完成以下的功能: 1。对设备初始化和释放。2。把数据从内核传送到硬件和从硬件读取数据。3。读取应用程序传送给设备文件的数据和回送应用程序请求的数据。4。检测和处理设备出现的错误。在Linux操作系统下有三类主要的设备文件类型,一是字符设备,二是

3、块设备,三是网络设备。字符设备和块设备的主要区别是:在对字符设备发出读/写请求时,实际的硬件I/O一般就紧接着发生了,块设备则不然,它利用一块系统内存作缓冲区,当用户进程对设备请求能满足用户的要求,就返回请求的数据,如果不能,就调用请求函数来进行实际的I/O操作。块设备是主要针对磁盘等慢速设备设计的,以免耗费过多的CPU时间来等待。已经提到,用户进程是通过设备文件来与实际的硬件打交道。每个设备文件都都有其文件属性(c/b),表示是字符设备还是块设备?另外每个文件都有两个设备号,第一个是主设备号,标识驱动程序,第二个是从设备号,标识使用同一个设备驱动程序的不同的硬件设备,比如有两个软盘,就可以用

4、从设备号来区分他们。设备文件的的主设备号必须与设备驱动程序在登记时申请的主设备号一致,否则用户进程将无法访问到驱动程序。 最后必须提到的是,在用户进程调用驱动程序时,系统进入核心态,这时不再是抢先式调度。也就是说,系统必须在你的驱动程序的子函数返回后才能进行其他的工作。如果你的驱动程序陷入死循环,不幸的是你只有重新启动机器了,然后就是漫长的fsck。读/写时,它首先察看缓冲区的内容,如果缓冲区的数据 如何编写Linux操作系统下的设备驱动程序 二、实例剖析 我们来写一个最简单的字符设备(比如蜂鸣器)驱动程序。虽然它什么也不做,但是通过它可以了解Linux的设备驱动程序的工作原理。把下面的C代码

5、输入机器,你就会获得一个真正的设备驱动程序。#define _NO_VERSION_ #include #include char kernel_version = UTS_RELEASE; 这一段定义了一些版本信息,虽然用处不是很大,但也必不可少。Johnsonm说所有的驱动程序的开头都要包含,一般来讲最好使用。 由于用户进程是通过设备文件同硬件打交道,对设备文件的操作方式不外乎就是一些系统调用,如 open,read,write,close, 注意,不是fopen, fread,但是如何把系统调用和驱动程序关联起来呢?这需要了解一个非常关键的数据结构: struct file_operat

6、ions 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 *

7、, int ,select_table *); / I/O端口复用,非阻塞的状态下实现设备的访问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 *);

8、int (*fsync) (struct inode * ,struct file *); /设备的同步信息int (*fasync) (struct inode * ,struct file *,int); /异步int (*check_media_change) (struct inode * ,struct file *); /检测数据是否发生改变int (*revalidate) (dev_t dev); /使设备重新有效 这个结构的每一个成员的名字都对应着一个系统调用。用户进程利用系统调用在对设备文件进行诸如read/write操作时,系统调用通过设备文件的主设备号找到相应的设备驱动

9、程序,然后读取这个数据结构相应的函数指针,接着把控制权交给该函数。这是linux的设备驱动程序工作的基本原理。既然是这样,则编写设备驱动程序的主要工作就是编写子函数,并填充file_operations的各个域。 下面就开始写子程序。 #include #include /文件系统#include /内存管理#include #include /汇编语言编写unsigned int test_major = 0; /主设备号,自动搜索static int read_test(struct inode *node,struct file *file,char *buf,int count)/读测

10、试,inode *node表示哪一个设备,*file表示文件描述符,*buf表读取时的接口,count表期望读的字节数,static int 返回值实际读到的数量 int left; if (verify_area(VERIFY_WRITE,buf,count) = -EFAULT ) / verify_area,验证某一个buf中的数据是否有效return -EFAULT; for(left = count ; left 0 ; left-) _put_user(1,buf,1); /从内核空间将数据拷贝到用户空间去,将”1”依次放到用户空间的buf中,每次放的大小是一个字节 buf+; r

11、eturn count; /返回读的数据数 这个函数是为read调用准备的。当调用read时,read_test()被调用,它把用户的缓冲区全部写1。buf 是read调用的一个参数。它是用户进程空间的一个地址。但是在read_test被调用时,系统进入核心态。所以不能使用buf这个地址,必须用_put_user(),这是kernel提供的一个函数,用于向用户传送数据。另外还有很多类似功能的函数。请参考,在向用户空间拷贝数据之前,必须验证buf是否可用。这就用到函数verify_area。static int write_tibet(struct inode *inode,struct fil

12、e *file,const char *buf,int count) return count; / *inode表入口节点,*file表设备描述符static int open_tibet(struct inode *inode,struct file *file ) MOD_INC_USE_COUNT; /宏定义,注册一个驱动之后,模块数自动+1return 0; static void release_tibet(struct inode *inode,struct file *file ) MOD_DEC_USE_COUNT; / 模块数自动减1 这几个函数都是空操作。实际调用发生时什

13、么也不做,他们仅仅为下面的结构提供函数指针。 struct file_operations test_fops = NULL, read_test, write_test, NULL, /* test_readdir */ NULL, NULL, /* test_ioctl */ NULL, /* test_mmap */ open_test, release_test, NULL, /* test_fsync */ NULL, /* test_fasync */ /* nothing more, fill with NULLs */ ; 设备驱动程序的主体可以说是写好了。现在要把驱动程序嵌入

14、内核。驱动程序可以按照两种方式编译。一种是编译进kernel,另一种是编译成模块(modules),如果编译进内核的话,会增加内核的大小,还要改动内核的源文件,而且不能动态的卸载,不利于调试,所以推荐使用模块方式。 int init_module(void) /注册方式 int result; result = register_chrdev(0, test, &test_fops); /注册字符型设备到内核中去,“0”表是自动根据设备节点里的主设备号来获取它的设备号;test表示你注册的设备名;&test_fops为注册的接口if (result 0) printk(KERN_INFO te

15、st: cant get major numbern); /在内核打印信息必须用printk;而printf只能在用户空间用return result; if (test_major = 0) test_major = result; /* dynamic */ /由内核分配一个设备号给驱动程序,获取设备的主设备号return 0; 在用insmod命令将编译好的模块调入内存时,init_module 函数被调用。在这里,init_module只做了一件事,就是向系统的字符设备表登记了一个字符设备。register_chrdev需要三个参数,参数一是希望获得的设备号,如果是零的话,系统将选择一

16、个没有被占用的设备号返回。参数二是设备文件名,参数三用来登记驱动程序实际执行操作的函数的指针。 如果登记成功,返回设备的主设备号,不成功,返回一个负值。 void cleanup_module(void) unregister_chrdev(test_major,test); 在用rmmod卸载模块时,cleanup_module函数被调用,它释放字符设备test在系统字符设备表中占有的表项。 一个极其简单的字符设备可以说写好了,文件名就叫test.c吧。 下面编译(模块方式) :$ gcc -O2 -DMODULE -D_KERNEL_ -c test.c /-O2表二级优化;-DMODUL

17、E表编译成模块;-D_KERNEL_表加载内核某一个块;2.6内核编译出来是test.ko文件得到文件test.o就是一个设备驱动程序。 如果设备驱动程序有多个文件,把每个文件按上面的命令行编译,然后 ld -r file1.o file2.o -o modulename。 /链接文件,生成相应的模块名驱动程序已经编译好了,现在把它安装到系统中去。 $ insmod f test.o /强制加载到内核中去如果安装成功,在/proc/devices文件中就可以看到设备test,并可以看到它的主设备号。要卸载的话,运行 :$ rmmod test 下一步要创建设备文件。 mknod /dev/te

18、st c major minor c 是指字符设备,major是主设备号,就是在/proc/devices里看到的。 用shell命令 $ cat /proc/devices 就可以获得主设备号,可以把上面的命令行加入你的shell script中去。 minor是从设备号,设置成0就可以了。 我们现在可以通过设备文件来访问我们的驱动程序。写一个小小的测试程序。 #include #include #include #include main() int testdev; /返回的设备描述符int i; char buf10; testdev = open(/dev/test,O_RDWR);

19、 /打开设备并获取设备描述符if ( testdev = -1 ) printf(Cannt open file n); exit(0); read(testdev,buf,10); for (i = 0; i Linux内核的入口点/ linux-2.4.x/arch/armnommu/boot/compresed/head.S(整个Linux内核的入口点,是用汇编语言编写的,open with gedit) linux-2.4.x/init/main.c (整个程序的入口点,Source Insight 研读) 在开发板上运行内核程序(main.c)运行SecureCRT软件,通过COM1

20、端口连接开发板:move 1000(在Flash中内核存放的位置)c008000(SDRAM位置)100000(大小)run c008000/etc/inittab /x86配置文件cd /etclscat rc /44B0配置文件 蜂鸣器的驱动第一步:编写蜂鸣器驱动程序testdriver.c,并将该驱动文件放在/home/s123/ uclinux/ uClinux-dist/ linux-2.4.x/drivers/char/ testdriver.c第二步:修改makefile文件,该文件在/home/s123/ uclinux/ uClinux-dist/linux-2.4.x/dr

21、ivers/char/添加驱动到makefile中:obj-$(CONFIG_TESTDRIVER)+=testdriver.o 第三步:加载配置文件到config.in中 bool mytestdriver CONFIG_TESTDRIVER第四步:将test_init(void)函数加载到char/mem.c文件中 声明:#ifdef CONFIG_TESTDRIVER extern int test_init(void) #endif 调用:#ifdef CONFIG_TESTDRIVER test_init(); #endif第五步:在/home/s123/ uclinux/ uCli

22、nux-dist/vendors/Samsung/44B0/makefile创建设备节点 test(设备名),c(设备类型),254(主设备号),0(从设备号)第六步:启动Linux进入命令行状态:cd /home/s123/cd uclinux/cd uClinux-dist/ /到uclinux顶层目录make menuconfig选中“Customize kernel Settings” /退出并保存,只配置内核进入Character devices :选中“mytestdriver(NEW)” /直接编译进内核,退出保存make dep /创建依赖文件make cleanmake lib_only /编译生成的库make user_only make romfsmake image /生成镜像文件make /生成最后的镜像文件启动SecureCRT工具软件:Connect选择com1端口,进入超级

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1