最新Linux设备驱动开发ppt课件
合集下载
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
结构体的每个域都对应着驱动模块用来处理某个被请求的事务的 函数的地址。
struct file_operations { struct module *owner; ssize_t(*read) (struct file *, char __user *, size_t, loff_t *); ssize_t(*write) (struct file *, const char __user *, size_t, loff_t *); 。。。
Linux设备驱动
Linux下设备的属性
设备的类型:字符设备、块设备、网络设备 主设备号:标识设备对应的驱动程序。一般“一个
主设备号对应一个驱动程序” 次设备号:每个驱动程序负责管理它所驱动的几个
硬件实例,这些硬件实例则由次设备号来表示。同 一驱动下的实例编号,用于确定设备文件所指的设 备。 可通过ls –l “设备文件名”命令查看设备的主次设 备号,以及设备的类型。
Gcc的语法扩展,使得可以定义该结构体: struct file_operations fops = {
read: device_read, write: device_write, open: device_open, release: device_release };
没有显示声明的结构体成员都被gcc初始化为NULL。
设备驱动概述
设备由两部分组成,一个是被称为控制器的电器部分, 另一个是机械部分。
一组寄存器组被赋予到各个控制器。I/O端口包含4组寄 存器,即状态寄存器,控制寄存器,数据输入寄存器, 数据输出寄存器。
状态寄存器拥有可以被CPU读取的(状态)位,用来 指示当前命 令是否执行完毕,或者字节是否可以被读出或写入,以及任何 错误提示。
int (*mmap) (struct file *, struct vm_area_struct *); 将设备内存映射到进程地址空间
15
一些重要的数据结构
file_operations重要的成员
驱动内核模块是不需要实现每个函数的。相对应的 file_operations的项就为 NULL。
入缓冲的例子。我们可以看到,每当一个系统调用被使用时,内 核就转到相应的设备驱动例程来操纵硬件。
8
Linux设备驱动
Linux操作系统把设备纳入文件系统的范畴来管理。 每个设备在Linux系统上看起来都像一个文件,它们存
放在/dev目录中,称为"设备节点当是请求的第一个要用的次设备号,它常 常是 0.
count 和 name 参数跟request_chrdev_region 的一样.
24
分配和释放字符设备号
不再使用时,释放这些设备编号。使用以下函 数:
void unregister_chrdev_region(dev_t from, unsigned count)
23
分配和释放字符设备号
动态分配设备编号(主要是主设备号)
int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count,const char *name)
dev 是一个仅用于输出的参数, 它在函数成功完成时保 存已分配范围的第一个编号。
加载脚本实现。
26
字符设备的注册
内核内部使用struct cdev结构表示字符设备。编写 设备驱动的第二步就是注册该设备。
包含<linux/cdev.h>头文件。 获取一个独立的cdev结构:
struct cdev *my_cdev = cdev_alloc();
调用cdev_init初始化cdev结构体
在模块的卸载函数中调用该函数。
25
分配和释放字符设备号
新驱动程序,建议使用动态分配机制获取主设备号, 也就是使用alloc_chrdev_region()。
动态分配导致无法预先创建设备节点。 可在分配设备号后,从/proc/devices文件中获取。 为了加载后自动创建设备文件,可以通过编写内核模块
其他成员可先忽略,后面具体实例分析。因为设备驱
动模块并不自己直接填充结构体 file,只是使用file中
的数据。
19
一些重要的数据结构
索引节点inode结构
文件打开,在内存建立副本后,由唯一的索引节点 inode描述。
与file结构不同。 file结构是进程使用的结构,进程每打开一个文件, 就建立一个file结构。不同的进程打开同一个文件, 建立不同的file结构。 Inode结构是内核使用的结构,文件在内存建立副 本,就建立一个inode结构来描述。一个文件在内 存里面只有一个inode结构对应。
20
一些重要的数据结构
索引节点inode结构
Inode结构包含大量描述文件信息的成员变量。 但是对于描述设备文件的inode,跟设备驱动有关的成
员只有两个。 Dev_t i_rdev; 包含真正的设备编号。 Struct cdev *i_cdev; 指向cdev结构体的指针。cdev是
表示字符设备的内核数据结构。 从inode中获得主设备号和次设备号的宏:
void cdev_init(struct cdev *cdev, struct file_operations *fops);
初始化该设备的所有者字段:
dev->cdev.owner = THIS_MODULE;
初始化该设备的可用操作集:
dev->cdev.ops = &device_fops;
大部分驱动程序涉及三个重要的内核数据结构: 文件操作file_operations结构体 文件对象file结构体 索引节点inode结构体
12
一些重要的数据结构
文件操作结构体file_operations
结构体file_operations在头文件 linux/fs.h中定义,用来存储驱动 内核模块提供的对设备进行各种操作的函数的指针。
控制寄存器则用于启动一条命令(指令)或者改变设备的(工 作)模式。
数据输入寄存器用于获取输入的数据。 数据输出寄存器则向CPU发送结果。
设备驱动概述
操作系统是通过各种驱动程序来驾驭硬件设备,它为用 户屏蔽了各种各样的设备。
设备驱动程序是操作系统内核和机器硬件之间的接口, 系统调用是操作系统内核和应用程序之间的接口。
目前设备编号dev_t是一个32位的整数,其中12位表示主设备号, 20位表示次设备号。
通过设备编号获取主次设备号:
MAJOR(dev_t dev);
MINOR(dev_t dev);
通过主次设备号合成设备编号:
MKDEV(int major, int minor);
Dev_t格式以后可能会发生变化,但只要使用这些宏,就可保证
Struct file 在<linux/fs.h>中定义。
指向结构体struct file的指针通常命名为filp,或者file。
建议使用文件指针filp。
18
一些重要的数据结构
文件对象file结构体的成员
Struct file_operations *f_op; 与文件相关的操作结构体指针。与文件相关的操作是 在打开文件的时候确定下来的,也就是确定该指针的 值。可在需要的时候,改变指针所指向的文件操作结 构体。用C语言实现面向对象编程的方法重载。
27
字符设备的注册
编写设备驱动的第二步就是注册该设备。
10
Linux设备驱动
Linux设备驱动程序是一组由 内核中的相关子例程和数据 组成的I/O设备软件接口。
每当用户程序要访问某个设 备时,它就通过系统调用, 让内核代替它调用相应的驱 动例程。这就使得控制从用 户进程转移到了驱动例程, 当驱动例程完成后,控制又 被返回至用户进程。
11
一些重要的数据结构
}; 推荐使用该方法,提高移植性,方法允许对结构体成员进行重新
排列。没有显示声明的结构体成员同样都被gcc初始化为NULL。
指向结构体file_operations的指针通常命名为fops。
17
一些重要的数据结构
文件对象file结构体
文件对象file代表着一个打开的文件。进程通过文件描 述符fd与已打开文件的file结构相联系。进程通过它 对文件的线性逻辑空间进行操作。例如:file>f_op->read();
Linux的内核是映射到每一个进程的高1G空间。每一个 用户进程运行时都好像有一份内核的拷贝,每当用户 进程使用系统调用时,都自动地将运行模式从用户级 转为内核级,此时进程在内核的地址空间中运行。
7
Linux设备驱动
Linux内核使用“设备无关”的I/O子系统来为所有的设备服务。 每个设备都提供标准接口给内核,尽可能地隐藏了自己的特性。 用户程序使用一些基本的系统调用从设备读取数据并且将它们存
}
13
一些重要的数据结构
file_operations重要的成员
Struct module *owner ,指向拥有该结构体的模块的指针。内核 使用该指针维护模块使用计数。
方法llseek用来修改文件的当前读写位置,把新位置作为返回值 返回。loff_t是在LINUX中定义的长偏移量
方法read用来从设备中读取数据。非负返回值表示成功读取的直 接数。
设备驱动程序的正确性。
22
分配和释放字符设备号
编写驱动程序要做的第一件事,为字符设备获取一 个设备号。
事先知道所需要的设备编号(主设备号)的情况: int register_chrdev_region(dev_t first, unsigned count, const char *name) first是要分配的起始设备编号值。 first的次设备号通常 设置为0。 Count 所请求的连续设备编号的个数。 Name设备名称,指和该编号范围建立关系的设备。 分配成功返回0。
Unsigned int iminor(struct inode *inode); Unsigned int imajor(struct inode *inode);
21
Linux设备驱动
主设备号和次设备号的内部表达:
Dev_t类型用于保存设备号,称为设备编号。/linux/types.h文件 中定义。
在应用程序看来,硬件设备只是一个设备文件, 应用 程序可以象操作普通文件一样对硬件设备进行操作.
设备驱动概述
驱动完成以下的功能:
对设备初始化和释放. 把数据从内核传送到硬件和从硬件读取数据. 读取应用程序传送给设备文件的数据和回送应用程
序请求的数据. 检测和处理设备出现的错误.
4
设备驱动概述
方法write向设备发送数据。 方法ioctl提供一种执行设备特定命令的方法。
14
一些重要的数据结构
file_operations重要的成员
unsigned int (*poll) (struct file *, struct poll_table_struct *); 系统调用select和poll的后端实现,用这两个系统调用来查询 设备是否 可读写,或是否处于某种状态。如果poll为空,则驱动设备会被 认为即可读又可写,返回值是一个状态掩码。
无操作系统的设备驱动 有操作系统的设备驱动
Application
Driver
Application Lib API
System call
Hardware
Embedded OS Driver
Hardware
不带操作系统软件结构
带操作系统软件结构
5
Linux设备驱动
6
Linux设备驱动
用户级的程序使用内核提供的标准系统调用来与内核 通讯,这些系统调用有:open(), read(), write(), ioctl(), close() 等等。
16
一些重要的数据结构
file_operations重要的成员
标准C的标记化结构体的初始化方法:
struct file_operations fops = { .read = device_read, .write = device_write, .open = device_open, .release = device_release
struct file_operations { struct module *owner; ssize_t(*read) (struct file *, char __user *, size_t, loff_t *); ssize_t(*write) (struct file *, const char __user *, size_t, loff_t *); 。。。
Linux设备驱动
Linux下设备的属性
设备的类型:字符设备、块设备、网络设备 主设备号:标识设备对应的驱动程序。一般“一个
主设备号对应一个驱动程序” 次设备号:每个驱动程序负责管理它所驱动的几个
硬件实例,这些硬件实例则由次设备号来表示。同 一驱动下的实例编号,用于确定设备文件所指的设 备。 可通过ls –l “设备文件名”命令查看设备的主次设 备号,以及设备的类型。
Gcc的语法扩展,使得可以定义该结构体: struct file_operations fops = {
read: device_read, write: device_write, open: device_open, release: device_release };
没有显示声明的结构体成员都被gcc初始化为NULL。
设备驱动概述
设备由两部分组成,一个是被称为控制器的电器部分, 另一个是机械部分。
一组寄存器组被赋予到各个控制器。I/O端口包含4组寄 存器,即状态寄存器,控制寄存器,数据输入寄存器, 数据输出寄存器。
状态寄存器拥有可以被CPU读取的(状态)位,用来 指示当前命 令是否执行完毕,或者字节是否可以被读出或写入,以及任何 错误提示。
int (*mmap) (struct file *, struct vm_area_struct *); 将设备内存映射到进程地址空间
15
一些重要的数据结构
file_operations重要的成员
驱动内核模块是不需要实现每个函数的。相对应的 file_operations的项就为 NULL。
入缓冲的例子。我们可以看到,每当一个系统调用被使用时,内 核就转到相应的设备驱动例程来操纵硬件。
8
Linux设备驱动
Linux操作系统把设备纳入文件系统的范畴来管理。 每个设备在Linux系统上看起来都像一个文件,它们存
放在/dev目录中,称为"设备节点当是请求的第一个要用的次设备号,它常 常是 0.
count 和 name 参数跟request_chrdev_region 的一样.
24
分配和释放字符设备号
不再使用时,释放这些设备编号。使用以下函 数:
void unregister_chrdev_region(dev_t from, unsigned count)
23
分配和释放字符设备号
动态分配设备编号(主要是主设备号)
int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count,const char *name)
dev 是一个仅用于输出的参数, 它在函数成功完成时保 存已分配范围的第一个编号。
加载脚本实现。
26
字符设备的注册
内核内部使用struct cdev结构表示字符设备。编写 设备驱动的第二步就是注册该设备。
包含<linux/cdev.h>头文件。 获取一个独立的cdev结构:
struct cdev *my_cdev = cdev_alloc();
调用cdev_init初始化cdev结构体
在模块的卸载函数中调用该函数。
25
分配和释放字符设备号
新驱动程序,建议使用动态分配机制获取主设备号, 也就是使用alloc_chrdev_region()。
动态分配导致无法预先创建设备节点。 可在分配设备号后,从/proc/devices文件中获取。 为了加载后自动创建设备文件,可以通过编写内核模块
其他成员可先忽略,后面具体实例分析。因为设备驱
动模块并不自己直接填充结构体 file,只是使用file中
的数据。
19
一些重要的数据结构
索引节点inode结构
文件打开,在内存建立副本后,由唯一的索引节点 inode描述。
与file结构不同。 file结构是进程使用的结构,进程每打开一个文件, 就建立一个file结构。不同的进程打开同一个文件, 建立不同的file结构。 Inode结构是内核使用的结构,文件在内存建立副 本,就建立一个inode结构来描述。一个文件在内 存里面只有一个inode结构对应。
20
一些重要的数据结构
索引节点inode结构
Inode结构包含大量描述文件信息的成员变量。 但是对于描述设备文件的inode,跟设备驱动有关的成
员只有两个。 Dev_t i_rdev; 包含真正的设备编号。 Struct cdev *i_cdev; 指向cdev结构体的指针。cdev是
表示字符设备的内核数据结构。 从inode中获得主设备号和次设备号的宏:
void cdev_init(struct cdev *cdev, struct file_operations *fops);
初始化该设备的所有者字段:
dev->cdev.owner = THIS_MODULE;
初始化该设备的可用操作集:
dev->cdev.ops = &device_fops;
大部分驱动程序涉及三个重要的内核数据结构: 文件操作file_operations结构体 文件对象file结构体 索引节点inode结构体
12
一些重要的数据结构
文件操作结构体file_operations
结构体file_operations在头文件 linux/fs.h中定义,用来存储驱动 内核模块提供的对设备进行各种操作的函数的指针。
控制寄存器则用于启动一条命令(指令)或者改变设备的(工 作)模式。
数据输入寄存器用于获取输入的数据。 数据输出寄存器则向CPU发送结果。
设备驱动概述
操作系统是通过各种驱动程序来驾驭硬件设备,它为用 户屏蔽了各种各样的设备。
设备驱动程序是操作系统内核和机器硬件之间的接口, 系统调用是操作系统内核和应用程序之间的接口。
目前设备编号dev_t是一个32位的整数,其中12位表示主设备号, 20位表示次设备号。
通过设备编号获取主次设备号:
MAJOR(dev_t dev);
MINOR(dev_t dev);
通过主次设备号合成设备编号:
MKDEV(int major, int minor);
Dev_t格式以后可能会发生变化,但只要使用这些宏,就可保证
Struct file 在<linux/fs.h>中定义。
指向结构体struct file的指针通常命名为filp,或者file。
建议使用文件指针filp。
18
一些重要的数据结构
文件对象file结构体的成员
Struct file_operations *f_op; 与文件相关的操作结构体指针。与文件相关的操作是 在打开文件的时候确定下来的,也就是确定该指针的 值。可在需要的时候,改变指针所指向的文件操作结 构体。用C语言实现面向对象编程的方法重载。
27
字符设备的注册
编写设备驱动的第二步就是注册该设备。
10
Linux设备驱动
Linux设备驱动程序是一组由 内核中的相关子例程和数据 组成的I/O设备软件接口。
每当用户程序要访问某个设 备时,它就通过系统调用, 让内核代替它调用相应的驱 动例程。这就使得控制从用 户进程转移到了驱动例程, 当驱动例程完成后,控制又 被返回至用户进程。
11
一些重要的数据结构
}; 推荐使用该方法,提高移植性,方法允许对结构体成员进行重新
排列。没有显示声明的结构体成员同样都被gcc初始化为NULL。
指向结构体file_operations的指针通常命名为fops。
17
一些重要的数据结构
文件对象file结构体
文件对象file代表着一个打开的文件。进程通过文件描 述符fd与已打开文件的file结构相联系。进程通过它 对文件的线性逻辑空间进行操作。例如:file>f_op->read();
Linux的内核是映射到每一个进程的高1G空间。每一个 用户进程运行时都好像有一份内核的拷贝,每当用户 进程使用系统调用时,都自动地将运行模式从用户级 转为内核级,此时进程在内核的地址空间中运行。
7
Linux设备驱动
Linux内核使用“设备无关”的I/O子系统来为所有的设备服务。 每个设备都提供标准接口给内核,尽可能地隐藏了自己的特性。 用户程序使用一些基本的系统调用从设备读取数据并且将它们存
}
13
一些重要的数据结构
file_operations重要的成员
Struct module *owner ,指向拥有该结构体的模块的指针。内核 使用该指针维护模块使用计数。
方法llseek用来修改文件的当前读写位置,把新位置作为返回值 返回。loff_t是在LINUX中定义的长偏移量
方法read用来从设备中读取数据。非负返回值表示成功读取的直 接数。
设备驱动程序的正确性。
22
分配和释放字符设备号
编写驱动程序要做的第一件事,为字符设备获取一 个设备号。
事先知道所需要的设备编号(主设备号)的情况: int register_chrdev_region(dev_t first, unsigned count, const char *name) first是要分配的起始设备编号值。 first的次设备号通常 设置为0。 Count 所请求的连续设备编号的个数。 Name设备名称,指和该编号范围建立关系的设备。 分配成功返回0。
Unsigned int iminor(struct inode *inode); Unsigned int imajor(struct inode *inode);
21
Linux设备驱动
主设备号和次设备号的内部表达:
Dev_t类型用于保存设备号,称为设备编号。/linux/types.h文件 中定义。
在应用程序看来,硬件设备只是一个设备文件, 应用 程序可以象操作普通文件一样对硬件设备进行操作.
设备驱动概述
驱动完成以下的功能:
对设备初始化和释放. 把数据从内核传送到硬件和从硬件读取数据. 读取应用程序传送给设备文件的数据和回送应用程
序请求的数据. 检测和处理设备出现的错误.
4
设备驱动概述
方法write向设备发送数据。 方法ioctl提供一种执行设备特定命令的方法。
14
一些重要的数据结构
file_operations重要的成员
unsigned int (*poll) (struct file *, struct poll_table_struct *); 系统调用select和poll的后端实现,用这两个系统调用来查询 设备是否 可读写,或是否处于某种状态。如果poll为空,则驱动设备会被 认为即可读又可写,返回值是一个状态掩码。
无操作系统的设备驱动 有操作系统的设备驱动
Application
Driver
Application Lib API
System call
Hardware
Embedded OS Driver
Hardware
不带操作系统软件结构
带操作系统软件结构
5
Linux设备驱动
6
Linux设备驱动
用户级的程序使用内核提供的标准系统调用来与内核 通讯,这些系统调用有:open(), read(), write(), ioctl(), close() 等等。
16
一些重要的数据结构
file_operations重要的成员
标准C的标记化结构体的初始化方法:
struct file_operations fops = { .read = device_read, .write = device_write, .open = device_open, .release = device_release