.1 Linux字符驱动程序结构
设备驱动程序实质上是一组完成不同任务的函数的集合,通过这些函数所提供的功能可以使得从设备接受输入和将输出送到设备就象读写文件一样,因此,Linux 中的每一个设备都具有文件的外在特征,都能使用open () ,close () ,read () ,write () 等系统调用.
系统调用是操作系统内核 和应用程序之间的接口,设备驱动程序硬件之间的接口.设备驱动程序为应用程序屏蔽了硬件的细节,这样在应用程序看来,硬件设备只是一个设备文件, 应用程序可以象操作普通文件一样对硬件设备进行操作.设备驱动程序是内核的一部分,它完成以下的功能:
1.对设备初始化和释放.
2.把数据从内核传送到硬件和从硬件读取数据.
3.读取应用程序传送给设备文件的数据和回送应用程序请求的数据.
4.检测和处理设备出现的错误.
在ARM平台上开发嵌入式Linux的设备驱动程序与在其他平台上开发是一样的。总的来说,实现一个嵌入式Linux设备驱动的大致流程如下:
1. 查看原理图,理解设备的工作原理
2. 定义主设备号
3. 在驱动程序中实现驱动的初始化。如果驱动程序采用模块的方式,则要实现模块初始化。
4. 设计所要实现的文件操作,定义file_operations结构。
5. 实现中断服务(中断并不是每个设备驱动所必须的)
6. 编译该驱动程序到内核中,或者用insmod命令加载
7. 测试该设备
字符设备驱动程序是linux系统最基本、最常用的驱动程序结构。可以说,只要不挂载文件系统的设备,都可以用字符设备去描述。在本节将以nvram字符设备,详细讲述linux字符驱动结构,一个完整的字符驱动构成主要是字符的初始化与卸载程序,提供给用户的文件接口程序,还有最主要的功能函数。
.2 nvram驱动的设计
nvram是一种非易失性随机访问存储器。编写驱动的第一步是定义驱动将要提供给用户程序的能力,nvram驱动最终实现的功能可以使用户自由的访问存储器,并且可以随机的读写存储器里面的内容,在驱动里把nvram地址映射到用户可以访问的位置,提供给用户读写和访问位置的接口,就可以实现这个设备的基本功能。
.3 结构体的定义
在系统内部,I/O设备的存/取通过一组固定的入口点来进行,这组入口点是由每个设备的设备驱动程序提供的。具体到Linux系统,设备驱动程序所提供的这组入口点由一个文件操作结构来向系统进行说明。file_operations结构定义于linux/fs.h文件中,随着内核的不断升级,file_operations结构也越来越大,不同版本的内核会稍有不同。
structfile_operations{
struct module *owner;
// 指向拥有该结构的模块的指针,避免正在操作时被卸载,初始化为THIS_MODULES
loff_t (*llseek) (struct file *, loff_t, int);
// llseek用来修改文件当前的读写位置,返回新位置
// loff_t为一个"长偏移量"。当此函数指针为空,seek调用将会以不可预期的方式修改file结构中的位置计数器。
ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
// 从设备中同步读取数据。读取成功返回读取的字节数。设置为NULL,调用时返回-EINVAL
ssize_t (*aio_read) (struct kiocb *, char __user *, size_t, loff_t);
// 初始化一个异步的读取操作,为NULL时全部通过read处理
ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
// 向设备发送数据。
ssize_t (*aio_write) (struct kiocb *, const char __user *, size_t, loff_t);
// 初始化一个异步的写入操作。
int (*readdir) (struct file *, void *, filldir_t);
// 仅用于读取目录,对于设备文件,该字段为 NULL
unsigned int (*poll) (struct file *, struct poll_table_struct *);
// 返回一个位掩码,用来指出非阻塞的读取或写入是否可能。
// 将pool定义为 NULL,设备会被认为即可读也可写。
int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);
// 提供一种执行设备特殊命令的方法。不设置入口点,返回-ENOTTY
long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
// 不使用BLK的文件系统,将使用此种函数指针代替ioctl
long (*compat_ioctl) (struct file *, unsigned int, unsigned long);
// 在64位系统上,32位的ioctl调用,将使用此函数指针代替
int (*mmap) (struct file *, struct vm_area_struct *);
// 用于请求将设备内存映射到进程地址空间。如果无此方法,将访问-ENODEV。
int (*open) (struct inode *, struct file *);
// 如果为空,设备的打开操作永远成功,但系统不会通知驱动程序
// 由VFS调用,当VFS打开一个文件,即建立了一个新的"struct file",之后调用open方法分配文件结构。open