苟培培 郭麦成 孙静 王涛
摘要本文首先介绍了在嵌入式Linux环境下设备驱动程序的开发流程,详细描述了Linux设备驱动程序的体系结构和Linux设备文件的概念。然后通过一个例子描述了如何设计和编写Linux设备驱动程序,并解释了其中的核心代码。最后构建一个Glade工程调用这个设备驱动程序,完成测试工作。
关键词设备文件,设备驱动 ,Glade
中图法分类:TP311.11
一、 引言
Linux是开放源代码的操作系统,由于其具有高效稳定、执行速度快、实现了真正的多任务、多用户环境、强大的网络功能、较好的可裁减性与移植性等特点,在嵌入式系统领域获得了飞速的发展。针对ARM体系结构CPU开发的具有MMU功能的嵌入式Linux操作系统无疑是ARM平台上操作系统的最佳选择。本文主要研究了Linux下进行开发驱动程序并构建一个GUI程序来使用这个驱动程序的一般流程。
二、 Linux设备驱动程序
Linux支持三类硬件设备:字符设备、块设备和网络设备。字符设备是指无须缓存直接按字节读写的设备。块设备是以块为单位进行读写,能够进行随机访问。网络设备在Linux里做专门的处理,它没有映射到文件系统的设备节点,对它的访问采用socket机制实现。字符设备与块设备的主要区别是:在对字符设备发出读/写请求时,实际的硬件I/O一般紧接着发生。块设备则不然,它利用一块系统内存作缓冲区来进行实际的I/O操作。
图1 驱动程序结构
在Linux中,几乎所有的内容都是文件,对设备驱动的访问也是以文件操作的方式实现。无论是字符设备还是块设备,用户对设备的操作都是通过虚拟文件系统(VFS)转化为设备驱
动与硬件操作例程的交互。即使是访问网络设备的socket接口,也是通过VFS实现的。Linux通过VFS为用户提供了一个统一的设备访问接口,使用户能够透明地访问设备驱动程序如图1所示。所有的硬件设备都可以使用和操作文件标准的系统调用接口来打开、关闭、读写和I/O控制操作,而驱动程序的主要任务就是实现这些系统函数的调用。Linux系统中的所有硬件设备都使用一种特殊的设备文件来表示。每个设备文件都有两个设备号:一个是主设备号,它用来标识该设备的种类,也标识该设备使用的驱动程序;另一个是次设备号,用来标识使用同一设备驱动程序的不同硬件设备。
实现一个嵌入式Linux设备驱动的大致流程如下:
定义主、次设备号,也可以动态获取;实现驱动初始化和清除函数,如果驱动程序采用模块方式,则要实现模块初始化和清除函数;设计所要实现的文件操作,定义file_operations 结构;实现所需的文件操作调用,如read、write 等;实现中断服务函数,并用request_irq 向内核注册。中断并不是每个设备驱动都需要;将驱动编译到内核或编译成模块,用insmod 命令加载;生成设备节点文件。
下面以一个简单的例子来说明设备驱动程序的结构,这个驱动程序是用来控制目标板上的一组LED。led_fops结构体定义了该设备需要的操作接口。它的成员全部是函数指针,所以实质上就是函数跳转表。
struct file_operations led_fops = {
open: led_open, /*打开设备操作 */
read: led_read, /* 读设备操作 */
write: led_write, /* 写设备操作 */
ioctl: led_ioctl, /* 控制模块的设置 */
release: led_release, /*释放设备操作 */
};
ssize_t led_read(struct file *filp, char *Putbuf, size_t length, loff_t *f_pos)
{ unsigned short BottonStatus;
unsigned char Bottontmp = 0;
int i;
BottonStatus = (KEY_CS & 0xff ); /*获取当前8个按键的状态*/
for(i = 0 ; i < 8; ++i)
{ if( ((BottonStatus >> i) & 1) == 0 )
Bottontmp = (i+1);
}
copy_to_user( Putbuf,&Bottontmp, length);
/*将内核空间的数据复制到用户空间*/
return length;
}
ssize_t led_write(struct file *filp, const char *Getbuf, size_t length, loff_t *f_pos)
{ int num;
unsigned char UsrWantLed;
copy_from_user(&UsrWantLed, Getbuf, length);
/*将用户空间的写入到设备文件的内容传送到内核空间 */
num = ( (UsrWantLed) & 0xff );
LED_CS=~(1 << (num-1)); /*通过对LED_CS地址赋值来控制LED的亮灭 */
return (0);
}
|