内核模块如何调用设备驱动
时间:2009-08-01 来源:red_eyed_hare
最近做Linux下驱动程序遇到了不少问题。Linux下驱动程序的设计,参考的书籍有《Linux设备驱动》第二版和第三版。但是书籍上说的驱动程序的编写都是怎么供用户态使用,即用于内核和用户态的交互。但是现在做项目遇到了,在静态编译进内核的程序中调用驱动程序,这涉及了怎么在内核中调用驱动程序。查了很多书籍都没有介绍这方面的知识。经过几个星期的努力,不断上网找资料,终于实现了怎么在内核模块中调用驱动程序,下面和大家一起分享一下经验,希望对大家有帮助。同时我还有几个问题没有解决,望大家提点意见,谢了。
下面是我参考书籍写的一个简单的字符设备驱动程序,如下:
#include<linux/module.h>
#include<linux/init.h>
#include<linux/fs.h>
#include<asm/uaccess.h>
#include<asm/semaphore.h>
#include <asm/irq.h>
#include <asm/io.h>
#include"chardev.h"
MODULE_LICENSE("GPL"); #define MAJOR_NUM 250// ssize_t bao_read (struct file*,char*,size_t,loff_t*);
ssize_t bao_write (struct file*,const char*,size_t,loff_t*);
int bao_ioctl(struct inode *inode,struct file*filp,unsigned int cmd,unsigned long args);
int bao_open(struct inode *inode,struct file *filp);
int bao_release(struct inode *inode,struct file *filp); //file_operations
struct file_operations bao_fops=
{
read:bao_read,
write:bao_write,
ioctl:bao_ioctl,
open:bao_open,
release:bao_release,
owner:THIS_MODULE,
};
static int bao_var=0;//baovar
static int bao_count=0;
static struct semaphore sem;
static spinlock_t spin=SPIN_LOCK_UNLOCKED; int __init bao_init(void)
{
int ret;
//
ret=register_chrdev(MAJOR_NUM,"baovar",&bao_fops);
if(ret)
{
printk("baovar register failure\n");
}
else
{
printk("baovar register success\n");
init_MUTEX(&sem);
}
return ret;
} void __exit bao_exit(void)
{
int ret;
//
ret=unregister_chrdev(MAJOR_NUM,"baovar");
if(ret)
{
printk("baovar unregister failure\n");
}
else
{
printk("baovar unregister success\n");
}
} int bao_open(struct inode *inode,struct file *filp)
{
//
spin_lock(&spin);
//
if(bao_count)
{
spin_unlock(&spin);
return -EBUSY;
}
printk("baoopen\n");
bao_count++;
// spin_unlock(&spin);
return 0;
} int bao_release(struct inode *inode,struct file *filp)
{
bao_count--;
printk("close->bao\n");
return 0;
}
ssize_t bao_read(struct file*filp,char *buf,size_t len,loff_t *off)
{
//
if(down_interruptible(&sem))
{
return -ERESTARTSYS;
}
//
if(__copy_to_user(buf,&bao_var,sizeof(int)))
{
up(&sem);
return -EFAULT;
}
up(&sem);
return sizeof(int);
} ssize_t bao_write(struct file*filp,const char *buf,size_t len,loff_t *off)
{
//
if(down_interruptible(&sem))
{
return -ERESTARTSYS;
}
//
if(__copy_from_user(&bao_var,buf,sizeof(int)))
{
up(&sem);
return -EFAULT;
}
//
up(&sem);
return sizeof(int);
}
int bao_ioctl(struct inode *inode,struct file*filp,unsigned int cmd,unsigned long args)
{
if(_IOC_TYPE(cmd)!=BAO_IOCTL){return -EINVAL;}
if(_IOC_NR(cmd)>BAO_IOCTL_MAXNR){return -EINVAL;}
int ret;
switch(cmd)
{
case IOCTL_READ:
return __put_user(bao_var, (int *) args);
case IOCTL_WRITE:
{ret = __get_user(bao_var, (int *) args);
if (ret)
return ret;
printk("bao_var = %d\n", bao_var);
break; }
default:printk("error\n");
}
return 1;
} module_init(bao_init);
module_exit(bao_exit); 以上是字符设备驱动模块 chardev.h头文件的内容如下: #include <linux/ioctl.h> #define BAO_IOCTL 't'
#define IOCTL_READ _IOR(BAO_IOCTL, 0, int)
#define IOCTL_WRITE _IOW(BAO_IOCTL, 1, int)
#define BAO_IOCTL_MAXNR 1 在Makefile文件的内容如下: CC=gcc
MODCFLAGS := -Wall -DMODULE -D__KERNEL__ -DLINUX -I/usr/src/linux-2.4.20-8/include
chardev.o :chardev.c
$(CC) $(MODCFLAGS) -c chardev.c
echo insmod chardev.o to turn it on
echo rmmod chardev to turn it off
echo 在bao_load.sh Shell文件中的内容如下: #!/bin/bash
#bao.sh
make
/sbin/insmod chardev.o
cat /proc/devices
mknod /dev/baovar c 250 0
echo "finish" 在终端下运行sh bao_load.sh或./bao_load.sh 就可以加载模块并建立设备节点。 在bao_unload.sh 内容如下: #!/bin/bash
#bao.sh
rm -i /dev/baovar
/sbin/rmmod chardev
cat /proc/devices
echo "finish" 同样运行此Shell文件便可以卸载设备。 上面说的很详细了,在Linux red hat 9.0下成功加载。 下面是另一内核模块,在此模块中调用了以上驱动程序,成功的对设备进行操作: #include <linux/module.h>
#define __KERNEL_SYSCALLS__
#ifdef MODVERSIONS
#include <linux/modversion.h>
#endif
#include <linux/smp_lock.h>
#include <linux/sched.h>
#include <linux/file.h>
#include <linux/unistd.h>
#include <asm/io.h>
#include <linux/fs.h>
#include <linux/kernel.h>
#include <asm/fcntl.h>
#include <asm/uaccess.h>
#include <asm/errno.h>
#include "chardev.h"
static inline _syscall3(int, ioctl, int ,fd,int, cmd,int*, arg); MODULE_LICENSE("GPL");
static char buf1[20];
static char buf2[20];
#define BEGIN_KMEM { mm_segment_t old_fs = get_fs(); set_fs(get_ds());
#define END_KMEM set_fs(old_fs); }
int errno; int init_module(void){
int err;
int put,get;
put=113579;
sprintf(buf1,"%s","baoqunmin");
printk("<1>Hello,world\n");
BEGIN_KMEM;
err=open("/dev/baovar",O_RDWR,0);
if(err>0)
printk("open file successful\n");
write(err,buf1,sizeof(buf1));
read(err,buf2,sizeof(buf2));
buf2[20]='\n';
printk("buf2-->%s\n",buf2);
ioctl(err,IOCTL_WRITE,&put);
ioctl(err,IOCTL_READ,&get);
printk("get-->%d\n",get);
END_KMEM;
return 0;
}
void cleanup_module(void){
printk("<1>Goodbye cruel word\n");
} Makefile文件如下:
CC=gcc
MODCFLAGS := -Wall -DMODULE -D__KERNEL__ -DLINUX -I/usr/src/linux-2.4.20-8/include
bao.o :bao.c
$(CC) $(MODCFLAGS) -c bao.c
echo insmod bao.o to turn it on
echo rmmod bao to turn it off
echo
加载和卸载过程如驱动模块的操作。 操作过程如下: 1.运行bao_load.sh文件 2.make 第二个模块 3./sbin/insmod bao.o 加载第二个模块调用驱动 4.dmesg 显示调用驱动的打印结果 5./sbin/rmmod bao 卸载模块 6.运行bao_unload.sh卸载驱动 以上都在Linux red hat 9.0下成功完成。 从以上程序可以看出,在第二个模块中成功的使用了open,write,read,ioctl。 其实这些都是供用户态使用的函数,这里引进了 #define BEGIN_KMEM { mm_segment_t old_fs = get_fs(); set_fs(get_ds());
#define END_KMEM set_fs(old_fs); } 成功的使用了这些函数,其次ioctl的系统调用如上。 以上完成了在内核模块中调用驱动详细过程,笔者还有几个问题不明白: 1.以上模块中调用驱动,用的open等函数,可以成功打开设备,操作设备, 以上只是简单的调用,我在做加密卡驱动时,用以上方法可以对加密卡进行正确操作,成功加密, 但是我把第二个模块静态编译到freeswan中调用,一运行freeswan就死机。最后不做任何工作,在内核 编译进内核时,只使用了一个open函数,还是死机。 为什么模块间调用的时,能正确操作,静态编译到内核的时候就会死机,问题还未解决? 2.上网查了资料说是内核可以用sys_open,但我在写模块的时候,编译没问题,一加载就出现 unresolved symbol sys_open错误,模块无法加载。但是同样的程序放入内核,只用sys_open 能打开设备,其他的都换了sys_xx,就死机。现在都不知道是什么问题导致死机。 希望大家能给点意见,谢了!
#include<linux/init.h>
#include<linux/fs.h>
#include<asm/uaccess.h>
#include<asm/semaphore.h>
#include <asm/irq.h>
#include <asm/io.h>
#include"chardev.h"
MODULE_LICENSE("GPL"); #define MAJOR_NUM 250// ssize_t bao_read (struct file*,char*,size_t,loff_t*);
ssize_t bao_write (struct file*,const char*,size_t,loff_t*);
int bao_ioctl(struct inode *inode,struct file*filp,unsigned int cmd,unsigned long args);
int bao_open(struct inode *inode,struct file *filp);
int bao_release(struct inode *inode,struct file *filp); //file_operations
struct file_operations bao_fops=
{
read:bao_read,
write:bao_write,
ioctl:bao_ioctl,
open:bao_open,
release:bao_release,
owner:THIS_MODULE,
};
static int bao_var=0;//baovar
static int bao_count=0;
static struct semaphore sem;
static spinlock_t spin=SPIN_LOCK_UNLOCKED; int __init bao_init(void)
{
int ret;
//
ret=register_chrdev(MAJOR_NUM,"baovar",&bao_fops);
if(ret)
{
printk("baovar register failure\n");
}
else
{
printk("baovar register success\n");
init_MUTEX(&sem);
}
return ret;
} void __exit bao_exit(void)
{
int ret;
//
ret=unregister_chrdev(MAJOR_NUM,"baovar");
if(ret)
{
printk("baovar unregister failure\n");
}
else
{
printk("baovar unregister success\n");
}
} int bao_open(struct inode *inode,struct file *filp)
{
//
spin_lock(&spin);
//
if(bao_count)
{
spin_unlock(&spin);
return -EBUSY;
}
printk("baoopen\n");
bao_count++;
// spin_unlock(&spin);
return 0;
} int bao_release(struct inode *inode,struct file *filp)
{
bao_count--;
printk("close->bao\n");
return 0;
}
ssize_t bao_read(struct file*filp,char *buf,size_t len,loff_t *off)
{
//
if(down_interruptible(&sem))
{
return -ERESTARTSYS;
}
//
if(__copy_to_user(buf,&bao_var,sizeof(int)))
{
up(&sem);
return -EFAULT;
}
up(&sem);
return sizeof(int);
} ssize_t bao_write(struct file*filp,const char *buf,size_t len,loff_t *off)
{
//
if(down_interruptible(&sem))
{
return -ERESTARTSYS;
}
//
if(__copy_from_user(&bao_var,buf,sizeof(int)))
{
up(&sem);
return -EFAULT;
}
//
up(&sem);
return sizeof(int);
}
int bao_ioctl(struct inode *inode,struct file*filp,unsigned int cmd,unsigned long args)
{
if(_IOC_TYPE(cmd)!=BAO_IOCTL){return -EINVAL;}
if(_IOC_NR(cmd)>BAO_IOCTL_MAXNR){return -EINVAL;}
int ret;
switch(cmd)
{
case IOCTL_READ:
return __put_user(bao_var, (int *) args);
case IOCTL_WRITE:
{ret = __get_user(bao_var, (int *) args);
if (ret)
return ret;
printk("bao_var = %d\n", bao_var);
break; }
default:printk("error\n");
}
return 1;
} module_init(bao_init);
module_exit(bao_exit); 以上是字符设备驱动模块 chardev.h头文件的内容如下: #include <linux/ioctl.h> #define BAO_IOCTL 't'
#define IOCTL_READ _IOR(BAO_IOCTL, 0, int)
#define IOCTL_WRITE _IOW(BAO_IOCTL, 1, int)
#define BAO_IOCTL_MAXNR 1 在Makefile文件的内容如下: CC=gcc
MODCFLAGS := -Wall -DMODULE -D__KERNEL__ -DLINUX -I/usr/src/linux-2.4.20-8/include
chardev.o :chardev.c
$(CC) $(MODCFLAGS) -c chardev.c
echo insmod chardev.o to turn it on
echo rmmod chardev to turn it off
echo 在bao_load.sh Shell文件中的内容如下: #!/bin/bash
#bao.sh
make
/sbin/insmod chardev.o
cat /proc/devices
mknod /dev/baovar c 250 0
echo "finish" 在终端下运行sh bao_load.sh或./bao_load.sh 就可以加载模块并建立设备节点。 在bao_unload.sh 内容如下: #!/bin/bash
#bao.sh
rm -i /dev/baovar
/sbin/rmmod chardev
cat /proc/devices
echo "finish" 同样运行此Shell文件便可以卸载设备。 上面说的很详细了,在Linux red hat 9.0下成功加载。 下面是另一内核模块,在此模块中调用了以上驱动程序,成功的对设备进行操作: #include <linux/module.h>
#define __KERNEL_SYSCALLS__
#ifdef MODVERSIONS
#include <linux/modversion.h>
#endif
#include <linux/smp_lock.h>
#include <linux/sched.h>
#include <linux/file.h>
#include <linux/unistd.h>
#include <asm/io.h>
#include <linux/fs.h>
#include <linux/kernel.h>
#include <asm/fcntl.h>
#include <asm/uaccess.h>
#include <asm/errno.h>
#include "chardev.h"
static inline _syscall3(int, ioctl, int ,fd,int, cmd,int*, arg); MODULE_LICENSE("GPL");
static char buf1[20];
static char buf2[20];
#define BEGIN_KMEM { mm_segment_t old_fs = get_fs(); set_fs(get_ds());
#define END_KMEM set_fs(old_fs); }
int errno; int init_module(void){
int err;
int put,get;
put=113579;
sprintf(buf1,"%s","baoqunmin");
printk("<1>Hello,world\n");
BEGIN_KMEM;
err=open("/dev/baovar",O_RDWR,0);
if(err>0)
printk("open file successful\n");
write(err,buf1,sizeof(buf1));
read(err,buf2,sizeof(buf2));
buf2[20]='\n';
printk("buf2-->%s\n",buf2);
ioctl(err,IOCTL_WRITE,&put);
ioctl(err,IOCTL_READ,&get);
printk("get-->%d\n",get);
END_KMEM;
return 0;
}
void cleanup_module(void){
printk("<1>Goodbye cruel word\n");
} Makefile文件如下:
CC=gcc
MODCFLAGS := -Wall -DMODULE -D__KERNEL__ -DLINUX -I/usr/src/linux-2.4.20-8/include
bao.o :bao.c
$(CC) $(MODCFLAGS) -c bao.c
echo insmod bao.o to turn it on
echo rmmod bao to turn it off
echo
加载和卸载过程如驱动模块的操作。 操作过程如下: 1.运行bao_load.sh文件 2.make 第二个模块 3./sbin/insmod bao.o 加载第二个模块调用驱动 4.dmesg 显示调用驱动的打印结果 5./sbin/rmmod bao 卸载模块 6.运行bao_unload.sh卸载驱动 以上都在Linux red hat 9.0下成功完成。 从以上程序可以看出,在第二个模块中成功的使用了open,write,read,ioctl。 其实这些都是供用户态使用的函数,这里引进了 #define BEGIN_KMEM { mm_segment_t old_fs = get_fs(); set_fs(get_ds());
#define END_KMEM set_fs(old_fs); } 成功的使用了这些函数,其次ioctl的系统调用如上。 以上完成了在内核模块中调用驱动详细过程,笔者还有几个问题不明白: 1.以上模块中调用驱动,用的open等函数,可以成功打开设备,操作设备, 以上只是简单的调用,我在做加密卡驱动时,用以上方法可以对加密卡进行正确操作,成功加密, 但是我把第二个模块静态编译到freeswan中调用,一运行freeswan就死机。最后不做任何工作,在内核 编译进内核时,只使用了一个open函数,还是死机。 为什么模块间调用的时,能正确操作,静态编译到内核的时候就会死机,问题还未解决? 2.上网查了资料说是内核可以用sys_open,但我在写模块的时候,编译没问题,一加载就出现 unresolved symbol sys_open错误,模块无法加载。但是同样的程序放入内核,只用sys_open 能打开设备,其他的都换了sys_xx,就死机。现在都不知道是什么问题导致死机。 希望大家能给点意见,谢了!
相关阅读 更多 +