前段时间一直在搞GPS导航系统的应用软件,最近告一段落,继续捡起linux设备驱动,啃之!
上次记录了模块初始化代码的学习,今次看看卸载模块的代码。
01.void scull_cleanup_module(void)
02.
03.{
04.
05. int i;
06.
07. dev_t devno = MKDEV(scull_major, scull_minor); //这个见过!得到当前模块的设备号
08.
09.
10.
11. if(scull_devices){
12.
13. for(i = 0; i < scull_nr_devs; i++){
14.
15. scull_trim(scull_devices +i); //在下面分析这个函数
16.
17. cdev_del(&scull_devices[i].cdev); /*系统调用。从系统中移除一个字符设备。该函数同cdev_alloc, cdev_init, cdev_add三个函数构成了字符设备注册所需的函数 */
18.
19. }
20.
21. kfree(scull_devices); /*此次和前面的kmalloc函数相对应,释放设备的内存空间(这里设备本身就是一段内存,哈哈) */
22.
23. }
24.
25.}
看一下scull_trim
/*
* Empty out the scull device; must be called with the device
* semaphore held.
*/
该函数的作用很简单:遍历整个数据区,释放所有找到的量子和量子集。内容也很简单,不解释。
01.int scull_trim(struct scull_dev*dev)
02.
03.{
04.
05. struct scull_qset *next, *dptr;
06.
07. int qset = dev->qset; /*"dev" is not-null*/
08.
09.
10.
11. for(dptr = dev->data; dptr; dptr = next) //遍历整个链表
12.
13. {
14.
15. if(dptr->data)
16.
17. {
18.
19. for(i = 0; i < qset; i++)
20.
21. kfree(dptr->data[i]);
22.
23. kfree(dptr->data);
24.
25. dptr->data = NULL;
26.
27. }
28.
29. next = dptr->next;
30.
31. kfree(dptr);
32.
33. }
34.
35. dev->size = 0;
36.
37. dev->quantum = scull_quantum;
38.
39. dev->qset = scull_qset;
40.
41. dev->data = NULL;
42.
43. return 0;
44.
45.}
SCULL设备驱动程序实现了几个最重要的设备方法。如下file_operations结构01.struct file_operations scull_fops = {
02..owner = THIS_MODULE,
03..llseek = scull_llseek,
04..read = scull_real,
05..write = scull_write,
06..ioctl = scull_ioctl,
07..open = scull_open,
08..release = scull_release,
09.};
这些方法都要驱动程序设计者自己来实现的,每个设备驱动程序都需要设计者来实现这几个方法。
例如驱动程序使用者对open的调用将会调用scull_open函数。我认为这里可以理解为所有驱动程序的接口都是相同的,只是内部实现不同。
关于open和release方法的作用参考书中page62, page63。
read和write方法的原型如下:
ssize_t read(stuct file *filp, char _ _user *buff, size_t count, loff_t offp);
ssize_t write(struct file * filp, const char _ _user *buff, size_t count, loff_t *offp);
这两个方法的实质就是数据拷贝。read方法拷贝数据到用户地址空间,write方法拷贝数据到内核地址空间。实现这两种拷贝用到的内核函数如下(是大多数read和write方法实现的核心部分):
unsigned long copy_to_user(void _ _user *to, const void *from, unsigned long count);
unsigned long copy_from_user(void *to, const void _ _user *from, unsigned long count);
分析一下scull的open方法:
01.int scull_open(struct inode *inode, struct file *filp)
02.{
03. struct scull_dev *dev; /* device information */
04.
05. dev = container_of(inode->i_cdev, struct scull_dev, cdev); /*由于inode的i_cdev字段只是包含了指向struct cdev结构的指针,而我们要使用的是scull_dev结构指针,所有这里使用了container_of宏,用来取得包含cdev结构的scull_dev结构*/
06. filp->private_data = dev; /* 将得到的scull_dev结构指针保存起来,方便以后的访问。*/
07.
08. /*这里只是当设备以“写”打开时,长度截为0*/
09. if ( (filp->f_flags & O_ACCMODE) == O_WRONLY) {
10. if (down_interruptible(&dev->sem)) /*获得信号量,成功获得则继续,没成功则返回-ERESTARSYS */
11. return -ERESTARTSYS;
12. scull_trim(dev); /* ignore errors */
13. up(&dev->sem); /*释放信号量*/
14. }
15. return 0; /* success */
16.}
分析一下scull的release方法:基本的scull没有需要关闭的硬件,因此直接返回。
01.int scull_release(struct inode *inode, struct file *filp)
02.{
03. return 0;
04.}
分析一下scull的read方法:
01.ssize_t scull_read(struct file *filp, char __user *buf, size_t count,
02. loff_t *f_pos)
03.{
04. struct scull_dev *dev = filp->private_data; /*取得open时分配的保存在filp->private_data中的scull_dev结构指针。*/
05. struct scull_qset *dptr; /* the first listitem */
06. int quantum = dev->quantum, qset = dev->qset; /*取得量子大小(4000bytes), 取得量子集大小(1000个量子)*/
07. int itemsize = quantum * qset; /* how many bytes in the listitem,链表中每个item的大小(4 000 000bytes) */
08. int item, s_pos, q_pos, rest;
09. ssize_t retval = 0;
10.
11. if (down_interruptible(&dev->sem)) /*下面要进行的是一个互斥的操作,所以这里要获得信号量*/
12. return -ERESTARTSYS;
13. if (*f_pos >= dev->size) /*如果要读取的偏移位置超过了设备文件的大小,则不能读取,直接跳到out*/
14. goto out;
15. if (*f_pos + count > dev->size) /*如果要读取的内容超过了设备文件的大小,则截短之,只读到文件尾就可以了*/
16. count = dev->size - *f_pos;
17.
18. /* find listitem, qset index, and offset in the quantum */
19. item = (long)*f_pos / itemsize;
20. rest = (long)*f_pos % itemsize;
21. s_pos = rest / quantum; q_pos = rest % quantum;
22.
23. /* follow the list up to the right position (defined elsewhere) */
24. dptr = scull_follow(dev, item);
25.
26. if (dptr == NULL || !dptr->data || ! dptr->data[s_pos])
27. goto out; /* don't fill holes */
28.
29. /* read only up to the end of this quantum */
30. if (count > quantum - q_pos)
31. count = quantum - q_pos;
32.
33. if (copy_to_user(buf, dptr->data[s_pos] + q_pos, count)) {
34. retval = -EFAULT;
35. goto out;
36. }
37. *f_pos += count;
38. retval = count;
39.
40. out:
41. up(&dev->sem);
42. return retval;
43.}