设备管理结构及驱动程序编写实例_第1页
设备管理结构及驱动程序编写实例_第2页
设备管理结构及驱动程序编写实例_第3页
设备管理结构及驱动程序编写实例_第4页
设备管理结构及驱动程序编写实例_第5页
已阅读5页,还剩56页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

第10章 设备管理第一页,共六十二页。第10章 主要内容

本章主要介绍了设备管理方面的有关知识:◆系统管理设备的方式。◆驱动程序运作过程。◆驱动程序的具体实例。第二页,共六十二页。第10章 目录

10.1

设备管理结构

10.2

驱动程序

10.3

驱动程序编写实例第三页,共六十二页。第10章 目录

10.1

设备管理结构

10.2

驱动程序

10.3

驱动程序编写实例第四页,共六十二页。10.1设备管理结构:

设备管理即输入输出子系统,分为上下两部分:1.上层的,与设备无关的,这部分根据输入输出请求,通过特定的设备驱动程序接口,来与设备进行通信。2.下层的,与设备有关的,常称为设备驱动程序,它直接与相应设备打交道,并且向上层提供一组访问接口。概述第五页,共六十二页。10.1设备管理结构:

设备管理的目标是对所有的外接设备进行良好的读、写、控制等操作。

待解决问题:

怎样将任意的一个设备的所有操作进行归纳,设计出统一的接口。内核常常使用设备类型、主设备号和次设备号来标识一个具体的设备。概述第六页,共六十二页。10.1设备管理结构:概述Linux中的设备管理应用了设备文件这个概念来统一设备的访问接口。

简单的说,系统试图使它对所有各类设备的输入、输出看起来就好像对普通文件的输入、输出一样。第七页,共六十二页。

如图10-1所示,应用程序通过Linux的系统调用与内核通信。第八页,共六十二页。10.1设备管理结构:概述

由于Linux中将设备当作文件来处理,所以对设备进行操作的系统调用和对文件操作的类似,主要包括open()、read()、write()、ioctl()、close()等。应用程序发出系统调用指令以后,会从用户态转换到内核态,通过内核将open()这样的系统调用转换成对物理设备的操作。第九页,共六十二页。10.1设备管理结构:

字符设备以字节为单位进行数据处理。字符设备通常只允许按顺序访问,一般不使用缓存技术。如鼠标,声卡等。

块设备以块为单位进行处理,块的大小通常为0.5KB到32KB等。字符设备与块设备第十页,共六十二页。10.1设备管理结构:字符设备与块设备

大多数块设备允许随机访问,而且常常采用缓存技术。块设备有硬盘、光盘驱动器等。可以查看文件/proc/devices获得。这里主要讨论字符设备,有兴趣的读者可参考其它书籍中有关块设备的内容。

第十一页,共六十二页。10.1设备管理结构:

设备管理中,除了设备类型(字符设备或块设备)以外,内核还需要一对称做主、次设备号的参数,才能唯一表示设备。主设备号和次设备号第十二页,共六十二页。10.1设备管理结构:主设备号和次设备号

主设备号(majornumber)相同的设备使用相同的驱动程序,而次设备号(minornumber)用来区分具体设备的实例。

例如:第一IDE接口上的所有磁盘及其分区共用同一主设备号3,而次设备号则为0,1,2,3…。第十三页,共六十二页。10.1设备管理结构:

Linux习惯上将设备文件放在目录/dev或其子目录之下。设备文件命名(通常由两部分组成)规则为:第一部分通常较短,可能只有2或3个字母组成,用来表示设备大类。

例如:普通硬盘如IDE接口的为“hd”,软盘为“fd”。Linux设备命名习惯第十四页,共六十二页。10.1设备管理结构:Linux设备命名习惯

第二部分通常为数字或字母用来区别设备实例。例如:

/dev/hda、/dev/hdb、/dev/hdc表示第一、二、三块硬盘;而dev/hda1、/dev/hda2、/dev/hda3则表示第一硬盘的第一、二、三分区。第十五页,共六十二页。第10章 目录

10.1

设备管理结构

10.2

驱动程序

10.3

驱动程序编写实例第十六页,共六十二页。10.2驱动程序:

在Linux操作系统中驱动程序是操作系统内核与硬件设备之间的桥梁,它屏蔽了硬件的细节(如总线协议、DMA操作等),在应用程序看来硬件设备只是一个特殊的文件。驱动程序基本功能第十七页,共六十二页。10.2驱动程序:驱动程序基本功能1.对设备初始化和释放。如对音频设备而言包括向内核注册设备,设置音频的输入输出参数(如采样频率、采样宽度等)、分配音频设备使用的内核内存等工作。

2.对设备进行管理。包括实时参数设置以及提供对设备的操作接口。驱动程序的基本功能为:第十八页,共六十二页。10.2驱动程序:驱动程序基本功能

3.读取应用程序传送给设备文件的数据并回送应用程序请求的数据。这需要在用户空间、内核空间、总线及外设之间传输数据。

4.检测和处理设备出现的错误。第十九页,共六十二页。结合大家比较熟悉的键盘来了解其运作过程:第二十页,共六十二页。10.2驱动程序:驱动程序的运作过程

当一个程序读/dev/tty文件(此为键盘)时,就会执行系统调用sys_read()(在fs/read_write.c中),该系统调用在判别出所读文件是一个字符设备文件时,即会调用rw_char()函数(在fs/char_dev.c中),该函数则会根据所读设备的设备类型,主、次设备号等参数,由字符设备读写函数表(设备开关表)调用rw_tty(),最终调用到这里的终端读操作函数tty_read()

第二十一页,共六十二页。10.2驱动程序:驱动程序的运作过程

当用户在键盘上键入了一个字符时,会引起键盘中断响应,此时键盘中断处理程序就会从键盘控制器读入对应的键盘扫描码,然后根据使用的键盘扫描码映射表译成相应字符,放入tty读队列read_q中。第二十二页,共六十二页。10.2驱动程序:驱动程序的运作过程

然后调用中断处理程序的do_tty_interrupt()函数,它又直接调用行规则函数copy_to_cooked()对该字符进行过滤处理,并放入tty辅助队列secondary中,供上述tty_read()读取。第二十三页,共六十二页。10.2驱动程序:驱动程序的运作过程

同时把该字符放入tty写队列write_q中,并调用写控制台函数con_write()。 此时如果该终端的回显(echo)属性是设置的,则该字符会显示到屏幕上(注:do_tty_interrupt()和copy_to_cooked()函数在tty_io.c中实现)。第二十四页,共六十二页。10.2驱动程序:open():打开设备,并初始化设备准备进行操作。可以为NULL,这样每次打开设备总会成功,而且不通知设备驱动程序。

read():从设备中读数据,需要提供字符串指针。

write():向字符设备写数据,需要提供所写内容指针。

ioctl():控制设备,例如控制光盘的弹出等。需要提供符合设备预先定义的命令字。常用接口介绍第二十五页,共六十二页。10.2驱动程序:常用接口介绍llseek():重新定位读、写位置,需要提供偏移量参数。

flush():清除内容。

release():关闭设备,并释放资源等。

mmap():将设备内存映射到进程地址空间。通常只有块设备驱动程序使用。第二十六页,共六十二页。10.2驱动程序:

struct {structmodule*owner; loff_t(*llseek)(structfile*,loff_t,int); ssize_t(*read)(structfile*,char*,size_t,loff_t*); ssize_t(*write)(structfile*,constchar *,size_t,loff_t*); int(*readdir)(structfile*,void*,filldir_t);常用函数原型1.设备操作函数原形第二十七页,共六十二页。

unsignedint(*poll)(structfile*,struct poll_table_struct*); int(*ioctl)(structinode*,structfile*,unsigned int,unsignedlong); int(mmap)(structfile*,structvm_area_struct*); int(*open)(structinode*,structfile*); int(*flush)(structfile*); int(*release)(structinode*,structfile*);第二十八页,共六十二页。10.2驱动程序:常用函数原型

int(*fsync)(structdentry*,int datasync); int(*fasync)(int,structfile*,int); int(*lock)(structfile*,intstruct*); ssize_t(*readv)(structfile*,conststructiovec *,unsignedlong,loff_t*); ssize_t(*writev)(structfile*,conststructiovec *,unsignedlong,loff_t*); }第二十九页,共六十二页。10.2驱动程序:常用函数原型 intregister_chrdev(unsignedintmajor,constchar*name,struct*fops) { if(major==0) { write_lock(&chrdevs_lock);2.向系统注册的函数原形第三十页,共六十二页。10.2驱动程序:常用函数原型

for(major=MAX_CHRDEV-1;major>0;major--) { if(chrdevs[major].fops==NULL) { chrdevs[major].name=name; chrdevs[major].fops=fops; write_unlock(&chrdevs_lock); returnmajor; } }第三十一页,共六十二页。10.2驱动程序:常用函数原型

write_unlock(&chrdevs_lock); return-EBUSY; } if(major>MAX_CHRDEV) return-EINVAL; write_lock(&chrdevs_lock);第三十二页,共六十二页。10.2驱动程序:常用函数原型

if(chrdevs[major].fops&&chrdevs[major].fops!=fops) { write_unlock(&chrdevs_lock); return-EBUSY; } chrdevs[major].name=name; chrdevs[major].fops=fops; write_unlock(&chrdevs_lock); return0; }第三十三页,共六十二页。第10章 目录

