QQ登录

只需一步,快速开始

 找回密码
 注册

QQ登录

只需一步,快速开始

查看: 3399|回复: 18

编写一个简单的驱动程序

  [复制链接]
发表于 2003-5-25 22:26:51 | 显示全部楼层 |阅读模式
一段时间以前,我做了个很简单的驱动程序,dragonfly老大发出最高指示要我把经过写下来,这几天终于有空可以完成dragonfly老大的最高指示。
        先说一下前言:
        1、大家互相学习,如有不对的地方,敬请指教!
        2、我将维护这个帖子,如有错误请及时通知我,我将更正;如有问题请直接询问,我将尽力回答。
        3、鉴于结合代码讲比较好,我做了个很简单的驱动,加载模块后将产生一个设备,通过这个设备可以得到当前时间。

        现在开始。首先是准备工作:
        1、内核以 vanilla 2.4.20 为准,你如果用的不是这个内核,请您在kernel.org下载并编译使用这个版本的内核。您必须把2.4.20的原代码放在/usr/src目录下,通常情况下,解压缩后会产生一个 叫做linux-2.4.20的目录,这时,请确定在/usr/src目录下有个名字叫做linux的链接指向那个目录。如果您不知道怎么编译并使用内核,那抱歉,请您先学会编译使用新内核,在本版有详细的讨论。
        2、为了方便和紧跟时代潮流,本帖子所用的例子要求必须使用devfs。关于devfs的详细安装和使用方法请看:
        http://www-900.ibm.com/developerWorks/cn/linux/filesystem/l-fs4/index.shtml
        http://www-900.ibm.com/developerWorks/cn/linux/filesystem/l-fs5/index.shtml
        我对这个要求对您带来的不便表示歉意。但无论如何,devfs都是值得尝试和使用的。

        下面是代码。一共三个文件:Makefile,dragonfly.c,df.h。把三个文件放到同一目录,使用make命令进行编译。为了充分表示对dragonfly斑竹的尊敬,我把产生的设备名字叫做dragonfly,自然的,编译后的目标文件名为dragonfly.o。您可以运行命令insmod dragonfly.o把模块插入内核,若您按照要求使用了devfs,那么将在/dev下产生目录linuxfans,在linuxfans下将产生设备文件dragonfly。这里先帖出代码,我将在代码后面的帖子中说明。
 楼主| 发表于 2003-5-25 22:27:58 | 显示全部楼层

df.h

df.h
[code:1]
/* Use 'k' as magic number */
#define DF_IOC_MAGIC  'k'

#define DF_IOCRESET                        _IO(DF_IOC_MAGIC, 0)
#define DF_GETTIME                        _IO(DF_IOC_MAGIC, 1)
[/code:1]
回复

使用道具 举报

 楼主| 发表于 2003-5-25 22:28:35 | 显示全部楼层

dragonfly.c

dragonfly.c
[code:1]
#ifndef __KERNEL__
#  define __KERNEL__
#endif
#ifndef MODULE
#  define MODULE
#endif

#include <linux/config.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/time.h>
#include <linux/devfs_fs_kernel.h>

#include "df.h"

int df_open(struct inode *inode, struct file *filp){
        return 0;
}
int df_release(struct inode *inode, struct file *filp){
        return 0;
}

int df_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg){
        struct timeval time;
       
        switch(cmd){
        case DF_IOCRESET:
                while (MOD_IN_USE)
                        MOD_DEC_USE_COUNT;
                MOD_INC_USE_COUNT;
                break;
        case DF_GETTIME:
                do_gettimeofday(&time);
                return(time.tv_sec);
                break;
        default:
                return -ENOTTY;
        }
       
        return 0;
}

struct file_operations df_fops = {
        ioctl:                df_ioctl,
        open:                df_open,
        release:        df_release,
};

devfs_handle_t df_devfs_dir;
devfs_handle_t de;

