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

加入VIP,免费下载
 

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

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

下载须知

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

版权提示 | 免责声明

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

内核与设备驱动编程 三.docx

1、内核与设备驱动编程 三 内核与设备驱动编程 091180083 刘浩 通信工程一、 实验目的1.学习Linux操作系统下内核程序的编写和应用2.学习可编程接口芯片8253的编程控制方法3.学习异步串行接口(UART 16550)驱动的编写,实现双机通信二、 实验原理简介1.内核模块Liux内核提供以下功能,进程管理、内存管理、文件系统、设备控制和网络功能。Linux内核提供的这些功能可以在系统运行时利用可加载模块来进行扩展,功能的模块化使Linux内核更加精简,灵活和方便。设备驱动程序是内核模块的一个重要部分,此外很多其他功能,如文件系统也可实现模块化。一个内核模块的入口函数(在模块加载ins

2、mod时被调用):缺省函数原型为int init_module(),可以用module_init(函数名)来声明从而更改函数名。这个入口函数的作用是为以后调用模块函数做准备。模块的第二个入口函数(在模块卸载rmmod时被调用)是:缺省函数原型void cleanup_module(),同样可以用module_exit(函数名)来声明从而改变函数名,这个函数的作用是告诉内核,模块要离开了,不能再提供什么功能了。其中这些声明包含在头文件。这里简单说明一下内核模块和应用程序之间的区别,应用程序的入口函数是main()函数,从头到尾执行一个单个任务,而内核模块是先注册自己然后服务于以后的每个请求,这在

3、后面的设备驱动程序中表现得相当明显。此外应用程序和内核模块在函数调用时也有很大区别,应用程序可以链接到外部,而模块只能链接到内核,只能调用由内核导出的那些函数。例如,应用程序可以调用定义在libc中的printf,而模块不能,模块使用printk,是内核定义的函数。另外,一个很重要的区别是内核模块运行在内核空间,而应用程序运行在用户空间。2.设备文件及设备驱动Linux中设备可分为三类:字符设备,块设备和网络设备。字符设备和块设备都可以通过文件节点的方式被访问,设备以文件形式表示后,可以像普通文件一样来open,close,read,write。在/dev目录下可以查看系统中已有的一些设备。用

4、ls l命令可以查看字符设备用c表示,块设备用b表示,例如:crw-rw-rw- 1 root root 1, 3 Feb 23 1999 null。其中设备文件名是null,它前面的是设备文件建立时间,再前面的两个数字表示的设备号,1是主设备号,3是次设备号。驱动程序对应的是主设备号,主设备号相同的设备由同一个驱动程序来驱动。此设备号用来区分不同的设备,当一个设备驱动程序驱动好几个设备时,次设备号就发挥作用了。在驱动程序初始化中,通过注册设备来分配主设备号。关于字符设备的注册和注销。注册:int regiser_chrdev(unsigned int major,const char* na

5、me,struct file_operations *fops)。对这个函数进行说明。函数的第二个参数是函数名,第三个参数是与文件相关的操作,这个指针指向对这个设备进行操作的一组函数,是file数据结构中的一个很重要的结构体。第一个参数若不为0,则为设备的主设备号,此时若设备注册成功,函数返回值为0;注册失败,函数返回一个负值。若第一个参数major是0,则为动态分配主设备号,当正确注册时,返回的是动态分配的设备号,若注册失败,返回一个负数。当驱动程序注册到内核表中,它的操作就与指定的主设备号对应起来,当我们对一个主设备号与之相对应的设备文件进行操作时,内核将从fop中找到并调用正确的函数,进

