linux字符设备 cdev

    看了有那么长时间的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,所以出现写入失败。

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值