免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 757 | 回复: 0
打印 上一主题 下一主题

字符设备驱动程序 (1) [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2009-01-03 03:19 |只看该作者 |倒序浏览

#include linux/module.h>
#include moduleparam.h>
#include linux/init.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/cdev.h>     /*cdev struct*/
#include asm/system.h>     /*cli(),*_flags*/
#include asm/uaccess.h> /*copy_ ....*/
#include "scull_t1.h" /*attention to the "" and */
/*
*Our parameters which can be set at load time.
*
*/
int scull_major = SCULL_MAJOR;
int scull_minor = 0;
int scull_nr_devs = SCULL_NR_DEVS; /*number of bare scull devs*/
int scull_quantum = SCULL_QUANTUM; /*every one of quantum array has a quantum size*/
int scull_qset = SCULL_QSET;        /*the longth of quantum-set*/
module_param(scull_major,int,S_IRUGO);        /*knowledge 1*/
module_param(scull_monor,int,S_IRUGO);
module_param(scull_nr_devs,int,S_IRUGO);
module_param(scull_quantum,int,S_IRUGO);
module_param(scull_qset,int,S_IRUGO);
struct scull_dev *scull_devices; /*allocated in scull_init_module*/
/*
*Empty out (like "clean up")the scull device
*/
int scull_trim(struct scull_dev *dev) /*知识点4*/
{
    struct scull_qset *next,*dptr;
    int qset = dev->qset; /*amount of quantum*/
    int i;
    for(dptr = dev->data;dptr;dptr = next) /*循环scull_qset list 个数*/
    {
        if(dptr->data)
        {
            for(i = 0; iqset; i++)
                kfree(dptr->data); /*释放dptr指向的数组的每个数组元素指向的quantum空间*/
            kfree(dptr->data);/* 释放当前的scull_set的量子集的空间*/
            dptr->data = NULL;/* 释放一个scull_set中的void **data指针*/
            /*其实就我个人写的话下面那句指向空会漏掉*/
        }
        next = dptr->next; /*下一个scull_qset list开始*/
        kfree(dptr); /*knowledge 2*/
    }
    dev->size =0; /*amount of data store here 为0*/
    dev->quantum = scull_quantum; /*set the quantum'size*//*你不是清空不,怎么还来设置它的大小啊*/
    dev->qset = scull_qset;         /*set the number of quantum in a array*//*同上的疑问*/
    dev->data = NULL;              /*释放当前的scull_device的struct scull_qset *data指针*/
    return 0;
}
/********************************************************************/
/*
*Open and close
*/
   
int scull_open(struct inode *inode,struct file *filp)
{
    struct scull_dev *dev; /*the information of this dev*/
    dev = contairner_of(inode->i_cdev,struct scull_dev, cdev); /*知识点3*/
    flip->private_data = dev; /*.将获得的dev保存到filp->private_data*/
        /*如果是以只读模式打开则调用scull_trim()截短设备为0*/
    if((filp->f_flags&O_ACCMODE) == O_WRONLY) //这里注意是检测打开的方式不是读写权限f_mode
        if(down_interruptible(&dev->sem)) /* 对访问区域加锁*/
            return -ERESTARTSYS; /*猜想应该是信号量不可用的意思吧??*/
        scull_trim(dev);
        up(&dev->sem);                        /* 解锁*/
}
int scull_release(struct inode *inode, struct file *filp)
{
    return 0;
    /*由于前面定义了scull是一个全局且持久的内存区,所以他的release什么都不做*/
}
/*
*Follow the list
*/
struct scull_qset *scull_follow(struct scull_dev *dev,int n)
{
    struct scull_qset *qs = dev->data;
    /*Allocate frist qset explicitly if need be*/
    if(! qs) /*当list为null时*/
    {
        qs = dev->data = kmalloc(sizeof(struct scull_qset),GFP_KERNEL);
        if(qs == NULL)
            return NULL; /*??*/
        memset(qs,0,sizeof(struct scull_qset));   
    }
    /*follow the list*/
    while(n--)
    {
        if(!qs->next)
        {
            qs->next = kmalloc(sizeof(struct scull_qset), GFP_KERNEL);
            if (qs->next == NULL)
                return NULL; /* Never mind */
            memset(qs->next, 0, sizeof(struct scull_qset));
        }
        qs = qs->next;
        continue; /*这下我傻了*/
        return qs;
    }
}
/*
*read and write
*/
ssize_t scull_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos)
{
    struct scull_dev *dev = filp->private_data; /*获得已经保存在其中的设备结构体入口指针*/
    struct scull_qset *dptr;                /*the first listitem*/
    int quantum = dev->quantum,qset = dev->qset;
    int itemsize = quantum *qset; /*这句很怪哦 不同的部分相互赋值干吗啊??*/
    int item,s_pos,q_pos,rest;
    ssize_t retval = 0;
    if(down_interruptible(&dev->sem))
        return -ERESTARSYS;
    if(*f_pos >= dev->size) /*判读off_t *f_pos参数是否超越了设备存储的范围*/
        goto out;
    if(*f_pos+count > dev->size)
        count = dev->size - *f_pos;
    /*find listitem,qset index,and offset in the quantum*//* 根据数组结构得出相应数据 */
    item = (long)*f_pos/ itemsize; /*long???why???*/
    rest = (long)*f_pos%itemsize;
    s_pos = rest/quantum; /*no long ??why??*/
    q_pos = rest%quantum;
    /*follow the list up to the right position(defined elsewhere)*/
    dptr = scull_follow(dev,item);
    /*判断各个数值的合法性并做修正*/
    if(dptr == NULL||!dptr->data||!dptr->data||!dptr->data[s_pos])
        goto out;
    /*read only up to the end of this quantum*/
    if(count>quantum - q_pos)
        count = quantum -q_pos;
   
    /*将内核数据复制到用户区域*/
    if(copy_to_user(buf, dptr->data[s_pos]+q_pos,count))/*发现自己对返回值还是不足*/
    {
        retval = -EFUALT;                                /*上面应该是成功返回0非成功返回非0即真*/
        goto out;
    }   
    *f_pos += count;
    retval = count;
   
      out:
        up(&dev->sem);
        return retval;   
}
ssize_t scull_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos )
{
    struct scull_dev *dev = filp->private_data;
    struct scull_qset *dptr;
    int quantum = dev->quantum,qset = dev->qset;
    int itemsize = quantum * qset;                     /*这句还是没懂*/
    int item,s_pos,q_pos,rest;
    ssize_t retval = -ENOMEM; /*no mem used in "goto out"*/
    if(down_interruptible(&dev->sem))
        return -ERESTART;
    /*同read*/
    item = (long)*f_pos / itemsize;
    rest = (long)*f_pos % itemsize;
    s_pos = rest / quantum;
    q_pos = rest % quantum;   
    /* follow the list up to the right position */
    /*同read*/
    dptr = scull_follow(dev, item);   
        if (dptr == NULL)
        goto out;
    if (!dptr->data)
    {
        dptr->data = kmalloc(qset * sizeof(char *), GFP_KERNEL);
        if (!dptr->data)
            goto out;
        memset(dptr->data, 0, qset * sizeof(char *));
    }
    if (!dptr->data[s_pos])
    {
        dptr->data[s_pos] = kmalloc(quantum, GFP_KERNEL);
        if (!dptr->data[s_pos])
            goto out;
    }
    if (count > quantum - q_pos)
        count = quantum - q_pos;   
/*注意假如内核访问用户区域,由于user域存在分页机制,
进程有可能被休眠以等待不再当前页中的数据,所以需要
copy_from_user函数是可重入的.*/
    if(copy_from_user(dptr->data[s_pos]+q_pos, buf, count))
    {
        retval = -EFAULT; /*bad address*/
        goto out;   
    }
    *f_pos +=count;
    retval = count;
    /*updata the size*/
    /*实际上大多数情况读写函数是不能完全读写完的,所以做更新方便读写完的操作*/
    if(dev->size *f_pos)
        dev->size = *f_pos;
    out:
        up(&dev->sem);
        return retval;
}
/*set file_operations*/
struct file_operations scull_fops =
{
    .owner = THIS_MODULE,
    .read = scull_read,
    .write = scull_write,
    .open = scull_open,
    .release = scull_release,
};
/*init and exit*/
void scull_cleanup_module(void)
{
    int i;
    dev_t devno = MKDEV(scull_major,scull_minor);/*获取设备号*/
    /*get rid of dev entries*/
    if(scull_devices)
    {
        for(i = 0; iscull_nr_devs; i++ )
        {
            scull_trim(scull_devices +i);
            cdev_del(&scull_devices.cdev);
        }
        kfree(scull_devices);
    }
    unregister_chrdev_region(devno, scull_nr_devs);
}
/*
*set up the char_dev structure for this device
*/
static void scull_setup_cdev(struct scull_dev *dev, int index)
{
    int err,devno = MKDEV(scull_major,scull_minor+index);
    /*初始化cdev,主要是指定其fops */
    cdev_init(&dev->cdev,&scull_fops); /*注册设备结构体*//*知识点5*/
    dev->cdev.owner = THIS_MODULE; /*注意这里是先注册再初始化有些元素,原因有待查*/
    dev->cdev.ops = &scull_fops; /*cdev->init里面已经初始化了 可省略*/
    err = cdev_add(&dev->cdev, devno,1) /*告知内核该结构体信息,设备注册到这里才算真的激活*/
    if(err)
        printk(KERN_NOTICE "Error %d adding scull%d",err,index); /*这个失败的可能很小书上这么提到why??*/   
}
int scull_init_module(void)
{
    int result,i;
    dev_t dev = 0;
    if (scull_major) /*选择是否自动获取设备号*/
    {
        dev = MKDEV(scull_major, scull_minor);
        result = register_chrdev_region(dev, scull_nr_devs, "scull");
    }
    else
    {
        result = alloc_chrdev_region(&dev, scull_minor, scull_nr_devs,
                "scull");
        scull_major = MAJOR(dev);
    }
    if (result  0)
    {
        printk(KERN_WARNING "scull: can't get major %d\n", scull_major);
        return result;
    }
    /*
    *allocate the devices we can't have them static,as the number
    *can be specified at load time
    */
    scull_devices = kmalloc(scull_nr_devs*sizeof(struct scull_dev), GFP_KERNEL);/*分配nr个scull_dev的内存*/
    if(!scull_devices)
    {
        result = -ENOMEM; /*NO enogh memory */
        goto fail;
    }
    memset(scull_devices,0,scull_nr_devs*sizeof(struct scull_dev)); /*kmalloc不清空*/
    /*init each device*/
    for(i = 0; iscull_nr_devs;i++)
    {
        scull_devices.quantum = scull_quantum;
        scull_devices.qset = scull_qset;
        init_MUTEX(&scull_devices.sem); /*初始化旗帜变量 用于down_interruptible up(&xxx_dev->sem);*/
        scull_setup_cdev(&scull_devices,i); /*这个一定记得放于最后*/
    }
    return 0; /*succeed*/
    fail: /*知识点6 goto*/
        scull_cleanup_module();
        return result;
}
/*加载 注销*/
module_init(scull_init_module);
module_exit(scull_cleanup_module);
/*程序信息*/
MODULE_AUTHOR("小兽");
MODULE_LICENSE("Dual BSD/GPL"); /*听说不写这个会有警告,等下编译的时候试验下*/


