基于Linux 操作系统字符设备驱动程序开发
2021-07-16占华林徐涛涛陈亮亮于子正李明鑫
占华林 徐涛涛 陈亮亮 于子正 李明鑫
(江西科技师范大学,江西南昌 330013)
任何一个计算机系统的运转都是系统中软硬件共同努力的结果,没有硬件的软件是空中楼阁,而没有软件的硬件则只是一堆废铁。计算机系统发现外部设备并与外部设备通信,依靠驱动程序。设备驱动程序运行在内核空间,作为外部设备与应用软件桥梁。在所有嵌入式操作系统里,嵌入式Linux 系统相比于其他的系统,在嵌入式领域有着独特的优势。首先是Linux 开源免费;然后是Linux 的内核体积小且易于模块化,适应性和可配置性好适合各种平台,它同时还支持多任务、多用户;最后是Linux 有很多开发论坛,提供丰富开发资料,对开发者而言是强大的技术外援。所以本论文是基于Linux 操作系统上开发字符设备驱动程序。
1 字符设备驱动程序设计基础
1.1 基本原理
1.1.1 创建设备节点。在Linux 操作系统中所有设备都当作文件来处理,每个设备在/dev 目录下都有一个设备文件与之相对应,对设备的打开、读写、控制和关闭等所有操作都是通过/dev/目录下对应的文件进行操作。设备节点有时会自动创建,可是字符设备驱动并不直接负责这一项操作。因此我们在设计字符设备驱动时会需要手动创建节点,使用mknod 命令在文件系统上人工创建。除此之外Linux 中有一组函数可以用来在/ dev目录下创建设备节点,即class_create ( ) 和device_create ( )。1.1.2 主次设备号。Linux 管理的所有设备都有与之对应的目录,内核通过驱动程序来识别设备。一般来说驱动程序通过主设备号快速匹配外部设备,但同一类设备数量不只一个(或者说一个驱动程序可以同时匹配多个设备),所以必须增加次设备号,它给驱动程序提供了一种能从同一驱动管理下的同类型设备分辨出目标设备的方法。1.1.3 用户空间和内核空间。嵌入式系统属于多用户、多任务操作系统,不能因为某个用户或某个任务的非法操作造成死机现象,所以用程序运行在用户空间,而驱动程序则运行在内核空间,内核空间维护计算机系统全局稳定性,驱动程序运行时受到内核程序较多约束,因为在内核中运行任一程序出现问题都会造成计算机系统死机。而应用程序运行较“宽松”。1.1.4 应用程序与硬件设备的访问。计算机系统中的应用程序通过运行在内核空间的驱动程序来访问计算机外部设备。应用程序按照如图1 所示的流程对硬件设备进行访问。首先是应用程序调用操作系统提供的系统函数,从而访问内核空间,然后内核空间的结构file_operations 来实现驱动程序中的相关函数,从而实现对设备的访问。
图1 应用程序到硬件设备的访问
1.2 重要结构file_operations
要实现用户空间对内核空间的调用,必须依赖Linux 操作系统内核中一个非常重要的数据结构:file_operations。此结构存放在内核include/linux/fs.h 文件中,所以用到此结构的代码都应包含此头文件#include <linux/fs.h>。file_operations 结构如下:
Linux 使用 file_operations 访问驱动中的函数时,file_operations 中存储着对设备进行各种操作的函数指针f_ops,file_operations 中每一个成员名都对应一个调用函数,如:file_open( )、file_allocate( )、file_read( )、file_write( )、file_ioctl( )、file_close( )等。比如在对系统进行数据存取的时候,系统通过设备的设备号来找到驱动,然后读取驱动,开始执行函数。例如:应用程序要对某个外部设备进行如open 操作时,这时匹配该外部设备的驱动程序就会执行对应的fopen 函数。
2 字符设备驱动程序开发
字符设备驱动程序开发涉及到知识点较多,开发起来显得有点复杂。但规律可循,总结如下流程:
第一步:编写驱动程序文件
准备工作(定义设备名称、定义主次设备号)、设备注册、定义结构体file_operations、实现结构体内的功能函数、设备注销等。具体下来有以下工作:a.初始化外部设备的相关寄存器。包括定时寄存器、中断寄存器、串口寄存器;b.初始化硬件设备参数;c.注册设备。通过主次设备号与次设备号将驱动程序与设备关联起来,内核会调用函数register_chrdev 来注册匹配的外部设备;d.注册外部设备中断函数。内核会调用函数request_irq( )来注册匹配的外部设备的中断函数;
第二步:驱动程序放进Linux 内核
将编辑好驱动程序源文件和头文件放置内核相对应的文件下./kernel/drivers/char,同时修改Linux 内核配置文件***.config,在使用make menuconfig 命令时在配置页面里出现相应的配置选项,使用空格键选中,保存后退出,再使用预安装好的交叉编译工具链进行交叉编译。需要说明的,设备驱动程序编译有两种方法,一种是编译进内核,另一种是编译成模块。一般来说,调试阶段编译成模块形式,驱动程序稳定后,编译进内核。修改内核配置文件如下:
a.修改Makefile 文件。
在kernel/drivers/char 目录下
的Makefile 文件中填加如下代码,编译后会生成DEVICE_DRIVER.o 文件:
DEVICE_DRIVER.o
b.修改配置菜单
在Linux 内核对应的目录下kernel/drivers/char 修改配置文件config.in。在comment 'Character devices' 下面添加。运行make menuconfig 命令时,选中support for DEVICE_DRIVER,编译后则选中的驱动程序就会编译进内核:
bool'supportforDEVICE_DRIVER'CONFIG_DEVICE_DRIVER
第三步:交叉编译
在kernel/目录下使用如下命令进行交叉编译。
make dep
make clean
Make zImage
第四步:下载烧录
使用相关应用软件将新的内样镜像文件下载烧录到板载Flash 内,或将驱动程序模块镜像使用插入的方法到嵌入式平台。插入命令:insmod device_driver.o。再使用命令创设备节点:mknod /dev/device_name c 50 0。
3 字符设备驱动程序举例
在Linux 环境下,以键盘驱动程序为例进行说明。硬件基本情况是:S3C2410+HD7279(键盘数码管模块)。
3.1 编写键盘驱动程序
a.编写file_operations 结构。
d.初始化键盘硬件函数。
3.2 测试键盘驱动程序
设备驱动编译后,接着编写测试程序kbd.c 用来检测驱动程序的效果。
fd = open("/dev/Kbd7279", 0)
结束语
本论文讲述了字符设备驱动的基本原理及编写字符设备驱动程序的方法。基于S3C2410+HD7279 平台上,演示了键盘字符设备驱动程序开发并进行了验证。本文可认为是字符设备驱动开发范例,为初学字符设备驱动开发提供参考。