10.1

设备管理结构

10.2驱动程序

10.3

驱动程序编写实例第三十四页,共六十二页。10.3驱动程序编写实例

为了更清楚地讲述Linux中设备驱动程序的编写,加深读者对启动程序的了解。下面介绍一个简单的设备驱动的实现过程。由于基于特殊的硬件设备实现的驱动程序难度较大,而且不方便验证,下面举一个虚拟设备驱动程序的例子。第三十五页,共六十二页。10.3驱动程序编写实例:

实现虚拟设备的写入、读出等操作。这个驱动程序并不是基于特定硬件设备的,实际上仅仅是对内存进行读、写操作。设备功能介绍第三十六页,共六十二页。10.3驱动程序编写实例:设备功能介绍

1.函数mydrv_read()的功能是从mybuf[100]中读取字符串,并传递给调用的进程。

2.函数mydrv_write()的功能是将调用的进程传入的字符串赋值给mybuf,如果字符串的长度超过100,则只取前100个字符。

3.函数mydrv_ioctl()中仅仅实现了一个控制功能:清除mybuf存储区。第三十七页,共六十二页。10.3驱动程序编写实例:

首先,要根据设备功能的需要,编写结构中的操作函数。

其次,要向系统注册该设备,包括字符设备的注册,devfs节点的注册与中断响应函数的注册。然后就可以利用对应的文件进行设备操控了。具体如下:具体实现第三十八页,共六十二页。10.3驱动程序编写实例:具体实现

#include<linux/module.h> #include<linux/kernel.h> #include<linux/fs.h> #include<linux/types.h> #include<linux/malloc.h> #include<asm/uaccess.h> #include<asm/page.h> #include<linux/ermo.h> #include<linux/config.h>1.源程序:第三十九页,共六十二页。10.3驱动程序编写实例:具体实现#defineMYDRV_CLS_IO('c',0x01) //定义清存储区命令字

charmybuf[100]; //存储区域

intmydrv_major=99; //主设备号

devfs_handle_tdev_handle; //保存设备文件系统的注册句柄

//第一步:编写函数第四十页,共六十二页。10.3驱动程序编写实例:具体实现ssize_tmydrv_read(structfile*filp,char*buf,size_tcount,loff_t*f_pos);//函数声明

staticssize_tmydrv_write(structfile*filp,constchar*buf,size_tcount,loff_t*ppos); staticintmydrv_ioctl(structinode*inode,structfile*file,unsignedintcmd,unsignedlongarg); intmydrv_open(structinode*inode,structfile*filp); intmydrv_release(structinode*inode,structfile*filp);//函数声明第四十一页,共六十二页。10.3驱动程序编写实例:具体实现structmydrv_ops={ //设备函数接口

open:mydrv_open,//实现对设备的操作

read:mydrv_read,write:mydrv_write,ioctl:mydrv_ioctl, release:mydrv_release; }; //mydrv_read()将内核空间的mybuf中的字符串赋给用户空间的buf区第四十二页,共六十二页。10.3驱动程序编写实例:具体实现ssize_tmydrv_read(structfile*filp,char*bur,size_tcount,loff_t*f_pos) //filp:指向设备文件的指针;f_pos:偏移量

intlength=strlen(mybuf); if(count>99)count=99; //忽略大于100部分

count=length-*f_pos; //计算字符个数的技巧第四十三页,共六十二页。10.3驱动程序编写实例:具体实现if(copy_to_user(buf,mybuf,count)){ //重内核区复制到用户区