int df_init(){
       
        EXPORT_NO_SYMBOLS;

        SET_MODULE_OWNER(&df_fops);
       
        df_devfs_dir = devfs_mk_dir(NULL, "linuxfans", NULL);
        if (!df_devfs_dir) return -EBUSY;

        de = devfs_register(df_devfs_dir, "dragonfly",
                        DEVFS_FL_AUTO_DEVNUM,
                        0, 0, S_IFCHR | S_IRUGO | S_IWUGO,
                        &df_fops,
                        NULL);

        return 0;
}
       
void df_cleanup(){
        devfs_unregister(de);
        devfs_unregister(df_devfs_dir);
}

module_init(df_init);
module_exit(df_cleanup);
MODULE_LICENSE("GPL");
[/code:1]
回复

使用道具 举报

 楼主| 发表于 2003-5-25 22:29:21 | 显示全部楼层

Makefile

Makefile
[code:1]
CFLAGS +=  -Wall -O2 -D__KERNEL__ -DMODULE -I/usr/src/linux/include

CC        =gcc

all: dragonfly.o

clean:
        rm -f *.o *~ .*~ core
[/code:1]
注意:
最后一行的 “rm -f *.o *~ .*~ core” 前面是一个tab键,而不是这里的三个空格。论坛把tab变成了三个空格,我也没办法,大家自己改一下吧,不然无法使用 make clean

(note by dragonfly: tab key is the new command line begin flag in makefile, readers can check makefile syntax to know more)
回复

使用道具 举报

 楼主| 发表于 2003-5-25 22:37:55 | 显示全部楼层
说明1:
本帖子无意替代伟大的《linux设备驱动程序》,事实上那本书是无法被替代的。本帖子的内容大部分建立在那本书上,如果您要在本文和那本书中选择更正确的做法,那我的建议是您应该选择伟大的《linux设备驱动程序》。同时我也建议您也应该有那本书。如果您不知道那是本什么书,那么您可以在google上搜索。
回复

使用道具 举报

 楼主| 发表于 2003-5-25 22:50:22 | 显示全部楼层

说明2

说明2:
下面的例子是对所产生设备的使用,一共两个,df.h和test.c,df.h就是前面驱动中的文件,您需要把这两个文件放在同一个目录中,然后使用 gcc -o test test.c 来编译:

df.h
[code:1]
/* Use 'k' as magic number */
#define DF_IOC_MAGIC  'k'

#define DF_IOCRESET                        _IO(DF_IOC_MAGIC, 0)
#define DF_GETTIME                        _IO(DF_IOC_MAGIC, 1)
[/code:1]

test.c:
[code:1]
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <time.h>
#include <sys/ioctl.h>
#include "df.h"

main()
{
        int                fd;
        unsigned long        xtm;
        struct tm         *res;

        fd = open("/dev/linuxfans/dragonfly", O_RDONLY);
        xtm = ioctl(fd, DF_GETTIME);
        close(fd);

//        time_t tmp;
//        tmp = time(NULL);
//        printf("epoch:%x\n", tmp);
//        printf("hex:  %x\n", xtm);

        res = gmtime(&xtm);

        printf("time: %d:%d:%d\n", res->tm_hour, res->tm_min, res->tm_sec);
}
[/code:1]
回复

使用道具 举报

发表于 2003-5-25 23:01:07 | 显示全部楼层
1) thanks a lot for such a clear post
2) ft, i dare not 最高指示 you, i only discuss with you.  
3) if u really want to use the "dragonfly" as the module name, i will prefer to use dragonfly.c instead of main.c. like kernel code, if u have a e100.o, u always use a e100.c. main.c is not a meaningful name.
4) in open/close, why not add MOD_INC_USE_COUNT;/MOD_DEC_USE_COUNT;
5) call do_gettimeofday is more accurate than access xtime directly.
6) thanks again
回复

使用道具 举报

 楼主| 发表于 2003-5-25 23:12:56 | 显示全部楼层
3)我已经改了
4)2.4内核好象能够自动维护模块计数器,我试了一次,当在open 里面加了MOD_INC_USE_COUNT,一打开设备lsmod看到模块被使用的次数是两次
5)我不知道那个函数(丢人)
回复

使用道具 举报

发表于 2003-5-25 23:31:23 | 显示全部楼层
4) oh? i donot seriously check it.
5) try to read some code built by others, u will know many. search linux kernel on sourceforge and freshmeat, u can find many linux kenrel related projects. then u can read what u feel interested.
回复

