Linux下设备管理与驱动程序编写实例课件

上传人:vosvybf****vycfil... 文档编号:243138057 上传时间:2024-09-16 格式:PPT 页数:65 大小:462KB
返回 下载 相关 举报
Linux下设备管理与驱动程序编写实例课件_第1页
第1页 / 共65页
Linux下设备管理与驱动程序编写实例课件_第2页
第2页 / 共65页
Linux下设备管理与驱动程序编写实例课件_第3页
第3页 / 共65页
点击查看更多>>
资源描述
Click to edit Master title style,Click to edit Master text styles,Second level,Third level,Fourth level,Fifth level,*,*,单击此处编辑母版标题样式,*,*,单击此处编辑母版文本样式,第二级,第三级,第四级,第五级,第11章设备管理,Linux,和其他操作系统一样,支持众多的、各式各样的外接设备。,但是,面对层出不穷的新硬件产品,必须有人不断编写新的驱动程序,以便让这些设备能够在 Linux 下正常工作,从这个意义上讲,讲述驱动程序的编写就是一件非常有意义的工作。,本章也涉及到Linux下设备管理的原则和方法。,举例来说,Linux下的驱动程序仅仅是为相应的设备编写几个基本函数,并向,VFS,注册就可以安装成功了。,当应用程序需要设备时,可以访问该设备对应的文件节点,利用VFS调用该设备的相关处理函数。,本章主要介绍了设备管理方面的有关知识:, 系统管理设备的方式。, 驱动程序运作过程。, 驱动程序的具体实例。,11.1 设备管理结构,11.1.1 概述,设备管理,即输入输出子系统,可分为上下两部分:一部分是,上层,的,与设备无关的,这部分根据输入输出请求,通过特定的设备驱动程序接口,来与设备进行通信。,另一部分是,下层,的,与设备有关的,常称为设备驱动程序,它直接与相应设备打交道,并且向上层提供一组访问接口。,设备管理的目标是对所有的外接设备进行良好的读、写、控制等操作。,首先要解决的问题就是怎样将任意的一个设备的所有操作进行归纳,设计出统一的接口。,内核常常使用,设备类型、主设备号,和,次设备号,来标识一个具体的设备。,但用户希望能用同样的应用程序和命令来访问设备和普通文件。,为此,Linux中的设备管理应用了设备文件这个概念来统一设备的访问接口。,简单的说,系统试图使它对所有各类设备的输入、输出看起来就好像对普通文件的输入、输出一样。,如图11-1所示,,应用程序通过Linux的系统调用与内核通信。,图11-1 Linux内核体系结构,由于Linux中将设备当作文件来处理,所以对设备进行操作的系统调用和对文件操作的类似,主要包括,open()、read()、write()、ioctl()、close(),等。,应用程序发出系统调用指令以后,会从用户态转换到内核态,通过内核将open()这样的系统调用转换成对物理设备的操作。,11.1.2 字符设备与块设备,字符设备,以,字节,为单位进行数据处理。字符设备通常只允许按,顺序,访问,一般不使用缓存技术。如鼠标,声卡等。,块设备,以,块,为单位进行处理,块的大小通常为0.5KB到32KB等。,大多数块设备允许随机访问,而且常常采用缓存技术。,块设备有,硬盘、光盘驱动器,等。可以查看文件/proc/devices获得。,我们这里主要讨论字符设备,有兴趣的读者可参考其它书籍中有关块设备的内容。,11.1.3 主设备号和次设备号,设备管理中,除了设备类型(字符设备或块设备)以外,内核还需要,一对称做主、次设备号,的参数,才能唯一表示设备。,主设备号(major number)相同的设备使用相同的驱动程序,而次设备号 (minor number) 用来区分具体设备的实例。,例如,第一IDE接口上的所有磁盘及其分区共用同一主设备号3,而次设备号则为0,1,2,3 。,11.1.4 Linux设备命名习惯,:,Linux习惯上将设备文件放在目录,/dev,或其子目录之下。,设备文件命名,(通常由两部分组成)规则为:第一部分通常较短,可能只有2或3个字母组成,用来表示设备大类。,例如,普通硬盘如IDE接口的为“hd”,软盘为“fd”。第二部分通常为数字或字母用来区别设备实例。,例如,/dev/hda、/dev/hdb、/dev/hdc表示第一、二、三块硬盘;而 dev/hda1、/dev/hda2、/dev/hda3则表示第一硬盘的第一、二、三分区。,11.2 驱动程序,11.2.1 驱动程序基本功能,在Linux操作系统中驱动程序是操作系统,内核与硬件设备,之间的桥梁,它屏蔽了硬件的细节 (如,总线协议、DMA操作,等),在应用程序看来硬件设备只是一个特殊的文件。,驱动程序的基本功能为:,1. 对设备初始化和释放。,如对音频设备而言包括向内核注册设备,设置音频的输入输出参数 (如采样频率、采样宽度等)、分配音频设备使用的内核内存等工作。,2. 对设备进行管理。,包括实时参数设置以及提供对设备的操作接口。,3. 读取应用程序传送给设备文件的数据并回送应用程序请求的数据。,这需要在用户空间、内核空间、总线及外设之间传输数据。,4. 检测和处理设备出现的错误。,11.2.2 驱动程序的运作过程,为了便于读者理解,在此结合大家比较熟悉的键盘来了解其运作过程。,如图11-2所示,.,图11-2 驱动程序的实现过程,当一个程序读/dev/tty文件(此为键盘)时,就会执行系统调用sys_read()(在fs/read_write.c中),该系统调用在判别出所读文件是一个字符设备文件时,即会调用rw_char()函数(在fs/char_dev.c中),,该函数则会根据所读设备的设备类型,主、次设备号等参数,由字符设备读写函数表(设备开关表)调用rw_tty(),最终调用到这里的终端读操作函数tty_read(),当用户在键盘上键入了一个字符时,会引起键盘中断响应,此时键盘中断处理程序就会从键盘控制器读入对应的键盘扫描码,然后根据使用的键盘扫描码映射表译成相应字符,放入tty读队列read_q中。,然后调用中断处理程序的do_tty_interrupt()函数,它又直接调用行规则函数copy_to_cooked()对该字符进行过滤处理,并放入tty辅助队列secondary中,供上述tty_read()读取。,同时把该字符放入tty写队列write_q中,并调用写控制台函数con_write()。,此时如果该终端的回显(echo)属性是设置的,则该字符会显示到屏幕上(注:do_tty_interrupt()和copy_to_cooked()函数在tty_io.c中实现)。,11.2.2 常用接口介绍,open,():,打开设备,并初始化设备准备进行操作。可以为NULL,这样每次打开设备总会成功,而且不通知设备驱动程序。,read,():,从设备中读数据,需要提供字符串指针。,write,():,向字符设备写数据,需要提供所写内容指针。,ioctl,():,控制设备,例如控制光盘的弹出等。需要提供符合设备预先定义的命令字。,llseek,():,重新定位读、写位置,需要提供偏移量参数。,flush,():,清除内容。,release,():,关闭设备,并释放资源等。,mmap,():,将设备内存映射到进程地址空间。通常只有块设备驱动程序使用。,11.2.3 常用函数原型,1.,设备操作函数原形,struct file_operations,struct module * owner;,loff_t(*llseek) (struct file *, loff_t,int);,ssize_t(*read) (struct file *,char*,size_t,loff_t *);,ssize_t(*write)(struct file *,const char *,size_t,loff_t *);,int (*readdir)(struct file *,void *,filldir_t);,unsigned int(*poll)(struct file *,struct poll_table_struct *);,int (*ioctl)(struct inode *,struct file *,unsigned int ,unsigned long );,int (mmap)(struct file *,struct vm_area_struct *);,int (*open)(struct inode *,struct file *);,int (*flush)(struct file*);,int(*release)(struct inode *,struct file *);,int (*fsync )(struct file ,struct dentry *,int datasync);,int (*fasync )(int,struct file *,int);,int (*lock)(struct file *,int struct file_lock *);,ssize_t(*readv)(struct file *,const struct iovec *,unsigned long,loff_t *);,ssize_t(*writev)(struct file *,const struct iovec *,unsigned long,loff_t *);,2向系统注册的函数原形,int,register_chrdev,(unsigned int major,const char * name,struct file_operations * fops),if (major = = 0 ),write_lock(,for(major=MAX_CHRDEV-1;major0;major-),if (chrdevsmajor.fops= =NULL),chrdevsmajor.name=name;,chrdevsmajor.fops=fops;,write_unlock(,return major;,write_unlock(,return -EBUSY;,if(majorMAX_CHRDEV),return -EINVAL;,write_lock(,if(chrdevsmajor.fops & chrdevsmajor,.,fops!=fops),write_unlock(,return -EBUSY;,chrdevsmajor.name=name;,chrdevsmajor.fops=fops;,write_unlock(,return 0;,11.3 驱动程序编写实例,为了更清楚地讲述Linux中设备驱动程序的编写,加深读者对启动程序的了解。下面介绍一个简单的设备驱动的实现过程。,由于基于特殊的硬件设备实现的驱动程序难度较大,而且不方便验证,下面举一个,虚拟设备驱动程序,的例子。,11.3.1 设备功能介绍,实现虚拟设备的,写入、读出,等操作。这个驱动程序并不是基于特定硬件设备的,实际上仅仅是对内存进行读、写操作。,当执行写入操作时,将会对特定的存储空间进行写入;当执行读出操作时,将会对该存储空间进行数据的读取;同时还可以利用ioctl进行清除该存储空间的操作。,这个,mydrv设备,的实现文件是mydrv.c,其中的文件接口flle_operations提供了mydrv_open、mydrv_release、mydrv_read、mydrv_write、mydrv_ioctl等函数。,1. 函数mydrv_read()的功能是从mybuf110中读取字符串,并传递给调用的进程。,2. 函数mydrv_write()的功能是将调用的进程传入的字符串赋值给mybuf,如果字符串的长度超过110,则只取前110个字符。,3. 函数mydrv_ioctl()中仅仅实现了一个控制功能:清除mybuf存储区。,11.3.2 具体实现,首先,要根据设备功能的需要,编写file_operations结构中的操作函数。,其次,要向系统,注册,该设备,包括,字符设备的注册,,,devfs节点的注册,与,中断响应函数的注册,。然后就可以利用对应的文件进行设备操控了。具体如下:,1源程序,# include ,# include ,# include ,# include ,# include ,# include ,# include ,# include ,# include ,# define MYDRV_CLS_IO( c ,0x01 ),/定义清存储区命令字,char mybuf110;,/存储区域,int mydrv_major = 99;,/主设备号,devfs_handle_t dev_handle;,/保存设备文件系统的注册句柄,/第一步:编写file_operations函数,ssize_t,mydrv_read,(struct file * filp, char * buf, size_t count,loff_t * f_pos);,/函数声明,static ssize_t,mydrv_write,(struct file * filp,const char * buf,size_t count,loff_t * ppos);,static int,mydrv_ioctl,( struct inode * inode,struct file * file, unsigned int cmd, unsigned long arg);,int,mydrv_open,(struct inode * inode,struct file *filp);,int,mydrv_release,(struct inode * inode, struct file * filp);,/函数声明,struct,file_operations mydrv_ops,=,/设备函数接口,open: mydrv_open ,/实现对设备的操作,read: mydrv_read,write: mydrv_write,ioctl: mydrv_ioctl,release: mydrv_release;,;,/ mydrv_read()将内核空间的mybuf中的字符串赋给用户空间的buf区,ssize_t,mydrv_read,(struct file * filp, char * bur, size_t count,loff_t * f_pos),/filp:指向设备文件的指针;f_pos:偏移量,int length = strlen(mybuf);,if(count 99) count = 99;,/忽略大于110部分,count = length - * f_pos;,/计算字符个数的技巧,if(copy_to_user(buf, mybuf, count) ) ,/重内核区复制到用户区,printk(error reading, copy_to_usern”);,retum -EFAULT;,*f_pos += count;,/下一个,retum count;,/ mydtv_write()将用户空间的buf字符串赋给内核空间的mybuf 数组中,static ssize_t,mydrv_write,(struct file * filp,const char * buf, size_t count,loff_t * ppos),int num;,num=counti_rdev);,if(mydrv_num),return -1;,if (dev = MAX_MYDRV_DEV),return -ENODEV;,filp- f_ap = ,/指向操作函数,printk(“open successn”);,MOD_INC_USE_COUNT;,/只是简单地加1,return 0;,/关闭mydrv设备,这里只是将引用次数减1,int,mydrv_release,(struct inode * inode, struct file * filp),MOD_DEC_USE_COUNT;,return 0;,/第二步:向系统注册该设备,/ module的安装,采用两种方式进行了设备的注册,int init_module(void),int result;,printk(“initing. n”);,result =,devfs_register_chrdev,(mydrv_major, “mydrv”,if (result 0),printk(KERN_WARNING “mydrv: unable to get major %d n”, mydrv_major);,return result;,dev_handle =,devfs_register(,NULL, “mydrv, DEVFS_FL_DEFAULT, 99,0,S_IFCHR, ,/devfs_register(devfs_handle_t dir,const char *name,unsigned int flags,/unsigned int major,unsigned int minor,umode_t mode,void *ops,void *info),if (mydrv_major:= = 0),mydrv_major = result;,strcpy(mybuf,Hello, please write anything ( length 110)to mydrv.”);,printk(“succeed in getting buffer n);,printk(%s n, mybuf);,retum 0;,/ module的卸载,进行设备的注销,void cleanup_module(void),devfs_unregister_chrdev(mydrv_major, “mydrv”);,devfs_unregister( dev_handle );,printk(exiting. n);,2设备驱动程序编译和安装,采用下面的命令可以对mydrv.c进行编译:,rootLinux root# gcc -c mydrv.c D_KERNEL_ -DMODULE -O2 -g -Wall -o,如果没有出错的话,将会在本目录下生成一个mydrv.o文件。,下面的操作必须是以root身份进行的(用命令su转换成root身份):,先执行模块的插入操作,,rootUnuxroot#/sbin/insmod mydrv.o,如果设备文件系统已经应用起来的话,此时在设备文件系统挂接的目录(通常是dev)下,就可以找到mydrv文件节点了。如果没有应用设备文件系统,则需要手工为设备添加文件节点:,rootLinuxdev#mknod mydrv c 99 0,此时就可以对设备进行读、写、ioctl等操作了。,当不再需要对设备进行操作时,可以采用下面的命令卸载模块:,rootLinuxdev#/sbin/rmmod mydrv,3设备的使用,下面的小程序可以对任何文件进行先写后读的操作:,#include ,int main(),FILE * fp;,char bufl00;,printf(Please input file name: );,scanf(% s, buf);,if(fp = fopen(buf, wb) = = NULL),/buf:文件名;wb:模式(只写、二进制),printf(Could not opened! n);,return -1;,else printf(File open ok! n);,printf(Please input( 110): );,scanf(% s, buf);,if(fputs(buf,fp) = = EOF),printf(Error writing file ! );,return -2;,fgets(but, 110,fp);,/由文件读取一字符串,printf(the File content is: %s n,buf);,fclose(fp);,return 0;,直接用gcc编译生成可执行文件之后,就可以用该程序对mydrv设备的文件节点进行操作。,当然,也可以采用cat命令得到mydrv设备的输出内容。,11.4 本章小结,本章主要介绍了设备管理方面的有关知识. 首先介绍了系统是怎样来管理设备的,即把设备看作一种的,特殊的文件,,从而实现了对设备的有关操作。,然后,说明了驱动程序运作过程。,最后,结合一个具体实例,阐明了驱动程序的具体的编写方法。,练习题,1.操作系统是怎么实现对设备进行管理的?,2.举出5个驱动程序的常用接口函数。,3.编写驱动程序一般有几个步骤,具体各是什么?,
展开阅读全文
相关资源
正为您匹配相似的精品文档
相关搜索

最新文档


当前位置:首页 > 办公文档 > 教学培训


copyright@ 2023-2025  zhuangpeitu.com 装配图网版权所有   联系电话:18123376007

备案号:ICP2024067431-1 川公网安备51140202000466号


本站为文档C2C交易模式,即用户上传的文档直接被用户下载,本站只是中间服务平台,本站所有文档下载所得的收益归上传人(含作者)所有。装配图网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。若文档所含内容侵犯了您的版权或隐私,请立即通知装配图网,我们立即给予删除!