printk("errorreading,copy_to_user\n”); retum-EFAULT; } *f_pos+=count;//下一个

retumcount; } //mydtv_write()将用户空间的buf字符串赋给内核空间的mybuf[]数组中第四十四页,共六十二页。10.3驱动程序编写实例:具体实现staticssize_tmydrv_write(structfile*filp,constchar*buf,size_tcount,loff_t*ppos){ intnum; num=count<100?count:100; if(copy_from_user(mybuf,buf,num))//mybufbuf return-EFAULT; printk("mydrv_writesucceed!\n”); returnnum; }第四十五页,共六十二页。10.3驱动程序编写实例:具体实现staticintmydrv_ioctl(structinode*inode,structfile*file,//如果传人的命令字是

unsignedintcmd,unsignedlongarg){//MYDRV-CLS则清除mybuf数组内容

switch(cmd){ caseMYDRV_CLS: mybuf[0]=0x0; return0; default: return-EINVAL; } } //打开mydrv设备时调用第四十六页,共六十二页。10.3驱动程序编写实例:具体实现#defineMAX_MYDRV_DEV2 intmydrv_open(structinode*inode,structfile*filp){//inede:设备文件节点

unsignedintdev=MINOR(inode->i_rdev); if(mydrv_num) return-1;第四十七页,共六十二页。10.3驱动程序编写实例:具体实现if(dev>=MAX_MYDRV_DEV) return-ENODEV; filp->f_ap=&mydrv_ops;//指向操作函数

printk(“opensuccess\n”); MOD_INC_USE_COUNT;//只是简单地加1 return0; } //关闭mydrv设备,这里只是将引用次数减1 intmydrv_release(structinode*inode,structfile*filp){ MOD_DEC_USE_COUNT; return0; }第四十八页,共六十二页。10.3驱动程序编写实例:具体实现//第二步:向系统注册该设备

//module的安装,采用两种方式进行了设备的注册

intinit_module(void) { intresult; printk(“initing...\n”); result=devfs_register_chrdev(mydrv_major,“mydrv”,&mydrv_ops);第四十九页,共六十二页。10.3驱动程序编写实例:具体实现if(result<0){ printk(KERN_WARNING“mydrv:unabletogetmajor%d\n”,mydrv_major); returnresult; } dev_handle=devfs_register(NULL,“mydrv",DEVFS_FL_DEFAULT,99,0, S_IFCHR,&mydrv_ops,NULL); //devfs_register(devfs_handle_tdir,constchar*name,unsignedintflags, //unsignedintmajor,unsignedintminor,umode_tmode,void*ops,void*info)第五十页,共六十二页。10.3驱动程序编写实例:具体实现if(mydrv_major:==0) mydrv_major=result; strcpy(mybuf,"Hello,pleasewriteanything(length<100)tomydrv.”); printk(“succeedingettingbuffer\n"); printk("%s\n",mybuf); retum0; } //module的卸载,进行设备的注销第五十一页,共六十二页。10.3驱动程序编写实例:具体实现voidcleanup_module(void) { devfs_unregister_chrdev(mydrv_major,“mydrv”); devfs_unregister(dev_handle); printk("exiting...\n"); }第五十二页,共六十二页。10.3驱动程序编写实例:具体实现2.设备驱动程序编译和安装

采用下面的命令可以对mydrv.c进行编译:

[root@Linuxroot]#gcc-cmydrv.c–D__KERNEL__-DMODULE-O2-g-Wall-o

如果没有出错的话,将会在本目录下生成一个mydrv.o文件。第五十三页,共六十二页。10.3驱动程序编写实例:具体实现

下面的操作必须是以root身份进行的(用命令su转换成root身份): 先执行模块的插入操作,

[root@Unuxroot]#/sbin/insmodmydrv.o第五十四页,共六十二页。10.3驱动程序编写实例:具体实现

如果设备文件系统已经应用起来的话,此时在设备文件系统挂接的目录(通常是/dev)下,就可以找到mydrv文件节点了。如果没有应用设备文件系统,则需要手工为设备添加文件节点:

[root@Linux/dev]#mknodmydrvc990

此时就可以对设备进行读、写、ioctl等操作了。第五十五页,共六十二页。10.3驱动程序编写实例:具体实现

当不再需要对设备进行操作时,可以采用下面的命

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论