Chinaunix首页 | 论坛 | 博客
  • 博客访问: 135278
  • 博文数量: 38
  • 博客积分: 2510
  • 博客等级: 少校
  • 技术积分: 376
  • 用 户 组: 普通用户
  • 注册时间: 2010-05-07 22:44
文章分类
文章存档

2010年(38)

我的朋友

分类: LINUX

2010-05-08 15:34:29

转自《Linux设备驱动开发入门》
 
    现在我开始构建一个完整的设备驱动:memory.c。可以从这个设备读取和写入一个字符。虽然这个设备没时么用途,但提供了个很好的样例,它是一个完整的驱动;很容易实现,因为它不操作实际的硬件设备(它是电脑内部模拟的硬件)。

"memory":驱动的初始化

    要开发驱动,一些在设备驱动中很常见的#include声明,需要首先要加进来:
 

<memory initial> =
/* Necessary includes for device drivers */
#include <linux/init.h>
#include <linux/config.h>
#include <linux/module.h>
#include <linux/kernel.h> /* printk() */
#include <linux/slab.h> /* kmalloc() */
#include <linux/fs.h> /* everything... */
#include <linux/errno.h> /* error codes */
#include <linux/types.h> /* size_t */
#include <linux/proc_fs.h>
#include <linux/fcntl.h> /* O_ACCMODE */
#include <asm/system.h> /* cli(), *_flags */
#include <asm/uaccess.h> /* copy_from/to_user */
MODULE_LICENSE("Dual BSD/GPL");
/* Declaration of memory.c functions */
int memory_open(struct inode *inode, struct file *filp);
int memory_release(struct inode *inode, struct file
*filp);
ssize_t memory_read(struct file *filp, char *buf, size_t
count, loff_t *f_pos);
ssize_t memory_write(struct file *filp, char *buf,
size_t count, loff_t *f_pos);
void memory_exit(void);
int memory_init(void);
/* Structure that declares the usual file */
/* access functions */
struct file_operations memory_fops = {
read: memory_read,
write: memory_write,
open: memory_open,
release: memory_release
};
/* Declaration of the init and exit functions */
module_init(memory_init);
module_exit(memory_exit);
/* Global variables of the driver */
/* Major number */
int memory_major = 60;
/* Buffer to store data */
char *memory_buffer;


    在#include之后,就是即将定义的函数的声明。通用的用于处理文件的函数在file_operations里声明。这些在过后会讲解。接下来是初始化和卸载函数--在模块加载和卸载时执行--对内核声明。最后,是该驱动的全局变量声明:一个是“主设备号”,另外一个是内存指针,memory_buffer,将用于存储该驱动的数据。

"memory"驱动:连接到设备

    在UNIX和Linux中,设备可以用和文件一样的方式从用户空间访问。这些设备文件通常在/dev目录下。

    要把一般文件和内核模块链接在一起需要两个数据:主设备号和从设备号。主设备号用于内核把文件和它的驱动链接在一起。从设备号用于设备内部使用,为简单起见,本文并不对它进行解释。

    需要创建一个文件(该设备文件用于和设备驱动操作)

# mknod /dev/memory c 60 0


    其中,c说明创建的是字符设备,60是主设备号,0是从设备号。

    在这个驱动里,register_chrdev函数用于在内核空间,把驱动和/dev下设备文件链接在一起。它又三个参数:主设备号,模块名称和一个file_operations结构的指针。在安装模块时将调用该函数:

<memory init module> =
int memory_init(void) {
    int result;
    /* Registering device */
    result = register_chrdev(memory_major, "memory",
            &memory_fops);
    if (result < 0) {
        printk(
                "<1>memory: cannot obtain major number %d\n",
                memory_major);
        return result;
    }
    /* Allocating memory for the buffer */
    memory_buffer = kmalloc(1, GFP_KERNEL);
    if (!memory_buffer) {
        result = -ENOMEM;
        goto fail;
    }
    memset(memory_buffer, 0, 1);
    printk("<1>Inserting memory module\n");
    return 0;
fail:
    memory_exit();
    return result;
}

    以上代码使用了kmalloc函数。这个函数工作在内核空间,用于为该驱动程序的缓冲区分配内存。它和我们熟悉的malloc函数很相似。最后,如果注册主设备号或者分配内存失败,模块将退出。

"memory"驱动:卸载驱动

    为通过memory_exit函数卸载模块,需要定义unregsiter_chrdev函数。这将释放驱动之前向内核申请的主设备号。

<memory exit module> =
void memory_exit(void) {
    /* Freeing the major number */
    unregister_chrdev(memory_major, "memory");
    /* Freeing buffer memory */
    if (memory_buffer) {
        kfree(memory_buffer);
    }
    printk("<1>Removing memory module\n");
}

    为了完全的卸载该驱动,缓冲区也需要通过该函数进行释放。

