Linux下设备管理与驱动程序编写实例(ppt 66页)
合集下载
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
2. 对设备进行管理。包括实时参数设置以及 提供对设备的操作接口。
3. 读取应用程序传送给设备文件的数据并回 送应用程序请求的数据。这需要在用户空间、 内核空间、总线及外设之间传输数据。
4. 检测和处理设备出现的错误。
11.2.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); return major; } }
大多数块设备允许随机访问,而且常常 采用缓存技术。
块设备有硬盘、光盘驱动器等。可以查 看文件/proc/devices获得。
我们这里主要讨论字符设备,有兴趣的 读者可参考其它书籍中有关块设备的内容。
11.1.3 主设备号和次设备号
设备管理中,除了设备类型(字符设备 或块设备)以外,内核还需要一对称做主、 次设备号的参数,才能唯一表示设备。
应用程序发出系统调用指令以后,会从 用户态转换到内核态,通过内核将open()这 样的系统调用转换成对物理设备的操作。
11.1.2 字符设备与块设备
字符设备以字节为单位进行数据处理。 字符设备通常只允许按顺序访问,一般不使 用缓存技术。如鼠标,声卡等。
块设备以块为单位进行处理,块的大小 通常为0.5KB到32KB等。
当用户在键盘上键入了一个字符时,会 引起键盘中断响应,此时键盘中断处理程序 就会从键盘控制器读入对应的键盘扫描码, 然后根据使用的键盘扫描码映射表译成相应 字符,放入tty读队列read_q中。
同时把该字符放入tty写队列write_q中, 并调用写控制台函数con_write()。
此时如果该终端的回显(echo)属性是设 置的,则该字符会显示到屏幕上(注: do_tty_interrupt()和copy_to_cooked()函数 在tty_io.c中实现)。
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);
11.3.2 具体实现
首先,要根据设备功能的需要,编写 file_operations结构中的操作函数。
其次,要向系统注册该设备,包括字符 设备的注册,devfs节点的注册与中断响应函 数的注册。然后就可以利用对应的文件进行 设备操控了。具体如下:
1.源程序 # 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>
11.2 驱动程序
11.2.1 驱动程序基本功能
在Linux操作系统中驱动程序是操作系统 内核与硬件设备之间的桥梁,它屏蔽了硬件 的细节 (如总线协议、DMA操作等),在应用 程序看来硬件设备只是一个特殊的文件。
驱动程序的基本功能为:
1. 对设备初始化和释放。如对音频设备而言 包括向内核注册设备,设置音频的输入输出 参数 (如采样频率、采样宽度等)、分配音频 设备使用的内核内存等工作。
简单的说,系统试图使它对所有各类设 备的输入、输出看起来就好像对普通文件的 输入、输出一样。
如图11-1所示,应用程序通过Linux的系 统调用与内核通信。
由于Linux中将设备当作文件来处理,所 以对设备进行操作的系统调用和对文件操作 的类似,主要包括open()、read()、write()、 ioctl()、close()等。
第11章 设备管理
Linux和其他操作系统一样,支持众多 的、各式各样的外接设备。
但是,面对层出不穷的新硬件产品,必 须有人不断编写新的驱动程序,以便让这些 设备能够在 Linux 下正常工作,从这个意义 上讲,讲述驱动程序的编写就是一件非常有 意义的工作。
本章也涉及到Linux下设备管理的原则和 方法。
当执行写入操作时,将会对特定的存储 空间进行写入;当执行读出操作时,将会对 该存储空间进行数据的读取;同时还可以利 用ioctl进行清除该存储空间的操作。
这个mydrv设备的实现文件是mydrv.c, 其中的文件接口flle_operations{}提供了 mydrv_open、mydrv_release、mydrv_read、 mydrv_write、mydrv_ioctl等函数。
11.3 驱动程序编写实例
为了更清楚地讲述Linux中设备驱动程序 的编写,加深读者对启动程序的了解。下面 介绍一个简单的设备驱动的实现过程。
由于基于特殊的硬件设备实现的驱动程 序难度较大,而且不方便验证,下面举一个 虚拟设备驱动程序的例子。
11.3.1 设备功能介绍
实现虚拟设备的写入、读出等操作。这 个驱动程序并不是基于特定硬件设备的,实 际上仅仅是对内存进行读、写操作。
write_unlock(&chrdevs_lock); return -EBUSY; } if(major>MAX_CHRDEV)
return -EINVAL; write_lock(&chrdevs_lock);
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); return 0; }
设备文件命名(通常由两部分组成)规 则为:第一部分通常较短,可能只有2或3个 字母组成,用来表示设备大类。
ห้องสมุดไป่ตู้
例如,普通硬盘如IDE接口的为“hd”,软 盘为“fd”。第二部分通常为数字或字母用来 区别设备实例。
例如,/dev/hda、/dev/hdb、/dev/hdc 表示第一、二、三块硬盘;而 dev/hda1、 /dev/hda2、/dev/hda3则表示第一硬盘的第 一、二、三分区。
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);
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);
11.1 设备管理结构
11.1.1 概述
设备管理即输入输出子系统,可分为上 下两部分:一部分是上层的,与设备无关的, 这部分根据输入输出请求,通过特定的设备 驱动程序接口,来与设备进行通信。
另一部分是下层的,与设备有关的,常 称为设备驱动程序,它直接与相应设备打交 道,并且向上层提供一组访问接口。
主设备号(major number)相同的设备 使用相同的驱动程序,而次设备号 (minor number) 用来区分具体设备的实例。
例如,第一IDE接口上的所有磁盘及其分 区共用同一主设备号3,而次设备号则为0,1, 2,3 …。
11.1.4 Linux设备命名习惯:
Linux习惯上将设备文件放在目录/dev或 其子目录之下。
如图11-2所示.
当一个程序读/dev/tty文件(此为键盘)时, 就会执行系统调用sys_read()(在 fs/read_write.c中),该系统调用在判别出所 读文件是一个字符设备文件时,即会调用 rw_char()函数(在fs/char_dev.c中),
该函数则会根据所读设备的设备类型, 主、次设备号等参数,由字符设备读写函数 表(设备开关表)调用rw_tty(),最终调用到 这里的终端读操作函数tty_read()
# define MYDRV_CLS_IO( 'c' ,0x01 ) //定义清存储区命令字 char mybuf[110]; //存储区域 int mydrv_major = 99; //主设备号 devfs_handle_t dev_handle; //保存设备文件系统的注册句柄 //第一步:编写file_operations函数
11.2.2 常用接口介绍
llseek(): 重新定位读、写位置,需要提供 偏移量参数。
flush(): 清除内容。
release(): 关闭设备,并释放资源等。
mmap(): 将设备内存映射到进程地址空间。 通常只有块设备驱动程序使用。
11.2.3 常用函数原型
1. 设备操作函数原形 struct file_operations {
2.向系统注册的函数原形 int register_chrdev(unsigned int major,const char * name,struct file_operations * fops) { if (major = = 0 ) {
write_lock(&chrdevs_lock);
设备管理的目标是对所有的外接设备进 行良好的读、写、控制等操作。
首先要解决的问题就是怎样将任意的一 个设备的所有操作进行归纳,设计出统一的 接口。
内核常常使用设备类型、主设备号和次 设备号来标识一个具体的设备。
但用户希望能用同样的应用程序和命令 来访问设备和普通文件。
为此,Linux中的设备管理应用了设备文 件这个概念来统一设备的访问接口。
1. 函数mydrv_read()的功能是从mybuf[110] 中读取字符串,并传递给调用的进程。
2. 函数mydrv_write()的功能是将调用的进程 传入的字符串赋值给mybuf,如果字符串的 长度超过110,则只取前110个字符。
3. 函数mydrv_ioctl()中仅仅实现了一个控制 功能:清除mybuf存储区。
举例来说,Linux下的驱动程序仅仅是为 相应的设备编写几个基本函数,并向VFS注册 就可以安装成功了。
当应用程序需要设备时,可以访问该设 备对应的文件节点,利用VFS调用该设备的相 关处理函数。
本章主要介绍了设备管理方面的有关知识: ◆ 系统管理设备的方式。 ◆ 驱动程序运作过程。 ◆ 驱动程序的具体实例。
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 *);
3. 读取应用程序传送给设备文件的数据并回 送应用程序请求的数据。这需要在用户空间、 内核空间、总线及外设之间传输数据。
4. 检测和处理设备出现的错误。
11.2.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); return major; } }
大多数块设备允许随机访问,而且常常 采用缓存技术。
块设备有硬盘、光盘驱动器等。可以查 看文件/proc/devices获得。
我们这里主要讨论字符设备,有兴趣的 读者可参考其它书籍中有关块设备的内容。
11.1.3 主设备号和次设备号
设备管理中,除了设备类型(字符设备 或块设备)以外,内核还需要一对称做主、 次设备号的参数,才能唯一表示设备。
应用程序发出系统调用指令以后,会从 用户态转换到内核态,通过内核将open()这 样的系统调用转换成对物理设备的操作。
11.1.2 字符设备与块设备
字符设备以字节为单位进行数据处理。 字符设备通常只允许按顺序访问,一般不使 用缓存技术。如鼠标,声卡等。
块设备以块为单位进行处理,块的大小 通常为0.5KB到32KB等。
当用户在键盘上键入了一个字符时,会 引起键盘中断响应,此时键盘中断处理程序 就会从键盘控制器读入对应的键盘扫描码, 然后根据使用的键盘扫描码映射表译成相应 字符,放入tty读队列read_q中。
同时把该字符放入tty写队列write_q中, 并调用写控制台函数con_write()。
此时如果该终端的回显(echo)属性是设 置的,则该字符会显示到屏幕上(注: do_tty_interrupt()和copy_to_cooked()函数 在tty_io.c中实现)。
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);
11.3.2 具体实现
首先,要根据设备功能的需要,编写 file_operations结构中的操作函数。
其次,要向系统注册该设备,包括字符 设备的注册,devfs节点的注册与中断响应函 数的注册。然后就可以利用对应的文件进行 设备操控了。具体如下:
1.源程序 # 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>
11.2 驱动程序
11.2.1 驱动程序基本功能
在Linux操作系统中驱动程序是操作系统 内核与硬件设备之间的桥梁,它屏蔽了硬件 的细节 (如总线协议、DMA操作等),在应用 程序看来硬件设备只是一个特殊的文件。
驱动程序的基本功能为:
1. 对设备初始化和释放。如对音频设备而言 包括向内核注册设备,设置音频的输入输出 参数 (如采样频率、采样宽度等)、分配音频 设备使用的内核内存等工作。
简单的说,系统试图使它对所有各类设 备的输入、输出看起来就好像对普通文件的 输入、输出一样。
如图11-1所示,应用程序通过Linux的系 统调用与内核通信。
由于Linux中将设备当作文件来处理,所 以对设备进行操作的系统调用和对文件操作 的类似,主要包括open()、read()、write()、 ioctl()、close()等。
第11章 设备管理
Linux和其他操作系统一样,支持众多 的、各式各样的外接设备。
但是,面对层出不穷的新硬件产品,必 须有人不断编写新的驱动程序,以便让这些 设备能够在 Linux 下正常工作,从这个意义 上讲,讲述驱动程序的编写就是一件非常有 意义的工作。
本章也涉及到Linux下设备管理的原则和 方法。
当执行写入操作时,将会对特定的存储 空间进行写入;当执行读出操作时,将会对 该存储空间进行数据的读取;同时还可以利 用ioctl进行清除该存储空间的操作。
这个mydrv设备的实现文件是mydrv.c, 其中的文件接口flle_operations{}提供了 mydrv_open、mydrv_release、mydrv_read、 mydrv_write、mydrv_ioctl等函数。
11.3 驱动程序编写实例
为了更清楚地讲述Linux中设备驱动程序 的编写,加深读者对启动程序的了解。下面 介绍一个简单的设备驱动的实现过程。
由于基于特殊的硬件设备实现的驱动程 序难度较大,而且不方便验证,下面举一个 虚拟设备驱动程序的例子。
11.3.1 设备功能介绍
实现虚拟设备的写入、读出等操作。这 个驱动程序并不是基于特定硬件设备的,实 际上仅仅是对内存进行读、写操作。
write_unlock(&chrdevs_lock); return -EBUSY; } if(major>MAX_CHRDEV)
return -EINVAL; write_lock(&chrdevs_lock);
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); return 0; }
设备文件命名(通常由两部分组成)规 则为:第一部分通常较短,可能只有2或3个 字母组成,用来表示设备大类。
ห้องสมุดไป่ตู้
例如,普通硬盘如IDE接口的为“hd”,软 盘为“fd”。第二部分通常为数字或字母用来 区别设备实例。
例如,/dev/hda、/dev/hdb、/dev/hdc 表示第一、二、三块硬盘;而 dev/hda1、 /dev/hda2、/dev/hda3则表示第一硬盘的第 一、二、三分区。
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);
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);
11.1 设备管理结构
11.1.1 概述
设备管理即输入输出子系统,可分为上 下两部分:一部分是上层的,与设备无关的, 这部分根据输入输出请求,通过特定的设备 驱动程序接口,来与设备进行通信。
另一部分是下层的,与设备有关的,常 称为设备驱动程序,它直接与相应设备打交 道,并且向上层提供一组访问接口。
主设备号(major number)相同的设备 使用相同的驱动程序,而次设备号 (minor number) 用来区分具体设备的实例。
例如,第一IDE接口上的所有磁盘及其分 区共用同一主设备号3,而次设备号则为0,1, 2,3 …。
11.1.4 Linux设备命名习惯:
Linux习惯上将设备文件放在目录/dev或 其子目录之下。
如图11-2所示.
当一个程序读/dev/tty文件(此为键盘)时, 就会执行系统调用sys_read()(在 fs/read_write.c中),该系统调用在判别出所 读文件是一个字符设备文件时,即会调用 rw_char()函数(在fs/char_dev.c中),
该函数则会根据所读设备的设备类型, 主、次设备号等参数,由字符设备读写函数 表(设备开关表)调用rw_tty(),最终调用到 这里的终端读操作函数tty_read()
# define MYDRV_CLS_IO( 'c' ,0x01 ) //定义清存储区命令字 char mybuf[110]; //存储区域 int mydrv_major = 99; //主设备号 devfs_handle_t dev_handle; //保存设备文件系统的注册句柄 //第一步:编写file_operations函数
11.2.2 常用接口介绍
llseek(): 重新定位读、写位置,需要提供 偏移量参数。
flush(): 清除内容。
release(): 关闭设备,并释放资源等。
mmap(): 将设备内存映射到进程地址空间。 通常只有块设备驱动程序使用。
11.2.3 常用函数原型
1. 设备操作函数原形 struct file_operations {
2.向系统注册的函数原形 int register_chrdev(unsigned int major,const char * name,struct file_operations * fops) { if (major = = 0 ) {
write_lock(&chrdevs_lock);
设备管理的目标是对所有的外接设备进 行良好的读、写、控制等操作。
首先要解决的问题就是怎样将任意的一 个设备的所有操作进行归纳,设计出统一的 接口。
内核常常使用设备类型、主设备号和次 设备号来标识一个具体的设备。
但用户希望能用同样的应用程序和命令 来访问设备和普通文件。
为此,Linux中的设备管理应用了设备文 件这个概念来统一设备的访问接口。
1. 函数mydrv_read()的功能是从mybuf[110] 中读取字符串,并传递给调用的进程。
2. 函数mydrv_write()的功能是将调用的进程 传入的字符串赋值给mybuf,如果字符串的 长度超过110,则只取前110个字符。
3. 函数mydrv_ioctl()中仅仅实现了一个控制 功能:清除mybuf存储区。
举例来说,Linux下的驱动程序仅仅是为 相应的设备编写几个基本函数,并向VFS注册 就可以安装成功了。
当应用程序需要设备时,可以访问该设 备对应的文件节点,利用VFS调用该设备的相 关处理函数。
本章主要介绍了设备管理方面的有关知识: ◆ 系统管理设备的方式。 ◆ 驱动程序运作过程。 ◆ 驱动程序的具体实例。
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 *);