使用道具 举报

 楼主| 发表于 2003-5-26 00:20:56 | 显示全部楼层
多谢,freshmeat是看了您上次那几个关于kernel patch的帖子里才知道的
呵呵
回复

使用道具 举报

 楼主| 发表于 2003-5-26 00:21:13 | 显示全部楼层
说明3:
对于main.c的解释:
#ifndef __KERNEL__
#  define __KERNEL__
#endif
#ifndef MODULE
#  define MODULE
#endif
这是必须要的,一定要加
#include <linux/config.h>
#include <linux/module.h>
#include <linux/kernel.h>
同样是必须的
#include <linux/time.h>
用到了struct timeval
#include <linux/devfs_fs_kernel.h>
devfs所需要的

df_open、df_release、df_ioctl三个函数是指定了对于设备文件/dev/linuxfans/dragonfly的操作,这里实现了三种操作:打开设备文件,关闭设备文件、ioctl操作。
df_open()和df_release():2.4内核自动维护模块计数器(就是lsmod命令看到的模块被使用的次数),所以我没有在里面加上MOD_INC_USE_COUNT和MOD_DEC_USE_COUNT。如果你使用老内核,你应该加上这两个宏《linux设备驱动程序》第44、78、84、107页描述到了这个问题。
struct file_operations df_fops = {
   ioctl:      df_ioctl,
   open:      df_open,
   release:   df_release,
};
如你所见,这个结构的成员分别是上面提到的三个函数的指针。内核依靠这个结构访问三个函数。struct file_operations定义在linux/fs.h
EXPORT_NO_SYMBOLS
不输出任何内核符号,关于内核符号请看《linux设备驱动程序》,网上也有相当多的文章讨论。
SET_MODULE_OWNER
我只知道它是必须的,看这个宏的名字也知道它的用处,但我不知道它的具体情况(dragonfly老大讲解一下)
df_devfs_dir = devfs_mk_dir(NULL, "linuxfans", NULL);
在/dev/目录下产生linuxfans目录
de = devfs_register(df_devfs_dir, "dragonfly",
         DEVFS_FL_AUTO_DEVNUM,
         0, 0, S_IFCHR | S_IRUGO | S_IWUGO,
         &df_fops,
         NULL)
在/dev/linuxfans下产生设备文件dragonfly,这个函数的参数大家自己在原码里面查一下,我懒得说了
df_cleanup()中的两个函数:分别删除在/dev目录中创建的目录和设备文件
最后三行所有驱动都是这样写的,不说了
简单说到这里。
回复

使用道具 举报

 楼主| 发表于 2003-5-26 00:30:46 | 显示全部楼层
说明4:
这个例子限定了内核版本(2.4.20)和条件(devfs),所以这里没有考虑任何错误处理和移植问题。这是为了尽量简洁。大家将来如果真要做驱动,可不能这么简单了,要考虑好多问题的。
回复

使用道具 举报

 楼主| 发表于 2003-5-26 01:06:35 | 显示全部楼层
Makefile 已更新
dragonfly.c中已经改为使用do_gettimeofday()得到时间。
回复

使用道具 举报

发表于 2003-5-26 03:04:52 | 显示全部楼层
if u have this '-D__KERNEL__ -DMODULE' in makefile, u need not that two ifndef in c code. but keeping them is good since u may change u makefile by mistake.
回复

使用道具 举报

发表于 2003-5-26 03:10:40 | 显示全部楼层
[quote:357adaf9a0="keenor"]多谢,freshmeat是看了您上次那几个关于kernel patch的帖子里才知道的
呵呵[/quote]

pls do not use 您, use "li" only. i am not so old hehe. keep peer to peer is better. we come here to discuss problems. i also learn many by discussing here.

my only purpose here is to popularize the linux use in china. depends less on pirate software and windows.
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

GMT+8, 2024-6-3 23:53 , Processed in 0.126011 second(s), 16 queries .

© 2021 Powered by Discuz! X3.5.

快速回复 返回顶部 返回列表