"momory"驱动:像文件一样打开设备

    内核空间打开文件的函数是open,和用户空间打开文件的函数fopen对应:在file_operations结构里,用于调用register_chrdev。在本例里,是memory_open函数。它又几个参数:一个inode结构,该结构向内核发送主设备号和从设备号的信息;另外是一个file结构,用于说明,该设备文件允许哪些操作。所有这些函数在本文中都未做深入的讲解。

    当设备文件被打开后,通常就需要初始化驱动的各个变量,对设备进行复位。但在本例中,这些操作都没进行。

    <memory open> =
int memory_open(struct inode *inode, struct file *filp)
{
    /* Success */
    return 0;
}

表1
设备驱动事件和在内核空间和用户空间之间实现该功能的函数
Events User functions Kernel functions
Load module insmod module_init()
Open device fopen file_operations: open
Read device
Write device
Close device
Remove modulermmod module_exit()

"momory"驱动:像文件一样关闭设备

    在内核空间里,和用户空间里关闭文件的fclose对应的函数是release:它也是file_operations结构体的成员,用于调用register_chrdev。本例中,它是函数memory_release,和上面的相似,它也有inode和file两个参数。

    当设备文件关闭后,通常需要释放该设备使用的内存,释放各种操作该设备相关的变量。但是,为简单起见,例子里没有进行这些操作。

<memory release> =
int memory_release(struct inode *inode, struct file
        *filp) {
    /* Success */
    return 0;
}

表2.
设备驱动事件和在内核空间和用户空间之间实现该功能的函数
Events User functions Kernel functions
Load module insmod module_init()
Open device fopen file_operations: open
Read device
Write device
Close device fclose file_operations: release
Remove modulermmod module_exit()

"momory"驱动:读取设备

    和用户空间函数fread类似,内核空间里,读取设备文件使用read函数:read是file_operations的成员,用于调用register_chrdev。

    本例中,是memory_read函数。它的参数有:一个file结构;一个缓冲区(buf),用户空间的fread函数将从该缓冲区读数据;一个记录要传输的字节数量的计数器(count),它和用户空间的fread使用的计数器值相同;最后一个参数(f_pos)指示从哪里开始读取该设备文件。

    本例中,memory_read函数通过copy_to_user函数从驱动的缓冲区
(memory_buffer)向用户空间传送一个简单的字节:

<memory read> =
ssize_t memory_read(struct file *filp, char *buf,
        size_t count, loff_t *f_pos) {
    /* Transfering data to user space */
    copy_to_user(buf,memory_buffer,1);
    /* Changing reading position as best suits */
    if (*f_pos == 0) {
        *f_pos+=1;
        return 1;
    } else {
        return 0;
    }
}

    设备文件的读取位置(f_pos)也改变了。如果起始点是文件的开头,那么f_pos的值将增加1,如果要读取的字节读取正常,则返回值为1。如果读取位置不是文件开头,则是文件的末尾,返回值将是0,(因为文件只存储了1个字节)。

表3. 设备驱动事件和在内核空间和用户空间之间实现该功能的函

Events User functions Kernel functions
Load module insmod module_init()
Open device fopen file_operations: open
Read device fread file_operations: read
Write device
Close device fclose file_operations: release
Remove modulesrmmod

"momory"驱动:向设备写数据

    和用户空间里写文件的fwrite对应,内核空间里是write:write是file_operations的成员,用于调用register_chrdev。本例中是memory_write函数,它有如下几个参数:一个file结构;buf,一个缓冲区,用户空间函数fwrite将向该该缓冲区写数据;count,统计将传送的字节数的计数器,和用户空间函数fwrite的计数器有相同的数值;最后是f_pos,指示从哪里开始写文件。

<memory write> =
ssize_t memory_write( struct file *filp, char *buf,
        size_t count, loff_t *f_pos) {
    char *tmp;
    tmp=buf+count-1;
    copy_from_user(memory_buffer,tmp,1);
    return 1;
}

    本例中,函数copy_from_user从用户空间传送数据到内核空间。

表4
设备驱动事件和在内核空间和用户空间之间实现该功能的函数
Events User functions Kernel functions
Load module insmod module_init()
Open device fopen file_operations: open
Close device fread file_operations: read
Write device fwrite file_operations: write
Close device fclose file_operations: release
Remove modulermmod module_exit()

完整的"memory"驱动

    把以上各部分代码整合起来,一个完整的驱动就完成了:

<memory.c> =
<memory initial>
<memory init module>
<memory exit module>
<memory open>
<memory release>
<memory read>
<memory write>

    在该模块使用之前,你需要和刚才那个模块一样,进行模块编译。编译好后,用以下命令进行加载:

# insmod memory.ko

    并且最后是取出设备文件的保护:

# chmod 666 /dev/memory

    如果以上步骤一切正常,此时你就可以向设备/dev/memory写一串字符,并且它将把你写入的最后一个字符存储起来。你可以按下例操作:

$ echo -n abcdef >/dev/memory

    使用cat检查设备的内容:

$ cat /dev/memory


    存储的字符将不会改变,直到该字符被覆盖,或者是该模块被卸载。
 
源代码:
文件: memory.zip
大小: 7KB
下载: 下载
阅读(1888) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~