设备管理_linux设备驱动程序课件_第1页
设备管理_linux设备驱动程序课件_第2页
设备管理_linux设备驱动程序课件_第3页
设备管理_linux设备驱动程序课件_第4页
设备管理_linux设备驱动程序课件_第5页
已阅读5页,还剩57页未读 继续免费阅读

下载本文档

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

文档简介

Linux设备驱动 广州嵌入式软件公共技术支持中心梁老师2007年07月 设备驱动概述 操作系统是通过各种驱动程序来驾驭硬件设备 它为用户屏蔽了各种各样的设备 硬件设备的抽象 设备驱动程序 处理和管理硬件控制器的软件 设备驱动程序是操作系统内核和机器硬件之间的接口 设备驱动概述 设备由两部分组成 一个是被称为控制器的电器部分 另一个是机械部分 一组寄存器组被赋予到各个控制器 I O端口包含4组寄存器 即状态寄存器 控制寄存器 数据输入寄存器 数据输出寄存器 状态寄存器拥有可以被CPU读取的 状态 位 用来指示当前命令是否执行完毕 或者字节是否可以被读出或写入 以及任何错误提示 控制寄存器则用于启动一条命令 指令 或者改变设备的 工作 模式 数据输入寄存器用于获取输入的数据 数据输出寄存器则向CPU发送结果 处理器和设备之间的基本界面是控制和状态寄存器 设备驱动概述 寄存器拥有在I O空间明确定义的地址范围 通常这些地址在启动时被分配 如果设备是静态加载的 各个设备的地址范围可能被预分配 这意味内核包含了已存在设备的驱动程序 通过运行 cat proc ioports 命令检查其所使用的地址范围 第一列输出显示了端口的范围而第二列则是拥用这些端口的设备 设备驱动概述 设备驱动的概念是非常抽象的并且处于一台计算上所运行软件的最低层 由于直接到设备的硬件特性的限制 每个设备驱动都只管理一种单一类型的设备 如果一个应用程序向设备提出 操作 要求 内核会联系到对应的设备驱动 设备驱动接着向特定的设备发出命令 设备驱动是一个函数集合 包含了许多调用入口 类似于open close read write ioctl llseek等 设备驱动概述 Linux操作系统把设备纳入文件系统的范畴来管理 文件操作是对设备操作的组织和抽象 设备操作则是对文件操作的最终实现 每个设备都对应一个文件名 在内核中也就对应一个索引节点 对文件操作的系统调用大都适用于设备文件 从应用程序的角度看 设备文件逻辑上的空间是一个线性空间 起始地址为0 每读取一个字节加1 从这个逻辑空间到具体设备物理空间 如磁盘的磁道 扇区 的映射则是由内核提供 并被划分为文件操作和设备驱动两个层次 设备驱动概述 Linux将设备分成两大类 一类像键盘那样以字符 字节 为单位 逐个字符进行输入 输出的设备 称为字符设备 一类是像磁盘那样以块或扇区为单位 成块进行输入 输出的设备 称为块设备 文件系统通常都建立在块设备上 设备驱动概述 文件操作和设备驱动是对一个具体的设备操作的不同层次 从这种观点出发 从概念上可以把一个系统划分为应用 文件系统和设备驱动三个层次 设备驱动概述 设备驱动概述 要使一项设备可以被应用程序访问 首先要在系统中建立一个代表此设备的设备文件 这是通过系统调用mknode 实现的 此外 更重要的是在设备驱动层要有这种设备的驱动程序 设备驱动概述 设备文件 任何设备都被当作路径 dev的设备文件处理 并通过这些设备文件提供访问硬件的方法 每个设备文件除了设备名外 还有类型 主设备号 次设备号这三个属性 设备文件是通过mknod系统调用创建的 其原型为 mknod constchar filename intmode dev tdev mknod dev led0c2530 设备驱动概述 主设备号和次设备号 主设备号标识设备对应的驱动程序 一般 一个主设备号对应一个驱动程序 次设备号用于确定设备文件所指的设备 可通过ls l 设备文件名 命令查看设备的主次设备号 以及设备的类型 设备驱动概述 主设备号和次设备号的内部表达 Dev t类型用于保存设备号 称为设备编号 linux types h文件中定义 目前设备编号dev t是一个32位的整数 其中12位表示主设备号 20位表示次设备号 通过设备编号获取主次设备号 MAJOR dev tdev MINOR dev tdev 通过主次设备号合成设备编号 MKDEV intmajor intminor Dev t格式以后可能会发生变化 但只要使用这些宏 就可保证设备驱动程序的正确性 一些重要的数据结构 大部分驱动程序涉及三个重要的内核数据结构 文件操作file operations结构体文件对象file结构体索引节点inode结构体 一些重要的数据结构 文件操作结构体file operations结构体file operations在头文件linux fs h中定义 用来存储驱动内核模块提供的对设备进行各种操作的函数的指针 结构体的每个域都对应着驱动模块用来处理某个被请求的事务的函数的地址 structfile operations structmodule owner loff t llseek structfile loff t int ssize t read structfile char user size t loff t ssize t write structfile constchar user size t loff t 一些重要的数据结构 file operations重要的成员Structmodule owner 指向拥有该结构体的模块的指针 方法llseek用来修改文件的当前读写位置 把新位置作为返回值返回 方法read用来从设备中读取数据 非负返回值表示成功读取的直接数 方法write向设备发送数据 方法ioctl提供一种执行设备特定命令的方法 一些重要的数据结构 file operations重要的成员驱动内核模块是不需要实现每个函数的 相对应的file operations的项就为NULL Gcc的语法扩展 使得可以定义该结构体 structfile operationsfops read device read write device write open device open release device release 这种语法清晰 没有显示声明的结构体成员都被gcc初始化为NULL 一些重要的数据结构 file operations重要的成员标准C的标记化结构体的初始化方法 structfile operationsfops read device read write device write open device open release device release 推荐使用该方法 提高移植性 方法允许对结构体成员进行重新排列 没有显示声明的结构体成员同样都被gcc初始化为NULL 指向结构体file operations的指针通常命名为fops 一些重要的数据结构 文件对象file结构体文件对象file代表着一个打开的文件 进程通过文件描述符fd与已打开文件的file结构相联系 进程通过它对文件的线性逻辑空间进行操作 例如 file f op read Structfile在中定义 指向结构体structfile的指针通常命名为filp 或者file 建议使用文件指针filp 一些重要的数据结构 文件对象file结构体的成员Structfile operations f op 与文件相关的操作结构体指针 与文件相关的操作是在打开文件的时候确定下来的 也就是确定该指针的值 可在需要的时候 改变指针所指向的文件操作结构体 用C语言实现面向对象编程的方法重载 其他成员可先忽略 后面具体实例分析 因为设备驱动模块并不自己直接填充结构体file 只是使用file中的数据 一些重要的数据结构 索引节点inode结构文件打开 在内存建立副本后 由唯一的索引节点inode描述 与file结构不同 file结构是进程使用的结构 进程每打开一个文件 就建立一个file结构 不同的进程打开同一个文件 建立不同的file结构 Inode结构是内核使用的结构 文件在内存建立副本 就建立一个inode结构来描述 一个文件在内存里面只有一个inode结构对应 一些重要的数据结构 索引节点inode结构Inode结构包含大量描述文件信息的成员变量 但是对于描述设备文件的inode 跟设备驱动有关的成员只有两个 Dev ti rdev 包含真正的设备编号 Structcdev i cdev 指向cdev结构体的指针 cdev是表示字符设备的内核数据结构 从inode中获得主设备号和次设备号的宏 Unsignedintiminor structinode inode Unsignedintimajor structinode inode 驱动程序中的内存分配 在Linux内核模式下 不能使用用户态的malloc 和free 函数申请和释放内存 内核编程最常用的内存申请和释放函数为kmalloc 和kfree 其原型为 include linux kernel hvoid kmalloc unsignedintlen intpriority voidkfree void ptr priority参数 通常设置为GFP KERNEL 可能会引起睡眠 如果在中断服务程序里申请内存则要用GFP ATOMIC参数 在中断中是不允许睡眠的 初始化和卸载函数 驱动程序是内核的一部分 因此我们需要给其添加模块初始化函数 该函数用来完成对所控设备的初始化工作 并调用register chrdev 函数注册字符设备 intregister chrdev unsignedintmajor constchar name structfile operations fops major是给定的主设备号 为0代表什么 name是驱动的名字 将出现在 proc devices fops是设备驱动的file operations结构 register chrdev将给设备分配0 255的次设备号 并且为每一个建立一个缺省的cdev结构 与模块初始化函数对应的就是模块卸载函数 需要调用register chrdev 的 反函数 设备操作函数集的定义 file operations结构体 驱动程序只是利用其中的一部分 对于字符设备来说 要提供的主要入口有 open release read write ioctl 等 设备操作函数集的定义 open 函数对设备特殊文件进行open 系统调用时 将调用驱动程序的open 函数 int open structinode structfile 参数inode为设备特殊文件的inode 索引结点 结构的指针 参数file是指向这一设备的文件结构的指针 open 的主要任务是确定硬件处在就绪状态 验证次设备号的合法性 次设备号可以用MINOR inode i rdev 取得 控制使用设备的进程数 根据执行情况返回状态码 0表示成功 负数表示存在错误 等 设备操作函数集的定义 release 函数当最后一个打开设备的用户进程执行close 系统调用时 内核将调用驱动程序的release 函数 void release structinode structfile release函数的主要任务是清理未结束的输入 输出操作 释放资源 用户自定义排他标志的复位等 设备操作函数集的定义 read 函数Read的任务 就是从设备拷贝数据到用户空间 当对设备特殊文件进行read 系统调用时 将调用驱动程序read 函数 ssize tread structfile filp char user buff size tcount loff t offp filp是文件对象指针 count是请求的传输数据大小 buff参数对write来说是指向持有被写入数据的缓存 对read则是放入新数据的空缓存 offp是指向一个 longoffsettype 的指针 它指出用户正在存取的文件位置 返回值是 signedsizetype 类型 设备操作函数集的定义 write 函数Write的任务 则从用户空间拷贝数据到设备 当设备特殊文件进行write 系统调用时 将调用驱动程序的write 函数 ssize twrite structfile filp constchar user buff size tcount loff t offp filp是文件对象指针 count是请求的传输数据大小 buff参数对write来说是指向持有被写入数据的缓存 对read则是放入新数据的空缓存 offp是指向一个 longoffsettype 的指针 它指出用户正在存取的文件位置 返回值是 signedsizetype 类型 设备操作函数集的定义 read和write方法的buff参数是用户空间指针 不能被内核代码直接解引用 user字符串只是形式上的说明 表明是用户空间地址 驱动必须能够存取用户空间缓存以完成它的工作 内核如何解决这个问题 为安全起见 内核提供专用的函数来完成对用户空间的存取 这些专用函数在中声明 unsignedlongcopy to user void user to constvoid from unsignedlongcount unsignedlongcopy from user void to constvoid user from unsignedlongcount 大多数读写函数都会调用这两个函数 用于跟应用程序空间交流信息 Read和Write方法 典型的Read函数对参数的使用 S3C2410的I O介绍 S3C2410有117个复用功能输入输出端口引脚 这些引脚是 PortA GPA 32个输入 输出端口PortB GPB 11个输入 输出端口PortC GPC 16个输入 输出端口PortD GPD 16个输入 输出端口PortE GPE 16个输入 输出端口PortF GPF 8个输入 输出端口PortG GPG 16个输入 输出端口PortH GPH 11个输入 输出端口 S3C2410的I O介绍 端口控制说明端口配置寄存器 GPACON GPHCON 大部分的引脚是复用的 所以必须对于每个引脚要求定义一个功能 端口配置寄存器定义了每个引脚的功能 端口数据寄存器 GPADAT GPHDAT 如果端口配置成输出端口 数据能够被写到端口数据寄存器的对应位 然后通过管脚输出 如果端口配置成输入端口 能从端口数据寄存器对应的位中读出管脚上的电平端口上拉寄存器 GPBUP GPHUP 端口上拉寄存器控制着每个端口组的上拉寄存器的使能或禁止 当对应位为0 这个引脚的上拉寄存器是允许的 当为1时 上拉寄存器是禁止的 MIZI提供的S3C2410 H 使用一个32位的数来表示端口的使用情况 模式 上拉 端口 端口引脚MODE PULLUP PORT OFS不需要自己手动组合 通过宏定义以及SHIFT和MASK组合 见程序 MIZI提供的S3C2410 H 端口的表示 definePORTA OFS0 definePORTB OFS1 definePORTC OFS2 definePORTD OFS3 definePORTE OFS4 definePORTF OFS5 definePORTG OFS6 definePORTH OFS7 MIZI提供的S3C2410 H 端口引脚的表示 defineGPIO A0MAKE GPIO NUM PORTA OFS 0 defineGPIO A1MAKE GPIO NUM PORTA OFS 1 defineGPIO A2MAKE GPIO NUM PORTA OFS 2 defineGPIO A3MAKE GPIO NUM PORTA OFS 3 defineMAKE GPIO NUM p o p GPIO PORT SHIFTT o GPIO OFS SHIFT MIZI提供的S3C2410 H set gpio ctrl x 功能 配置端口引脚的功能 设置IO口控制寄存器和上拉寄存器用法 set gpio ctrl 模式 上拉 IO脚 模式 是否上拉 IO脚 在S3C2410 h中都有其定义好的名字 set gpio ctrl GPIO E11 GPIO PULLUP DIS GPIO MODE OUT MIZI提供的S3C2410 H write gpio bit x v 功能 把端口对应的端口数据寄存器x位设置为vwrite gpio bit GPIO E11 0 read gpio bit x 功能 把端口数据寄存器x位的状态读入 函数返回值既是其状态read gpio bit GPIO G11 MIZI提供的S3C2410 H write gpio reg x v 功能 把端口数据寄存器x设置为vread gpio reg x 功能 读取端口数据寄存器x 函数返回值既是其数据 按驱动的框架写好驱动 实现初始化 卸载函数 以及file opertation操作集 对于led使用的gpio函数 内核没有输出作为公开符号 所以需要手动修改内核代码 代码放在arch arm march s3c2410 gpio c 输出符号 EXPORT SYMBOL s3c2410 gpio setpin EXPORT SYMBOL s3c2410 gpio cfgpin EXPORT SYMBOL s3c2410 gpio pullup 编写驱动模块的Makefile 交叉编译驱动模块NFS加载驱动模块 进行测试 嵌入式键盘驱动 广州嵌入式软件公共技术支持中心梁老师2007年7月 注册中断服务例程 中断线是一个宝贵且常常有限的资源 特别当它们只有15个时 内核维护一个中断线的注册表 要使用中断线 就要进行中断线的申请 也就是IRQ InterruptReQuirement 因此我们也常把申请一条中断线称为申请一个IRQ或者是申请一个中断号 IRQ线是从0开始顺序编号的 第一条IRQ线通常表示成IRQ0 IRQn的缺省向量是n 32 注册中断服务例程 并不是每个设备都可以向中断线上发中断信号的 只有对某一条确定的中断线拥有了控制权 才可以向这条中断线上发送信号 计算机的外部设备越来越多 所以15条中断线已经不够用了 中断线是非常宝贵的资源 只有当设备需要中断的时候才申请占用一个IRQ 或者是在申请IRQ时采用共享中断的方式 这样可以让更多的设备使用中断 注册中断服务例程 在实现中断注册接口 intrequest irq unsignedintirq irqreturn t handler int void structpt regs unsignedlongflags constchar dev name void dev id voidfree irq unsignedintirq void dev id request irq的返回值是0指示申请成功 为负值时表示错误码 函数返回 EBUSY表示已经有另一个驱动占用了所要申请的中断线 注册中断服务例程 request irq的参数说明 unsignedintirq 要申请的中断号 对某些设备 如传统PC设备上的系统时钟或键盘 这个值通常是预先确定的 而对于大多数其它设备来说 这个值要么可以通过探测获取 要么可以动态确定 irqreturn t handler int void structpt regs 要安装的中断处理函数指针 后面介绍 注册中断服务例程 request irq的参数说明 unsignedlongflags 与中断管理相关的位掩码选项 constchar dev name 用在 proc interrupts中显示中断的拥有者 void dev id这个指针用于共享的中断线 做为驱动程序的私有数据区 可用来识别那个设备产生的中断 不使用共享中断线方式时 可设置为NULL 注册中断服务例程 flags参数的详细说明 Flags的每个位有不同含义SA INTERRUPT当该位被设置时 表示这是一个 快速 中断 快速中断处理例程运行时 屏蔽中断 SA SHIRQ这个位表示中断可以在设备间共享 proc文件系统中的中断信息 proc interrupts反映系统的中断信息第一列是IRQ号给出每个中断线发生中断的次数 给出处理中断的可编程中断控制器 给出在该中断号上注册中断处理例程的设备名称 实现中断处理例程 首先中断处理例程也是普通的C程序 特别之处 在中断时间内运行 不能向用户空间发送或者接收数据 不能做任何导致休眠的操作 不能调用schedule函数 无论快速还是慢速中断处理例程 都应该设计成执行时间尽可能短 实现中断处理例程 中断处理函数的参数和返回值irqreturn t handler int void structpt regs irqreturn tshort interrupt intirq void dev id structpt regs regs Irq中断号Dev id驱动程序可用的数据区 通常可传递指向描述设备的数据结构指针 structpt regs regs 保存了处理器进入中断代码之前的cpu寄存器的值 一般驱动可不要 矩阵式键盘原理 矩阵式键盘一般适用于按键数量较多的场合 它由行线和列线组成 按键位于行 列的交叉点上 如图所示 一个4 4的行 列结构可以构成一个有16个按键的键盘 矩阵式键盘原理 按键设置在行 列交叉点上 行 列分别连接到按键开关的两端 行线通过上拉电阻接到十5V上 平时无按键动作时 行线处于高电平状态 而当有健按下时 行线电平状态将由通过此按键的列线电平决定 列线电平如果为低 行线电平为低 列线电平如果为高 则行线电平亦为高 这一点是识别矩阵式键盘是否被按下的关键所在 矩阵式键盘原理 矩阵键盘按键的识别方法分两步进行 识别键盘哪一行的键被按下 让所有列线均为低电平 检查各行线电平是否为低 如果有行线为低 则说明该行有键被按下 否则说明无键被按下 如果某行有键被按下 识别键盘哪一列的键被按下 亦称之为扫描法 逐列置低电平 并置其余各列为高电平 检查各行线电平的变化 如果行电平变为低电平 则可确定此行此列交叉点处按键被按下 键盘的硬件实现 键盘的硬件实现 4X4矩阵键盘四个输入引脚 EINT0 G

温馨提示

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

评论

0/150

提交评论