/*knowledge 1 module_param
*我这样理解这个函数,它向当前模块传入参数
*而这些参数是经过该函数注册的,带有一定的属性
*具体属性可以跟踪moduleparam.h里面的具体函数,我想能
*理解好__attribute__这个家伙的性能应该就能理解这个函数的意义了。
*值得注意的是,注册数组之类的是用的别的类似于这个函数的函数
*
*这个知识点我犯错了,因为本身LDD3上介绍得很清楚了,由于是很久以前看书
*时候看到的,给忘记了.... *应该书它的作用是让insmod命令对那些参数可见,而这
*种使用只是在调试的时候应该
*用,至于平常视乎更多的是不允许这些参数被修改的。
*
*再次提醒自己,不要什么程序可以跑了基本能看懂就OK了,这样的路迟早走不远,更别说
*实现自己的理想。
****************************************************************************/


/*N0 2
* 关于指针释放的,说实在的这里发现自己的C基础很差,完全找不到
*指针释放的概念,在这里做个笔记 提醒下以后多注意下
*/
/*NO3
*分配并填写置于filp->private_data里的数据结构。
*将初始化过的struct scull_dev dev的指针传递到filp->private_data里,以备后用
*
*具体说法为我们不需要cdev结构体本身,但希望得到包含scull_dev结构体?
*于是将scull_dev结构体指针保存在private_data字段中,方便使用
*
*简介点说是.通过indode->i_cdev(其本身指向scull_dev的cdev)获得scull_dev的入口指针
*识别需要被打开的设备
*/


