从外到内一步一步讲吧!
一、初始化和退出模块
这个没什么讲的了,就是告诉内核是哪个函数负责这个功能的。
module_init(scull_init_module);
module_exit(scull_cleanup_module);
初始化模块的函数
int scull_init_module(void)
{
int result, i;
dev_t dev = 0;
/*
* Get a range of minor numbers to work with, asking for a dynamic
* major unless directed otherwise at load time.
*/
//如果有主设备号,那说明就是事先分配好的主设备号。就应该用静态分配设备号,不然就动态分配。
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 ", scull_major);
return result;
}
/*
* allocate the devices -- we can't have them static, as the number
* can be specified at load time
*/
//这里为scull_dev设备分配空间一共分配4个scull_dev大小的空间因为有4个scull设备在/dev下。
scull_devices = kmalloc(scull_nr_devs * sizeof(struct scull_dev), GFP_KERNEL);
if (!scull_devices) {
result = -ENOMEM;
goto fail; /* Make this more graceful */
}
memset(scull_devices, 0, scull_nr_devs * sizeof(struct scull_dev));
//初始化每一个设备
/* Initialize each device. */
for (i = 0; i < scull_nr_devs; i++) {
scull_devices[i].quantum = scull_quantum;//当前的分配尺寸,是1000
scull_devices[i].qset = scull_qset;//当前的数组大小,是4000字节
init_MUTEX(&scull_devices[i].sem);//互斥信号量,这章好像还没用上。
scull_setup_cdev(&scull_devices[i], i);//字符设备注册
}
//其他的相关scull设备初始化和本章无关。
/* At this point call the init function for any friend device */
dev = MKDEV(scull_major, scull_minor + scull_nr_devs);
dev += scull_p_init(dev);
dev += scull_access_init(dev);
#ifdef SCULL_DEBUG /* only when debugging */
scull_create_proc();
#endif
return 0; /* succeed */
fail:
scull_cleanup_module();
return result;
}
注销模块函数
void scull_cleanup_module(void)
{
int i;
dev_t devno = MKDEV(scull_major, scull_minor);//得到设备号,如果注册时候有设备号的情况。
/* Get rid of our char dev entries */
if (scull_devices) {
for (i = 0; i < scull_nr_devs; i++) {
scull_trim(scull_devices + i);//清空设备结构的内容
cdev_del(&scull_devices[i].cdev);//删除字符设备
}
kfree(scull_devices);//释放字符设备结构占用的空间
}
#ifdef SCULL_DEBUG /* use proc only if debugging */
scull_remove_proc();
#endif
/* cleanup_module is never called if registering failed */
unregister_chrdev_region(devno, scull_nr_devs);
//删除其他的scull相关设备,也就是前面提到的其他的scull版本
/* and call the cleanup functions for friend devices */
scull_p_cleanup();
scull_access_cleanup();
}
二、open和release函数
int scull_open(struct inode *inode, struct file *filp)
{
struct scull_dev *dev; /* device information *///首先声明了一个scull_dev设备的指针
dev = container_of(inode->i_cdev, struct scull_dev, cdev);//通过inode的i_cdev结构也就是cdev结构我们可以得到自己定义的scull_dev结构指针。
filp->private_data = dev; /* for other methods *///将找到的指针保存到file结构中的private_data字段中,用以备用。
/* now trim to 0 the length of the device if open was write-only */
if ( (filp->f_flags & O_ACCMODE) == O_WRONLY) {
if (down_interruptible(&dev->sem))
return -ERESTARTSYS;
scull_trim(dev); /* ignore errors *///如果以只写方式打开的话对结构清零。
up(&dev->sem);
}
return 0; /* success */
}
int scull_release(struct inode *inode, struct file *filp)
{
return 0;
}
三、读写函数
ssize_t scull_read(struct file *filp, char __user *buf, size_t count,
loff_t *f_pos)
{
struct scull_dev *dev = filp->private_data; //定义一个scull_dev设备类型指针指向在open函数找到的scull_dev设备类型保存在file的private_data中。
struct scull_qset *dptr; /* the first listitem *///第一个量子集链表项
int quantum = dev->quantum, qset = dev->qset;//量子的大小和数组的大小
int itemsize = quantum * qset; /* how many bytes in the listitem *///每个链表项的总字节数这里是4000*1000
int item, s_pos, q_pos, rest;//第几个量子以及当前量子的第多少个位置
ssize_t retval = 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 */
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 (defined elsewhere) */
dptr = scull_follow(dev, item);//从链表头一直找到链表的末端,如果中间的位置是NULL那就给它分配空间。
if (dptr == NULL || !dptr->data || ! dptr->data[s_pos])
goto out; /* don't fill holes */
/* read only up to the end of this quantum */
//只读取到当前量子的末端。如果不是count所希望的,那就再次调用read读取下一个量子。
if (count > quantum - q_pos)
count = quantum - q_pos;
//把当前量子中从开始一直到量子结束的内容拷贝到用户空间
if (copy_to_user(buf, dptr->data[s_pos] + q_pos, count)) {
retval = -EFAULT;
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; /* value used in "goto out" statements */
if (down_interruptible(&dev->sem))
return -ERESTARTSYS;
/* find listitem, qset index and offset in the quantum */
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 */
dptr = scull_follow(dev, item);
if (dptr == NULL)
goto out;
//如果当前的首指针是NULL那就分配一个量子的空间4000字节
if (!dptr->data) {
dptr->data = kmalloc(qset * sizeof(char *), GFP_KERNEL);
if (!dptr->data)
goto out;
memset(dptr->data, 0, qset * sizeof(char *));
}
//如果指向的需要拷贝数据的地方为NULL那就分配1000个数组指针
if (!dptr->data[s_pos]) {
dptr->data[s_pos] = kmalloc(quantum, GFP_KERNEL);
if (!dptr->data[s_pos])
goto out;
}
/* write only up to the end of this quantum */
if (count > quantum - q_pos)
count = quantum - q_pos;
if (copy_from_user(dptr->data[s_pos]+q_pos, buf, count)) {
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;
}
阅读(644) | 评论(0) | 转发(0) |