实验五:驱动程序设计
******************************************************************************* 实验的总体说明:
该实验开发板处于正常启动模式,打开minicom用zmodem传输文件。
SW1的123456拨码开关设置为ON-OFF-OFF-ON-ON-ON。
SW2的123456拨码开关配置为ON-ON-OFF-ON-ON-ON,即关闭IrDA模块。*******************************************************************************
说明:
讲述Linux设备驱动的分类及其概念,最基本的驱动编写方法和注意事项。由此设计以下几个实验。
(1)Hello Kernel内核模块的设计,编译,插入,卸载与验证。了解内核模块的设计过程以及内核模块最基本的结构。
(2)一个设备驱动程序的设计实现,实现从一个字符设备中定时读取数据,提供给应用程序以调用接口,可向设备写入数据,并向内核注册。
(3)选择BHU DBMX1 ADS的触摸屏驱动程序做以分析。
环境:Red Hat Linux 9.0,gcc3.x,/usr/src/linux目录内核版本为2.4.18。minicom 查看调试信息。KDB内核调试环境。
工具:vi,gcc,make。
7.1.基础知识
前面接触到了基于Qt/Embedded,Qtopia的GUI程序的设计,同时也了解了一些串口通信程序的设计方法等。所接触到的API可以帮助程序员访问文件系统,内存,网络等事物连接着的计算机硬件和设备。这些东西有一个共同的特性,那就是允许应用程序向内核提出硬件操作的要求。为了满足来自应用程序的操作要求,内核需要依靠不同的设备驱动程序与它可能遇到的各种类型的设备打交道。尽管硬件设备的底层技术千差万别,但通过设备驱动程序,内核却能够向应用程序提供一个形式统一的程序接口。文件系统就是一个例子——文件虽然可以保存在RamDisk,Flash ROM等等的设备中,但用户对文件进行操作时使用的系
统调用(open, read, write等)却都是一样的。
在编写设备驱动代码时需要掌握一些新的函数库,考虑一些新的问题,这些问题都是用户在设计应用
程序中所不曾遇到的。在编写应用程序的时候,内核能够为用户提供基本的安全保障,但由于设备驱动程序将构成内核自身的一部分,而这也就意味着内核将无法为设备驱动程序提供安全保障,内核代码对计算机有绝对的控制,因此需要设备驱动的设计人员不能滥用这种权利。
设备
设备可以是计算机上的任何一个部分,既可以直接制造在计算机的核心,也可以是一个外围设备——比如网卡等等。在说起编写新的设备驱动程序时用户一般都指后者-为最新的接口设备需要添加必要的软件支持。
设备驱动
设备驱动是这样一类软件:它们控制着设备的操作动作,并且提供了一个可用的程序接口使其他程序能够与这个设备互动。设备驱动程序并不一定控制着某个物理性的硬件外部设备,比如/dev/null、/dev/random。这些设备与真实的硬件没有什么联系,它们只是从内核获取数据再送往应用程序的一种手段。
各种设备驱动构成了它们所控制的硬件和操作系统内核之间的一个过渡层次。这个层次扎根于硬件,服务于内核,极大的简化了内核的设计和应用——它向外界提供了一个精心设计的接口,具体的工作
将由各个设备驱动程序去完成,而内核就不必亲自去与每一个设备打交道。这就意味着操作系统的内核部分只要还能适应有关模型的框架,就可以去在不了解各种不同设备的的具体情况的前提下被编写出来并做进一步的开发。比如说,如果想给Linux内核增加一种新的文件系统,用户并不需要全部重写内核代码,并且这一工作能够通过现有的代码的再使用相对容易的完成,文件系统被分为一个普遍意义上的虚拟文件系统VFS 和各种注册在VFS上的文件系统。内核的一切主要部分都是按照这种方式设计的。这就提高了整体上的模块化水平,新设备驱动程序的设计工作也因此而更容易的进行。
设备分类
设备驱动程序可以根据它们的行为分为不同的类型。两种最基本的类型是字符设备和块设备。字符设备以字节为单位进行读写,数据缓冲系统对它们访问不提供缓冲。而块设备则是另外一种情况,它们允许随机访问,每次读写的数据量都是数据块长度的整数倍,并且需要通过缓冲系统才能实现。
除网络设备之外,大多数设备驱动程序都需要通过文件系统进行访问。/dev 目录下充斥着各种设备特殊文件。
$ls -l /dev
…
crw-r----- 1 0 0 1, 1 Jan 1 00:00 mem
…
使这些文件和普通的文件看起来不一样的地方就是那些”b”和”c”,它们分别把对应的设备划分为一个字符设备或一个块设备。设备文件不一定要放在/dev目录下,但是这是一个传统做法,同时还有利于保持系统秩序。跟在设备文件owner 和group后面的那两个数字是设备的主编号与辅编号。主编号表明了这是哪种设备,辅编号表明了这是哪一个具体的设备。在上面的/dev目录中的mem主编号是1,辅编号是1。用户可以给设备起任意的名字,而内核则只关心它的主编号。用户可以使用如下命令来创建一个设备文件:
#mknod <name> <type> <major> <minor>
因此如果要创建出上面的mdsk文件,应该以root执行下面这个命令:
#mknod /dev/mdsk b 128 0
用户空间与内核空间
Linux运行在两种模式下,一种是用户模式,另外一种就是驱动程序的运行空间-内核模式,这在另外
一个方面反映出了CPU实际处理指令方式的改变-对于这两种模式的支持都是在其内部完成并转换的。在用户模式下无法直接访问硬件和执行某些指令。
在用户空间内的应用程序通常都要与glibc进行链接,而这些符号将在运行时候得到解析,但内核模块就不同了,它们将直接与内核进行链接,在使用它们自己向外界提供函数的方面是有限制的。一个作为内核模块编写出来的设备驱动
程序的运行并不是普通意义上的运行,模块中符号是在它被加载到内核去的时候得到解析的。
以下几点在编写内核模块的时候应该注意:
z不要使用浮点运算
z在驱动中不要进行繁忙的等待,内核中的用时一秒相当于整个系统都等待一秒
西安兵马俑z驱动程序在与硬件打交道的时候需要精确的定时苹果13粉版多被男性购买
z内核编译选项应该加上-D__KERNEL__ 和-DMODULE标志
z有些内核函数需要编译时的高优化级别支持,如outb需要-O2级别以上
z注意内核树目录的包含 –I/usr/local/src/linux-2.4.18
z注意防止内核模块被加载到一个与它不兼容的内核去,使用MODVERSIONS 定义检查函数版本,在设备驱动程序中需要包涵<Linux/modversions.h>文件z避免内核空间的函数名和变量冲突,在内核模块中需要将函数和全局变量声明为static
z永远注意所使用的数据类型和硬件设备的总线宽度的关系
催眠曲与设备驱动模块相关的基本命令
lsmod命令:
# /sbin/lsmod
Module Size Used by
ssiDAC3550a 4612 0 (unused)
i2c-ssi 1104 0 [ssiDAC3550a]
cs89x0 10768 1
… …
列出当前动态加载的模块清单,它会把当前插入的所有内核模块都列出来
insmod命令:
#/sbin/insmod hello.o
Using hello.o
大话西游上映时间init_module: Hello World!
…
…
把当前模块加载到内核空间中,它一般放在/sbin中并且只有root才能这样做,在Linux内核中root对应的uid和gid都是0,虽然无法使用getuidbyname这样类似的操作在BHU DBMX1 ADS上通过传递root获得当前用户的uid和gid,但实际上BHU DBMX1 ADS上的Linux直接将uid和gid都为0的用户作为登录后的默认用户,毕竟BHU DBMX1 ADS上的Linux是针对嵌入式应用设计的,而不是一个公用的网络服务器。
对应于insmod的操作命令是rmmod:
# /sbin/rmmod hello
cleanup_module: Hello World
……
它的作用是将一个已经加载到内核空间的驱动模块卸载掉,但在一种情况下它不会起作用:当前设备的使用计数不为0时。
dmesg命令
# dmesg | tail -n3
VFS: Can't find a romfs filesystem on dev 81:00.
感恩 作文init_module: Hello World!
cleanup_module: Hello World
怎么卸载显卡驱动通过dmesg的操作将获得内核缓冲区中的消息,通过管道操作获得最后3条信息的输出。
内核模块基本实现函数介绍
初始化模块函数init_module,将在insmod操作加载后首先执行:
int init_module(void),它负责设置模块内部的数据结构、初始化硬件、做好第一次使用设备之前所应该完成的工作。该函数需要在内核模块中自行实现。
关闭设备和释放设备可能占用过的各种资源,当调用rmmod操作时会执行cleanup_module函数:
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论