看了有那么长时间的linux device driver了,看起来感觉不过如此,总想写点东西实践一下,可惜一直不知道如何下手,今天终于下定决心了,不说废话 先把程序贴上分析下。
#include <linux/moduleparam.h>
内核允许对驱动程序在加载的时候传递参数,在编写内核程序的时候。要在代码中用宏module_param来指定接受的参数名,而这个宏在<linux/moduleparam.h>中的定义如下
/* Helper functions: type is byte, short, ushort, int, uint, long,
ulong, charp, bool or invbool, or XXX if you define param_get_XXX,
param_set_XXX and param_check_XXX. */
#define module_param_named(name, value, type, perm) /
param_check_##type(name, &(value)); /
module_param_call(name, param_set_##type, param_get_##type, &value, perm); /
__MODULE_PARM_TYPE(name, #type)
#define module_param(name, type, perm) /
module_param_named(name, name, type, perm)
由此可知 module_param的实现是通过 module_param_named(name, name, type, perm)的。
其中使用了 3 个参数:要传递的参数变量名, 变量的数据类型, 以及访问参数的权限 。
以上是其他地方找的
#include <linux/module.h>
#include <linux/init.h>
这两个头文件主要是包含module_init module_exit,这两个函数分别在模块加载和删除的时候运行,参数是函数指针一般声明为static,因为其他地方不用调用。
#include <linux/kernel.h>
#include <linux/fs.h> /*包含大部分关于文件方面的函数*/
#include <linux/cdev.h>/*字符设备驱动的结构*/
#include <linux/gfp.h>/*GFP_KERNEL*/
#include <linux/slab.h> /* kmalloc() */
#include <asm/uaccess.h> /*copy_from_user() copy_to_user()*/
MODULE_AUTHOR("Alessandro Rubini, Jonathan Corbet");
MODULE_LICENSE("Dual BSD/GPL");
int snull_minor=0;
int snull_major=0;
/*驱动结构*/
struct snull_dev
{
char* inbuffer;
char* outbuffer;
int size;
struct cdev cdev;
};
struct snull_dev *snull_device;
/* open file*/
int snull_open(struct inode *inode,struct file *filp)
{
struct snull_dev* dev;
/*这个函数用于返回一个包含了cdev结构的snull_dev的指针
原型:container_of(pointer,container_type,container_field)*/
dev = container_of(inode->i_cdev,struct snull_dev,cdev);
filp->private_data = dev;
if((filp->f_flags&O_ACCMODE) == O_WRONLY)
{
if(dev->inbuffer)
{
kfree(dev->inbuffer);
dev->inbuffer = NULL;
}
if(dev->outbuffer)
{
kfree(dev->outbuffer);
dev->outbuffer = NULL;
}
}
filp->f_pos = 0;
return 0;
}
/*close*/
int snull_close(struct inode *inode,struct file *filp)
{
return 0;
}
/*read*/
ssize_t snull_read(struct file* filp,const char __user* usr_buffer,size_t size,loff_t* offset)
{
struct snull_dev* dev;
dev = filp->private_data;
printk("size:%d,offset:%ld/n",size,*offset);
if((size+*offset) > dev->size)
{
printk("read to large/n");
return -1;
}
if(!dev->inbuffer)
{
return -1;
}
/*将内核空间的数据复制到数据空间
原型:copy_to_user(void __user *to,const void *from,unsigned long count)*/
if(copy_to_user(usr_buffer,dev->inbuffer+*offset,size))
{
return -2;
}
*offset+=size;
return size;
}
/*write*/
ssize_t snull_write(struct file* filp,const char __user* usr_buffer,size_t size,loff_t* offset)
{
struct snull_dev* dev;
dev = filp->private_data;
printk("size:%d,offset:%ld/n",size,*offset);
if((size+*offset) > dev->size)
{
printk("write to large/n");
return -1;
}
if(!dev->inbuffer)
{
dev->inbuffer = kmalloc(dev->size,GFP_KERNEL);
if(!dev->inbuffer)
{
printk(KERN_WARNING "snull:can't get memory for snull_dev");
return -1;
}
}
/*将用户空间的数据复制到内核空间
原型:copy_from_user(void *to,void __user *from,unsigned long count)*/
if(copy_from_user(dev->inbuffer+*offset,usr_buffer,size))
{
return -2;
}
*offset+=size;
return size;
}
/*该结构定义在linux/fs.h中,其中的函数用于用户空间的库函数调用相应函数时调用。
struct file_operations snull_fops = {
.owner = THIS_MODULE,
.open = snull_open,
.read = snull_read,
.write = snull_write,
.release = snull_close,
};
static void __exit snull_exit(void)
{
dev_t dev;
dev = MKDEV(snull_major,snull_minor);
if(snull_device)
{
if(snull_device -> inbuffer)
kfree(snull_device -> inbuffer);
if(snull_device -> outbuffer)
kfree(snull_device -> outbuffer);
cdev_del(&snull_device -> cdev);
kfree(snull_device);
}
unregister_chrdev_region(dev,1);
}
void snull_setup_cdev(struct snull_dev* dev)
{
dev->size = 10000;
int err,devno = MKDEV(snull_major,snull_minor);
printk("dev major %d/n",snull_major);
printk("dev major %d/n",snull_minor);
/*初始化字符设备*/
cdev_init(&dev->cdev,&snull_fops);
dev->cdev.owner = THIS_MODULE;
dev->cdev.ops=&snull_fops;
/*添加设备到内核中,将设备与设备号绑定到一起,设备号分为主设备号和此设备号
主要用于创建设备文件的时候用到:mknod /dev/snull c major minor*/
err = cdev_add(&dev->cdev,devno,1);
if(err)
printk("error adding snull/n");
}
static int __init snull_init(void)
{
dev_t dev;
int result;
/*动态申请主设备号*/
result = alloc_chrdev_region(&dev,snull_minor,1,"snull");
snull_major = MAJOR(dev);
printk("dev major %d/n",snull_major);
if(result < 0)
{
printk(KERN_WARNING "snull:can't get major %d/n",snull_major);
return result;
}
snull_device = kmalloc(sizeof(struct snull_dev),GFP_KERNEL);
if(!snull_device)
{
printk(KERN_WARNING "snull:can't get memory for snull_dev");
result = -ENOMEM;
goto fail;
}
snull_device->inbuffer = NULL;
snull_device->outbuffer = NULL;
memset(snull_device,0,sizeof(struct snull_dev));
snull_setup_cdev(snull_device);
dev = MKDEV(snull_major,snull_minor);
printk("dev major %d/n",dev);
fail:
// snull_exit();
return result;
}
module_init(snull_init);
module_exit(snull_exit);
下面说说过程中主要遇到的问题
1.编译时出现variable 'snull_fops' has initializer but incomplete type,后来多次调试和查网页发现主要是由于没有包含linux/fs.h.
2.在rmmod的时候,内核崩溃,分析出错报告,发现是由于释放内存的时候被释放的地址为NULL。
if(snull_device)
{
if(snull_device -> inbuffer)
kfree(snull_device -> inbuffer);
if(snull_device -> outbuffer)
kfree(snull_device -> outbuffer);
cdev_del(&snull_device -> cdev);
}
kfree(snull_device);
开始这段程序是这样写的,可能由于开始测试的时候,程序其他地方有误,导致snull_device为0了,但是没找到是什么地方的原因,因为在init中分配snull_device的时候是成功的。
3.在open时候,没有设置filp->f_pos为0,所以读写时候出错。
4.在写测试程序的时候,用的带缓冲的fwrite函数,在这个地方由于库函数自动设置了buffer的大小为一内存页大小(4096),而我才程序中设置最大为1024,所以出现写入失败。