/***
*NO4
*
*scull_trim的实现相当于遍历了链表,先用dptr指针作为游标,指向每一个
*scull_qset,再通过量子集的大小dev->qset,在for循环中对dptr->data中的每一个量
*子进行释放。dptr->data是一个指针数组,所以kfree的参数为dptr->data。把量
*子集释放后再释放dptr->data.接着通过next指针移动游标,再释放当前的dptr。
*完整的内存释放后再对scull_devices初始化。
*http://www.diybl.com/course/6_system/linux/Linuxjs/2008109/149527_2.html
*/


/*
*NO5 设备结构体注册详情查看LDD3 P59
*记得刚接触cdev这个家伙的时候是在跟着一本用老方法注册设备的书做完两个
*驱动之后的事了,为了接受这个结构体花了不少时间,现在都不知道为什么,
*但确实卡了我好久。呵呵 想想好笑
*这里用户自己定义的设备结构体scull_dev中嵌入了cdev注意接受这种嵌入的方法!!!!
在open函数里面
* 通过indode->i_cdev(其本身指向scull_dev的cdev)获得scull_dev的入口指针识别需要被打开的设备
* dev = container_of(inode->i_cdev, struct scull_dev, cdev);
* 将获得的dev保存到filp->private_data filp->private_data = dev
*
这样就完成了整个自己定义设备结构体的嵌入工作
*/


/*
*NO6 goto
*大一接触C的时候这个印象特深,说什么容易把程序跳乱不要乱用,前几天看一些关于
*goto 的讨论才知道,是个什么大师发表了一篇关于goto毒害的文章后才被认为是毒瘤
*看了些资料后的整体认识是:1:向后跳的话没什么大碍,也不影响可读性(短距离)
*                                    2:这里用到的错误恢复机制视乎没有别的语句能比goto
*                                     更简介的实现所需要的功能 这点蛮好的!
*                                    3:goto 源于汇编,可以说是比较底层的操作,很多破解直接
*                                     一个goto语句越过注册码算法部分 跳到检测注册码后的
*                                     程序运行部分破解就完成,所以你不能说它不强大!!!
*/


本文来自ChinaUnix博客,如果查看原文请点:http://blog.chinaunix.net/u3/90333/showart_1769534.html
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP