- 论坛徽章:
- 0
|
转载:ldd3第三章Scull代码解析
来源:
http://ardam331.blog.163.com/blog/static/5691238220071117561920/
上次看的那个led设备的驱动程序相对来说比较简单些。而且功能也不是很全面。今天打算总结一下书中第3章给出的实例。 从外到内一步一步讲吧!一、初始化和退出模块这个没什么讲的了,就是告诉内核是哪个函数负责这个功能的。
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 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_devices.quantum = scull_quantum;//当前的分配尺寸,是1000 scull_devices.qset = scull_qset;//当前的数组大小,是4000字节 init_MUTEX(&scull_devices.sem);//互斥信号量,这章好像还没用上。 scull_setup_cdev(&scull_devices, 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 //清空设备结构的内容
cdev_del(&scull_devices.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 dev->size = *f_pos; out: up(&dev->sem); return retval;}
本文来自ChinaUnix博客,如果查看原文请点:http://blog.chinaunix.net/u3/100532/showart_1995189.html |
|