6、行操作。注销:任何被注册的设备在模块卸载时都必须释放主设备号,对设备进行注销的函数:int unregister_chrdev(unsigned int major,const char*name)。关于建立文件节点。设备驱动程序为用户程序屏蔽掉了底层的硬件信息,用户程序对字符设备的操作就像操作普通的文件一样。要对设备进行操作,需要建立设备文件节点,当用户程序操作设备节点时(如打开,关闭,读写),得到设备文件的主设备号,进而找到对应的驱动程序,进而对设备进行fop中对应的各种操作。所以设备文件节点是联系用户程序和设备驱动程序的纽带。建立设备文件节点用以下命令:mknod /dev/name c

7、 major minor m 666。其中name表示设备名,c表示建立的是字符设备文件,major是主设备号,minor是次设备号,-m 666表示建立文件的读写权限为可读可写文件。在卸载设备驱动程序时有的时候需要删除设备文件:rm /dev/name。3.文件操作打开的设备在内核中以file结构标识,内核使用file_operations结构访问驱动程序中的函数。关于file_operations结构体中的函数,以及file结构的说明见课本P246P250。4.可编程接口芯片8253介绍一片8253可编程定时/计数器中有三个完全独立的减法计数器,每个计数器有六种不同的工作方式。具体原理见课

8、本P123126。这里主要介绍一下它的工作方式二和工作方式三,这两种工作方式是不需要硬件触发就能产生连续波形的方式,方式二成为频率发生器,方式三称为方波发生器。方式二只有当计数减到1时输出一个时钟周期的低电平。而方式三是减二计数,当减到0时输出状态翻转,从而使输出波形接近方波。在8253控制扬声器驱动程序中我们采用的是8253的工作方式三而不是工作方式二,也是因为这个区别,工作方式二短暂的低电平,这样会使得驱动的功率太小,扬声器发出声音太小而影响实验效果。早期的个人计算机中,有一片8253,作为系统的硬件时钟设备,占用端口为40H43H。输入时钟为1.19MHz。T/C0承担系统日时钟,输出接

9、8259的IR0,作为系统的计时中断信号。T/C1承担动态存储器的时钟刷新,T/C2控制着系统的扬声器,产生声音信号。它与扬声器的硬件接口电路如下:要使扬声器发声,8255的PB0 和PB1必须为高电平,初始化后B口为输出方式,用于一些控制信号的输出。8255的分配端口为60H63H。B口的地址为61H,8255控制口的地址为63H。对8255B口写03H,用汇编语言:outb_p(0x03,0x61)。写8253的控制字,outb_put(0xb6,0x43)。0xb6转换为二进制数为10110110,根据下面的控制字格式我们知道,它表示:选择计数器2、先读写低字节,后读写高字节、选用方式三

10、、采用2进制计数。给计数器赋初值。由于计数器的时钟频率是1.19MH z,设给计数器赋值为x,计数器是减法计数的,则输出信号的频率为(1.19/x) MHz。所以通过给8253的初值寄存器赋不同的值就可以得到不同频率的输出信号。本实验中要求编写8253控制扬声器发出音乐,通过计算转换可以得到下面的音调与频率及计数初值之间的对应表格。音符1234567低音频率131147165175196220247数据0x237B0x1F9F0x1C2C0x1A900x17B70x15210x12D1中音频率262294330349392440494数据0x11BD0x0FCF0x0E160x0D510x0B

11、DB0x0A900x0968高音频率523587659698784880987数据0x08E30x07E80x070D0x06A80x05ED0x05480x04B55. 异步串行接口(UART)介绍UART是一种非常古老但是却一直保存在现有计算机系统中的接口,它可以把处理器对数据的并行处理转换成为串行的数据加以传输,这种接口非常简单但是依然特别常用。串行卡从8250到16450,16550不断发展,串行通信性能不断提高。目前个人计算机中不再有独立的UART芯片,同8253等一样被集成了,但操作方式没有什么改变。标准配置下,个人计算机的串行端口的基地址为3F8H,2F8H,3E8H,对串口的编

