Linux中国 Linux中国门户站!
设为主页 设为主页
收藏本站 收藏本站
 
当前位置 :首页 ->Linux技术 ->嵌入式系统 ->正文

Linux下PCI设备驱动程序开发

来源:IBM DW中国 作者:肖文鹏  时间:2007-04-22 点击: [收藏] [投稿]
2. 设备驱动程序接口

Linux中的I/O子系统向内核中的其他部分提供了一个统一的标准设备接口,这是通过include/linux/fs.h中的数据结构file_operations来完成的:


struct file_operations {
        struct module *owner;
        loff_t (*llseek) (struct file *, loff_t, int);
        ssize_t (*read) (struct file *, char *, size_t, loff_t *);
        ssize_t (*write) (struct file *, const char *, size_t, loff_t *);
        int (*readdir) (struct file *, void *, filldir_t);
        unsigned int (*poll) (struct file *, struct poll_table_struct *);
        int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);
        int (*mmap) (struct file *, struct vm_area_struct *);
        int (*open) (struct inode *, struct file *);
        int (*flush) (struct file *);
        int (*release) (struct inode *, struct file *);
        int (*fsync) (struct file *, struct dentry *, int datasync);
        int (*fasync) (int, struct file *, int);
        int (*lock) (struct file *, int, struct file_lock *);
        ssize_t (*readv) (struct file *, const struct iovec *, unsigned long, loff_t *);
        ssize_t (*writev) (struct file *, const struct iovec *, unsigned long, loff_t *);
        ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);
        unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
};

当应用程序对设备文件进行诸如open、close、read、write等操作时,Linux内核将通过file_operations结构访问驱动程序提供的函数。例如,当应用程序对设备文件执行读操作时,内核将调用file_operations结构中的read函数。

2. 设备驱动程序模块

Linux下的设备驱动程序可以按照两种方式进行编译,一种是直接静态编译成内核的一部分,另一种则是编译成可以动态加载的模块。如果编译进内核的话,会增加内核的大小,还要改动内核的源文件,而且不能动态地卸载,不利于调试,所有推荐使用模块方式。

从本质上来讲,模块也是内核的一部分,它不同于普通的应用程序,不能调用位于用户态下的C或者C++库函数,而只能调用Linux内核提供的函数,在/proc/ksyms中可以查看到内核提供的所有函数。

在以模块方式编写驱动程序时,要实现两个必不可少的函数init_module( )和cleanup_module( ),而且至少要包含<linux/krernel.h>和<linux/module.h>两个头文件。在用gcc编译内核模块时,需要加上-DMODULE -D__KERNEL__ -DLINUX这几个参数,编译生成的模块(一般为.o文件)可以使用命令insmod载入Linux内核,从而成为内核的一个组成部分,此时内核会调用模块中的函数init_module( )。当不需要该模块时,可以使用rmmod命令进行卸载,此进内核会调用模块中的函数cleanup_module( )。任何时候都可以使用命令来lsmod查看目前已经加载的模块以及正在使用该模块的用户数。

3. 设备驱动程序结构

了解设备驱动程序的基本结构(或者称为框架),对开发人员而言是非常重要的,Linux的设备驱动程序大致可以分为如下几个部分:驱动程序的注册与注销、设备的打开与释放、设备的读写操作、设备的控制操作、设备的中断和轮询处理。

  • 驱动程序的注册与注销

    向系统增加一个驱动程序意味着要赋予它一个主设备号,这可以通过在驱动程序的初始化过程中调用register_chrdev( )或者register_blkdev( )来完成。而在关闭字符设备或者块设备时,则需要通过调用unregister_chrdev( )或unregister_blkdev( )从内核中注销设备,同时释放占用的主设备号。

  • 设备的打开与释放

    打开设备是通过调用file_operations结构中的函数open( )来完成的,它是驱动程序用来为今后的操作完成初始化准备工作的。在大部分驱动程序中,open( )通常需要完成下列工作:

    1. 检查设备相关错误,如设备尚未准备好等。
    2. 如果是第一次打开,则初始化硬件设备。
    3. 识别次设备号,如果有必要则更新读写操作的当前位置指针f_ops。
    4. 分配和填写要放在file->private_data里的数据结构。
    5. 使用计数增1。

    释放设备是通过调用file_operations结构中的函数release( )来完成的,这个设备方法有时也被称为close( ),它的作用正好与open( )相反,通常要完成下列工作:

    1. 使用计数减1。
    2. 释放在file->private_data中分配的内存。
    3. 如果使用计算为0,则关闭设备。
  • 设备的读写操作

    字符设备的读写操作相对比较简单,直接使用函数read( )和write( )就可以了。但如果是块设备的话,则需要调用函数block_read( )和block_write( )来进行数据读写,这两个函数将向设备请求表中增加读写请求,以便Linux内核可以对请求顺序进行优化。由于是对内存缓冲区而不是直接对设备进行操作的,因此能很大程度上加快读写速度。如果内存缓冲区中没有所要读入的数据,或者需要执行写操作将数据写入设备,那么就要执行真正的数据传输,这是通过调用数据结构blk_dev_struct中的函数request_fn( )来完成的。

  • 设备的控制操作

    除了读写操作外,应用程序有时还需要对设备进行控制,这可以通过设备驱动程序中的函数ioctl( )来完成。ioctl( )的用法与具体设备密切关联,因此需要根据设备的实际情况进行具体分析。

  • 设备的中断和轮询处理

    对于不支持中断的硬件设备,读写时需要轮流查询设备状态,以便决定是否继续进行数据传输。如果设备支持中断,则可以按中断方式进行操作。

     如果您对本文有任何疑问或者建议,请到讨论区发表您的意见: >> 论坛入口 <<



    上一篇:Linux下的硬件驱动——USB设备(下)   下一篇:Linux串口上网的简单实现

    文章评论】 【收藏本文】 【推荐好友】 【打印本文】 【我要投稿】 【论坛讨论
更多相关文章
Power by linux-cn.com 粤ICP备05006655号