红联Linux门户
Linux帮助

scull字符驱动,咋么看的啊??

发布时间:2010-10-02 20:55:36来源:红联作者:yezhubenyue
弱弱的问下那个没头文件main()这些函数都是咋么调
用的啊? 咋么看函数之间都没联系啊 求解!!!!!!!!!!!!!!


#include
#include
#include
#include //printk()
#include //kmalloc()
#include //everything...
#include //error codes
#include //size_t
#include //O_ACCMODE
#include //struct cdev{}
#include //cli(), *_flags
#include //copy_*_user
#include "scull.h" //local definitions
//参数可以使用insmod加载
int scull_major = SCULL_MAJOR;//主设备号
int scull_minor = 0;//次设备号
int scull_nr_devs = SCULL_NR_DEVS;//设备数量
int scull_quantum = SCULL_QUANTUM;//每个量子分配的内存大小
int scull_qset = SCULL_QSET;//量子集的个数

module_param(scull_major,int,S_IRUGO);
module_param(scull_minor,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;//全局持久的scull设备变量
//释放整个数据区域的函数,类似于清零
//scull_dev结构中*data指向一个有scull_qset的链表,链表中每个scull_qset结构中的**data指向一个指针数组,数组中每个指针指向一片内存区域,这些内存区域用来 存储数据。而scull_trim函数则使用循环的方式释放这些内存区域和scull_qset结构
int scull_trim(struct scull_dev *dev)
{
struct scull_qset *next,*dptr;
int qset=dev->qset;//量子集中量子的个数,也就是一个量子集的大小
int i;
for(dptr=dev->data;dptr;dptr=next)
{
if(dptr->data)
{
for(i=0;i kfree(dptr->data[i]);
kfree(dptr->data);//释放一个scull_qset链表项指针的空间
dptr->data = NULL;//赋值为空
}
next = dptr->next;//准备下一个链表项
kfree(dptr);//释放除第一个节点(scull_dev dev)以外的所有节点!也就是释放所有的scull_qset结构体
}
dev->size = 0;
dev->quantum = scull_quantum;//量子的大小,量子的定义:每次申请分配内存的最小单位.
dev->qset = scull_qset;//量子集的大小
dev->data = NULL;
return 0;
}
//Open & Close
//Open函数关键是完成:分配并填写被置于filp->private_data里的数据结构!!
int scull_open(struct inode *inode,struct file *filp)
{
struct scull_dev *dev;
dev = container_of(inode->i_cdev,struct scull_dev,cdev);//通过指针struct cdev*来获得struct scull_dev*这个新指针!注意cdev数据是scull_dev结构体的成员变量名,inode->i_cdev是指向cdev变量类型的指针.
filp->private_data = dev;//for other methods
if((filp->f_flags & O_ACCMODE) == O_WRONLY)//只写的方式打开设备
{
if(down_interruptible(&dev->sem))//获取信号函数!返回值为0表示成功,非0表示失败
return -ERESTARTSYS;//
scull_trim(dev);//ignore errors 清零处理!
up(&dev->sem);//释放信号函数
}
return 0;
}

int scull_release(struct inode *inode,struct file *filp)//释放设备
{
return 0;
}
/*
*以下是scull模块中的一个沿链表前行得到正确scull_set指针的函数,将在read和write方法中被调用
*这个函数的实质是:如果已经存在这个scull_set,就返回这个scull_set的指针。如果不存在这个scull_set,一边沿链表为scull_set分配空间一边沿链表前行,直到所需要的scull_set 被分配到空间并初始化为止,就返回这个scull_set 的指针。
*/
struct scull_qset *scull_follow(struct scull_dev *dev,int n)
{
struct scull_qset *qs = dev->data;//指向第一个量子集
if(!qs)//如果在该索引之前的scull_qset结构不存在的话,为之分配内存
{
qs = dev->data = kmalloc(sizeof(struct scull_qset),GFP_KERNEL);
if(qs == NULL)
return NULL;//创建失败
memset(qs,0,sizeof(struct scull_qset));
}
while(n--)//接下来继续创建节点
{
if(!qs->next)//如果在该索引之前的scull_qset结构不存在的话,为之分配内存
{
qs->next = kmalloc(sizeof(struct scull_qset),GFP_KERNEL);
if(qs->next == NULL)
return NULL;//创建失败
memset(qs->next,0,sizeof(struct scull_qset));
}
qs = qs->next;
continue;
}
return qs;//正好返回第item个scull_qset链表项指针.
}
//read and write
//注意:scull设备的读和写函数仅限在1个量子集中进行!!!
ssize_t scull_read(struct file *filp,char __user *buf,size_t count,loff_t *f_pos)
{
struct scull_dev *dev = filp->private_data;//将设备数据赋值给变量dev,为提取设备数据做准备!
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;//实际读取的字节数,初始化为0
//获取互斥信号
if(down_interruptible(&dev->sem))
return -ERESTARTSYS;
if(*f_pos >= dev->size)//超过了设备数据量,文件指针的位置已经不在设备中了!!
goto out;
if(*f_pos + count > dev->size)//确保提取设备数据的长度是有效的!!
count = dev->size - *f_pos;
//find listitem,qset index,and offset in the quantum
//在量子集中寻找链表项、qset索引以及quantum偏移量
//计算当前偏移对应第几个scull_qset、在scull_qset中data的下标、以及在*data指向的内存区域中的偏移;
item = (long)*f_pos / itemsize;//当前偏移对应第item个scull_qset
rest = (long)*f_pos % itemsize;
s_pos = rest / quantum;//qset索引,scull_qset中data的下标
q_pos = rest % quantum;//量子偏移量的位置,在*data指向的内存区域中的偏移
//遍历链表(如果没有链表则建立链表),直至到达正确的节点位置
//根据当前要操作的sucll_qset的索引item,返回其指针,如果在该索引之前的scull_qset结构不存在的话,为之分配内存
dptr = scull_follow(dev,item);//返回scull_qset*指针
if(dptr == NULL || !dptr->data || !dptr->data[s_pos])
goto out;
if(count > quantum - q_pos)//仅读至当前量子的末尾
count = quantum - q_pos;
//ulong copy_to_user(void __user *to,const void *from,ulong count);
//拷贝数据
if(copy_to_user(buf,dptr->data[s_pos]+q_pos,count))//返回0表示成功,非0表示失败
{
retval = -EFAULT;//无效的地址
goto out;
}
//重置偏移量
*f_pos += count;
retval = count;//返回读取的字节数count
out:
up(&dev->sem);//释放信号量
return retval;
}
//write 将用户空间的数据写入到设备!
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;//scull设备的信息,在scull_open函数中赋值给了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;//写入数据的报错
if(down_interruptible(&dev->sem))//获取信号量函数
return -ERESTARTSYS;
//计算当前偏移对应第几个scull_qset、在scull_qset中data的下标、以及在*data指向的内存区域中的偏移;
//find listitem,qset index,offset in the quantum
item = (long)*f_pos / itemsize;//listitem 得到当前偏移对应第item个scull_qset
rest = (long)*f_pos % itemsize;//得到当前链表项的偏移量
s_pos = rest / quantum;//scull_qset中data的下标
q_pos = rest % quantum;//在*data指向的内存区域中的偏移
//follow the list up to the right position
dptr = scull_follow(dev,item);//根据当前要操作的sucll_qset的索引item,返回其指针,如果在该索引之前的scull_qset结构不存在的话,为之分配内存
if(dptr == NULL)//查找的链表项不存在,或者其他错误
goto out;
if(!dptr->data)//分配内存(地址空间),用作数据的存储!!
{
dptr->data = kmalloc(qset * sizeof(char *),GFP_KERNEL);//分配qset字节的空间,sizeof(char *)表示单位是字节
if(dptr->data == NULL)
goto out;
memset(dptr->data,0,qset * sizeof(char *));
}
if(!dptr->data[s_pos])//给qset分配空间,用作数据的存储.
{
dptr->data[s_pos] = kmalloc(quantum,GFP_KERNEL);
if(dptr->data[s_pos] == NULL)
goto out;
}
if(count > quantum - q_pos)//仅写至当前量子的末尾
count = quantum - q_pos;
//ulong copy_from_user(void *to,const void __user *from,ulong count);
//拷贝数据
if(copy_from_user(dptr->data[s_pos]+q_pos,buf,count))//返回0表示成功,非0表示失败
{
retval = -EFAULT;
goto out;
}
//重置偏移量
*f_pos += count;
retval = count;
/* update the size */
if (dev->size < *f_pos)
dev->size = *f_pos;
out:
up(&dev->sem);//释放信号量
return retval;
}
//文件操作结构
struct file_operations scull_fops = {
.owner = THIS_MODULE,
.read = scull_read,
.write = scull_write,
.open = scull_open,
.release = scull_release,
};
//为scull设备建立字符设备结构体
static void scull_setup_cdev(struct scull_dev *dev,int index)
{
int err,devno = MKDEV(scull_major,scull_minor + index);
cdev_init(&dev->cdev,&scull_fops);//初始化建立cdev和file_operations之间的连接,struct file_operations scull_fops
dev->cdev.owner = THIS_MODULE;//所属模块
err = cdev_add(&dev->cdev,devno,1);//注册设备,返回0表示成功,非0表示失败
if(err)
printk(KERN_NOTICE "Error %d adding scull%d",err,index);
}
//卸载模块的函数!
//scull_dev结构中*data指向一个有scull_qset的链表,链表中每个scull_qset结构中的**data指向一个指针数组,数组中每个指针指向一片内存区域,这些内存区域用来 存储数据。而scull_trim函数则使用循环的方式释放这些内存区域和scull_qset结构
void scull_cleanup_module(void)
{
int i;
dev_t devno = MKDEV(scull_major,scull_minor);
//卸载设备,清零处理
if(scull_devices)
{
for(i=0;i {
scull_trim(scull_devices+i);//调用scull_trim()函数释放每个设备中存储数据的内存区域
cdev_del(&scull_devices[i].cdev);//调用cde_del()函数移除该设备对应的字符设备结构
}
kfree(scull_devices);//释放设备scull_dev结构
}
unregister_chrdev_region(devno,scull_nr_devs);//释放设备号
}

//初始化模块的函数
int scull_init_module(void)
{
int result,i;
dev_t dev = 0;
//注册设备号
if(scull_major)//根据已知的主设备号,静态注册设备
{
dev = MKDEV(scull_major,scull_minor);//调用MKDEV宏定义,利用主次设备号得到dev_t
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;
}
//全局scull设备变量,struct scull_dev *scull_devices;
//为scull_nr_devs个设备分配scull_dev的内存空间,并清空
scull_devices = kmalloc(scull_nr_devs * sizeof(struct scull_dev),GFP_KERNEL);
if(!scull_devices)//返回0,表示分配内存失败
{
result = -ENOMEM;
goto fail;
}
//分配的内存空间初始化为0
memset(scull_devices,0,scull_nr_devs * sizeof(struct scull_dev));
//Initialize each device
//在循环中初始化每个scull_dev设备:初始化quantnum、qset等变量,初始化信号量、调用函数scull_setup_cdev()
for(i=0;i {
scull_devices[i].quantum = scull_quantum;
scull_devices[i].qset = scull_qset;
init_MUTEX(&scull_devices[i].sem);//互斥信号量,scull设备用不上.
scull_setup_cdev(&scull_devices[i],i);
}
return 0;
fail:
scull_cleanup_module();
return result;
}

module_init(scull_init_module);
module_exit(scull_cleanup_module);
MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("beyond2010-6-4");
文章评论

共有 8 条评论

  1. shenhao0129 于 2010-10-04 22:10:52发表:

    不客气,看书还是不能随便的跳着看,基础很重要,要从容易到难

  2. yezhubenyue 于 2010-10-03 18:12:37发表:

    谢谢楼上的饿!!!!!!!

  3. yezhubenyue 于 2010-10-03 18:12:20发表:

    !!!!额,迷迷糊糊的也差不多了~~~~~~~~~~~~~~~~~

  4. shenhao0129 于 2010-10-02 22:48:22发表:

    引用:
    3# shenhao0129

    问下我这么理解对吗??那个模块加载时时会执行module_init(); 然后模块就停下来等待事件的触发?? 打开文件的时候就执行对应的open()函数
    读的时候就执行read()函数等等。。。。。。 ...
    yezhubenyue 发表于 2010-10-2 22:12

    恩,差不多就是这么运行的呢,这里是不能存在main函数的,都是一个一个的子函数供上层的read等函数调用的,比如你写一个数据到硬盘,在上层调用write函数,最后你可以跟踪函数的调用一层一层的看,最后是调用到来硬盘的驱动函数了,原理都差不多的

  5. yezhubenyue 于 2010-10-02 22:12:49发表:

    3# shenhao0129

    问下我这么理解对吗??那个模块加载时时会执行module_init(); 然后模块就停下来等待事件的触发?? 打开文件的时候就执行对应的open()函数
    读的时候就执行read()函数等等。。。。。。。。。。推出的时候会执行module_exit(scull_cleanup_module);


    这样理解对吗????????????????????????

  6. yezhubenyue 于 2010-10-02 21:23:40发表:

    3# shenhao0129


    module_init(scull_init_module);
    module_exit(scull_cleanup_module);
    MODULE_LICENSE("Dual BSD/GPL");
    MODULE_AUTHOR("beyond2010-6-4");这几个吗???是调用了scull_init_module()函数,,但还有很多函数没被调用到啊。。。。。。更我说说啦~~~~~谢谢了。。比如那个scull_read函数,scull_open之类的咋么调用了啊!!!!!!?????

  7. shenhao0129 于 2010-10-02 21:15:53发表:

    看到最后几个函数了吗?你对于内核的结构一点都不熟悉,直接看这个肯定是看不懂的

  8. yezhubenyue 于 2010-10-02 20:56:17发表:

    是ldd3上的scull字符驱动。。。。。。。。。。