12、程兼容16550标准。以下是16550的寄存器地址。下面介绍通信线路控制寄存器LCR的作用:DLABSBRKSPBEPSPENSTBWLS1WLS0DLAB:寻址位,为0时正常工作,为1时设置波特率;D6位:中止位设置。D5是奇偶校验位附加位,为0时无效。D4D3=01时,为奇校验,为11时,是偶校验。D1和D0位设定字符的长度,00表示5位,01表示6位,10表示7位,11表示8位。还有其他很多寄存器,在课本p117页有具体详细的介绍。这里不再说明。计算机系统中的串口芯片16550通常和RS232C标准的9/25阵串口连接器连接。下面将给出其接口:下面介绍RS232C 25针的几个重要的管脚

13、:20是DTR数据终端准备好信号;4是RTS请求发送信号;8是载波检测;6是数据通信设备准备好;5是容许发送或清除信号;2是发送数据端;3是接收数据端。三、 实验内容,问题及分析解决1. 简单内核模块的编写利用课本上的“hello,world”模块程序代码,实现编译模块,加载模块卸载模块整个过程。程序代码: #define MODULE#define _KERNEL_ /说明:这两个预定义有的时候是多余的,在2.6 的内核中通常会警告出被重定义了。#include /编写内核模块必须添加的头文件 MODULE_LICENSE(“GPL”);/ 这个宏用来告诉内核该模块采用的是自由许可证,没有这

14、个宏时没和在加载模块的时候会抱怨如:hello:module license “unspecified”taints kernel。所以建议添上这个宏。int init_module(void) /模块的入口函数 printk(”hello,worldn”);/注意表示的是消息的优先级,printk根据这 优先级的所表示的严重程度对消息进行分类。可 以采用宏来表示优先级。在下面将具体介绍。 return 0; void cleanup_module(void) /模块的退出函数printk(”goodbye,worldn”); printk的八种消息级别。KERN_EMERG(紧急事件消息,用

15、于系统崩溃之前的提示消息),KERN_ALTER(用于需要立即采取动作的行动),KERN_CRIT(临界状态,通常面临严重的硬件或者软件操作失败),KERN_ERR(用于报告错误状态),KERN_WARNING(用于报告警告信息),KERN_NOTICE(有必要进行提示的正常信息),KERN_INFO(提示性信息,很多驱动程序用来打印他们找到的硬件信息),KERN_DEBUG(用于调试信息)。这八种级别用尖括号中的整数07来表示,整数越小,优先级别越高。在编写驱动程序时,通常需要通过printk打印信息来对驱动程序进行调试。实验中我一直希望能在终端中打印出提示信息,可是没有成功,通过查阅资料,

16、知道信息有优先级的分别,终端也有缺省的优先级设置,只有当printk中的优先级比终端的设置要高时才能打印出来。后来查看/var/log/messages看到了加载和卸载时打印的消息。此外通过dmesg也可以查看信息。关于模块的编译。2.6内核模块的编译和2.6内核模块的编译有很大的区别。在2.6的内核版本中,使用下面的Makefile就会很容易的编译内核模块。Ifneq($(KERNELRELEASE),)/如果已经定义了KERNELRELEASE,则说明是从内核构造系统调用的。obj_m :=hello.o /上面的赋值语句说明一个模块从目 标文件hello.o中构造,得到的目标模块名称为h

17、ello.ko,如果要构建的模块名称为module.ko,并且由两个源文件生成(设为file1.c,file2.c)则这一行应该写为:obj_m:=module.ko 和 module-objs:=file1.o file2.o这两行。这对于我们来说感觉很疑惑,怎么这一行就能完成整个编译过程呢。经阅读课外资料,得知这是利用GNU make的扩展语法,其余的问题都是由内核构造系统来完成的。else /若从命令行调用,这时要调用内核构造系统KERNELDIR=:/lib/modules/$(shell uname -r)/build /内核构造系统的,目录,其中$(shell uname -r)是

18、获取当前的内核版本PWD :=$(shell pwd) /当前目录default: $(MAKE) C $(KERNELDIR) M=$(PWD)modules /两次运行make命令, 第一次从进入内核构造系统,第二次进入当前路径编译。endif 编译及运行过程:将内核模块源代码hello.c和Makefile放到同一个目录下,然后进入这个目录,执行make,可以看到make的过程。生成了hello.ko文件,这就是编译好的内核模块。执行下面命令进行内核加载:/sbin/insmod hello.ko。加载成功后,用lsmod可以看到hello这个模块及其使用计数。也可以在/proc/mod

19、ules中看到hello这个模块。用dmesg可以看到加载时打印出来的hello,world。这些都说明模块已经正确加载。当卸载模块时,用命令:/sbin/rmmod hello.ko。lsmod及cat /proc/modules 都看不到模块了,用dmesg可以看到卸载时打印的信息:goodbye,world。说明模块已经被成功卸载。在这里说明一下,insmod和rmmod都市超级用户权限的命令,在个人电脑上,如果不是以root用户登录的话,需要用命令sudo insmod hello.ko。而在实验室电脑上,由于已经被赋予这个权限,所以不需要sudo。但insmod命令是在sbin目录下

20、。这点需要注意一下。 到这里,关于模块的编写流程,编译过程,加载及卸载过程已经了解得很清楚了。这为下面编写设备驱动程序打下了坚实的基础。2. 简单的读写字符设备驱动程序的编写及测试在这个过程中将建立一个虚拟的字符设备,编写驱动程序,实现用户空间和内核空间的数据交换。在这个程序中要学习如何编写file_operations结构中的函数,如open,release,write,read及ioctl等。目前我们暂时掌握这5个函数的编写。以后有机会还应该关注这个结构体中其他函数的编写,如llseek,poll(poll和select的系统调用,是并行执行的很重要的系统调用),mmap(将设备内存空间映

21、射到进程空间的系统调用)等这些函数对于驱动程序功能的扩展有很大作用。下面介绍前面几个函数的编写。本实验中的open方法:这个实验编得很简单,open函数只是递增了使用计数,防止两个文件同时被打开。int devicevalue = 0; /自定义的计数变量int dev_open(struct inode *inode, struct file *file) if (devicevalue) return -EBUSY; devicevalue+; return 0; 说明:open函数一般需要完成更多的功能:如检查设备特定的错误,如果设备是第一次打开,则对其进行初始化。如果驱动程序驱动的设备

22、不止一种,不止一个,则需要识别次设备号,更新fop指针。分配并填写file-private_data中的数据结构。这些功能在课本上提供的SKULL驱动的open中有很好的体现。 关闭release方法:本实验中的release只是递减使用计数。一般release 方法还应该完成的工作是:释放由open分配的在file-private_data中的空间。关闭设备。int dev_close(struct inode *inode, struct file *file) devicevalue-; return 0;关于read和write方法:主要是用户空间和内核空间传递数据。ssize_t d

23、ev_read(struct file *file, char *buf, size_t count, loff_t *offset) char *message=“hello,world0”; copy_to_user(buf ,message,count); return count; /从内核空间读取数据,向用户空间写数据,ssize_t dev_write(struct file *file, char *buf, size_t count, loff_t *offset) char *message; copy_from_user( message,buf,count); retur

24、n count; /从用户空间读数据,写进内核空间说明:这里的读写都是相对内核空间而言的。函数参数中的buf指的是用户空间中的buf,是由用户程序系统调用read(fd,buf,count)或这write(fd,buf,count)中的buf传递过来的参数,count也对应于用户程序中的count。而file指针也和用户程序中的文件描述符fd相对应。struct file_operations fops= .open =dev_open, /也可以用open:dev_open, .release= dev_close, .read = dev_read, .write = dev_write,

25、;说明:这个声明采用的是标准C的标记化结构初始化语句。这种语法使得驱动程序在结构的定义发生变化时更具可移植性,并且使得代码紧凑易读。这个结构体的作用是:当用户空间执行某种操作时,会由内核找到这个数据结构fops,进而找到相应的系统调用,从而调用驱动程序中编写的对应函数。程序的最后是编写驱动的入口和退出函数,主要的作用是注册设备和注销设备,此外还有可能需要分配内存和释放内存空间。int dev_init(void) int ret; ret = register_chrdev(MAJOR_NUM, name, &fops); return 0;void dev_exit(void) unregi

26、ster_chrdev(MAJOR_NUM, name);module_init (dev_init);module_exit (dev_exit);MODULE_LICENSE(GPL);到这里一个较为简单的字符设备驱动程序已经编写成功。然后就是编写简单的应用程序来测试一下这个驱动程序。这里应用程序略去了。其中有很简单的函数调用:fd=open(“/dev/name”,O_RDWR);read(fd,buf,4);write(fd,string,4);整个加载编译测试的流程。像模块的操作一样先编译加载模块。创建设备节点 mknod rdwr c 134 77 -m 666。这里需要注意的是,

27、这里的主设备号和设备名称必须和驱动程序中定义的一致。然后编译应用程序。最后执行。结果可以实现用户空间和内核空间之间的数据交换。设备节点创建后可以用cat /proc/devices命令来查看这个设备的使用情况。3编写8253内核模块直接操作在扬声器相关端口。 #define MODULE#define _KERNEL_#include #include #include /其中包含outb_p,inb_p等函数int freq;MODULE_PARM(freq, i);/用insmod改变模块参数之前必须先对这些参数进行声明。上面的宏中带有两个参数,一个是变量名,一个是描述变量类型的字符串。目

28、前模块参数支持五种类型。b一个字节,h短整形,i整形,l长整型,s字符串。int init_module() int div,divh,divl; div = 1193182 / freq; divh = div / 256; /初值的高字节 divl = div - divh * 256; /初值的低字节 outb_p(0xb6, 0x43); /写8253命令字 outb_p(divl, 0x42); /先送低字节 outb_p(divh, 0x42); /后送高字节 outb_p(0x03, 0x61); /打开8255的控制信号,开始计数return 0;void cleanup_mo

29、dule()outb_p(0,0x61); /关闭8255的控制信号,停止计数,扬声器停止发声MODULE_LICENSE(GPL);MODULE_DESCRIPTION(Insert module by insmod 8253.o freq=); /宏模块使用时的说明 说明:这个模块的加载过程跟第一个模块有点区别,因为加载过程需要设置模块的参数。/sbin/insmod spk.ko freq=300时,听到声音。/sbin/rmmod spk.ko声音停止。改变加载时传递的频率大小,可以发现声音的音调不一样。 4. 编写8253设备驱动程序和应用程序,实现音乐播放功能上面已经通过直接编写8

30、253的内核模块实现了扬声器的发声。在这个过程中将继续编写设备驱动程序来控制扬声器。设备驱动是一种更好的方式,可以供应用程序来系统调用,结合应用程序可以实现更加丰富的功能。这里只介绍release和write函数,因为其他函数跟前面所编写的程序是差不多的。static int speaker_release(struct inode *inode, struct file *file)/释放设备 Device_Open -; outb_p(0,0x61);/关闭扬声器 static ssize_t speaker_write(struct file *file,const char *buffer,size_t length,loff_t *offset) char *str; copy_from_user(str,buffer,2); outb_p(0xb6,0x43);/1011,0110 初始化 outb_p(str0,0x42);/写入计数初值 outb_p(str1,0x42); outb_p(3,0x61);/打开扬声器 return 0; 用户程序的编写:这里只是介绍其中比较重要的部分。要实现音乐播放,首先要清楚音乐的几个因素:一是音调,也就是对应于频率,二是音阶,这对应于响音的长度,三是响度。在这个实验中,我们能控

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

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