Linux驱动开发之LDD3中第三章scull注释详解

  1. #include <linux/module.h>  
  2. #include <linux/moduleparam.h>  
  3. #include <linux/init.h>  
  4. #include <linux/kernel.h> /* printk() */  
  5. #include <linux/slab.h>       /* kmalloc() */  
  6. #include <linux/fs.h>     /* everything... */  
  7. #include <linux/errno.h>  /* error codes */  
  8. #include <linux/types.h>  /* size_t */  
  9. #include <linux/fcntl.h>  /* O_ACCMODE */  
  10. #include <linux/cdev.h>  
  11. #include <asm/system.h>       /* cli(), *_flags */  
  12. #include <asm/uaccess.h>  /* copy_*_user */  
  13. #include "scull.h"      /* local definitions */   
  14. /*  
  15.  * Our parameters which can be set at load time.  
  16.  */  
  17. //主设备号   
  18. int scull_major =   SCULL_MAJOR;   
  19. //次设备号   
  20. int scull_minor =   0;   
  21. //请求连续设备编号数量   
  22. int scull_nr_devs = SCULL_NR_DEVS;  /* number of bare scull devices */  
  23. //量子大小   
  24. int scull_quantum = SCULL_QUANTUM;   
  25. //量子集大小   
  26. int scull_qset =    SCULL_QSET;   
  27. module_param(scull_major, int, S_IRUGO);   
  28. module_param(scull_minor, int, S_IRUGO);   
  29. module_param(scull_nr_devs, int, S_IRUGO);   
  30. module_param(scull_quantum, int, S_IRUGO);   
  31. module_param(scull_qset, int, S_IRUGO);   
  32. struct scull_dev *scull_devices;    /* allocated in scull_init_module */  
  33. /*  
  34.  * Empty out the scull device; must be called with the device  
  35.  * semaphore held.  
  36.  */  
  37. /*  
  38.  * 释放整个数据区,简单遍历列表并且释放它发现的任何量子和量子集。  
  39.  * 在scull_open在文件为写而打开时调用。  
  40.  * 调用这个函数时必须持有信号量。  
  41.  */  
  42. int scull_trim(struct scull_dev *dev)   
  43. {   
  44.     struct scull_qset *next, *dptr;   
  45.     //量子集大小   
  46.     int qset = dev->qset;   /* "dev" is not-null */  
  47.     int i;   
  48.     for (dptr = dev->data; dptr; dptr = next) { /* all the list items */  
  49.         if (dptr->data) {//量子集中有数据   
  50.             //遍历释放当前量子集中的每个量子,量子集大小为qset   
  51.             for (i = 0; i < qset; i++)   
  52.                 kfree(dptr->data[i]);   
  53.             //释放量子数组指针   
  54.             kfree(dptr->data);   
  55.             dptr->data = NULL;   
  56.         }   
  57.         //next获取下一个量子集,释放当前量子集   
  58.         next = dptr->next;   
  59.         kfree(dptr);   
  60.     }   
  61.     //清理struct scull_dev dev中的变量的值   
  62.     dev->size = 0;   
  63.     dev->quantum = scull_quantum;   
  64.     dev->qset = scull_qset;   
  65.     dev->data = NULL;   
  66.     return 0;   
  67. }   
  68. /*  
  69.  * Open and close  
  70.  */  
  71. int scull_open(struct inode *inode, struct file *filp)   
  72. {   
  73.     struct scull_dev *dev; /* device information */  
  74.     dev = container_of(inode->i_cdev, struct scull_dev, cdev);   
  75.     filp->private_data = dev; /* for other methods */  
  76.     /* now trim to 0 the length of the device if open was write-only */  
  77.     //文件以只读模式打开时,截断为0   
  78.         if ( (filp->f_flags & O_ACCMODE) == O_WRONLY) {   
  79.         if (down_interruptible(&dev->sem))   
  80.             return -ERESTARTSYS;   
  81.         scull_trim(dev); /* ignore errors */  
  82.         up(&dev->sem);   
  83.     }   
  84.     return 0;          /* success */  
  85. }   
  86. int scull_release(struct inode *inode, struct file *filp)   
  87. {   
  88.     return 0;   
  89. }   
  90. /*  
  91.  * Follow the list  
  92.  */  
  93. //返回设备dev的第n个量子集的指针,量子集不够n个就申请新的   
  94. struct scull_qset *scull_follow(struct scull_dev *dev, int n)   
  95. {   
  96.     //第一个量子集指针   
  97.     struct scull_qset *qs = dev->data;   
  98.     /* Allocate first qset explicitly if need be */  
  99.     // 如果当前设备还没有量子集,就显示分配第一个量子集   
  100.     if (! qs) {   
  101.         qs = dev->data = kmalloc(sizeof(struct scull_qset), GFP_KERNEL);   
  102.         if (qs == NULL)   
  103.             return NULL;  /* Never mind */  
  104.         memset(qs, 0, sizeof(struct scull_qset));   
  105.     }   
  106.     /* Then follow the list */  
  107.     // 遍历当前设备的量子集链表n步,量子集不够就申请新的   
  108.     while (n--) {   
  109.         if (!qs->next) {   
  110.             qs->next = kmalloc(sizeof(struct scull_qset), GFP_KERNEL);   
  111.             if (qs->next == NULL)   
  112.                 return NULL;  /* Never mind */  
  113.             memset(qs->next, 0, sizeof(struct scull_qset));   
  114.         }   
  115.         qs = qs->next;   
  116.         continue;   
  117.     }   
  118.     return qs;   
  119. }   
  120. /*  
  121.  * Data management: read and write  
  122.  */  
  123. ssize_t scull_read( struct file *filp, //设备对应的文件结构    
  124.                     char __user *buf,  //读到用户空间   
  125.                     size_t count,      //字节数   
  126.                     loff_t *f_pos)     //要读的位置,在filp私有数据中的偏移   
  127. {   
  128.     struct scull_dev *dev = filp->private_data;    
  129.     struct scull_qset *dptr;    /* the first listitem */  
  130.     //量子、量子集大小   
  131.     int quantum = dev->quantum, qset = dev->qset;   
  132.     //一个量子集的字节数   
  133.     int itemsize = quantum * qset; /* how many bytes in the listitem */  
  134.     int item, s_pos, q_pos, rest;   
  135.     ssize_t retval = 0;   
  136.     if (down_interruptible(&dev->sem))   
  137.         return -ERESTARTSYS;   
  138.     //要读的位置超过了数据总量   
  139.     if (*f_pos >= dev->size)   
  140.         goto out;   
  141.     //要读的count超出了size,截断count   
  142.     if (*f_pos + count > dev->size)   
  143.         count = dev->size - *f_pos;   
  144.     /* find listitem, qset index, and offset in the quantum */  
  145.     //在量子/量子集中定位读写位置:第几个量子集,中的第几个量子,在量子中偏移   
  146.     //第几个量子集   
  147.     item = (long)*f_pos / itemsize;   
  148.     //在量子集中的偏移量   
  149.     rest = (long)*f_pos % itemsize;   
  150.     //第几个量子,在量子中的偏移   
  151.     s_pos = rest / quantum; q_pos = rest % quantum;   
  152.     /* follow the list up to the right position (defined elsewhere) */  
  153.     //读取要读的量子集的指针   
  154.     dptr = scull_follow(dev, item);   
  155.     //读取出错处理   
  156.     if (dptr == NULL || !dptr->data || ! dptr->data[s_pos])   
  157.         goto out/* don't fill holes */  
  158.     /* read only up to the end of this quantum */  
  159.     // 只在一个量子中读:如果count超出当前量子,截断count   
  160.     if (count > quantum - q_pos)   
  161.         count = quantum - q_pos;   
  162.     // 将要读位置的内容复制count字节到用户空间buf中   
  163.     if (copy_to_user(buf, dptr->data[s_pos] + q_pos, count)) {   
  164.         retval = -EFAULT;   
  165.         goto out;   
  166.     }   
  167.     *f_pos += count;   
  168.     retval = count;   
  169.   out:   
  170.     up(&dev->sem);   
  171.     return retval;   
  172. }   
  173. ssize_t scull_write(struct file *filp, const char __user *buf, size_t count,   
  174.                 loff_t *f_pos)   
  175. {   
  176.     struct scull_dev *dev = filp->private_data;   
  177.     struct scull_qset *dptr;   
  178.     //量子、量子集大小   
  179.     int quantum = dev->quantum, qset = dev->qset;   
  180.     // 一个量子集总字节数   
  181.     int itemsize = quantum * qset;   
  182.     int item, s_pos, q_pos, rest;   
  183.     ssize_t retval = -ENOMEM; /* value used in "goto out" statements */  
  184.     if (down_interruptible(&dev->sem))   
  185.         return -ERESTARTSYS;   
  186.     /* find listitem, qset index and offset in the quantum */  
  187.     //第几个量子集   
  188.     item = (long)*f_pos / itemsize;   
  189.     //在该量子集中的偏移   
  190.     rest = (long)*f_pos % itemsize;   
  191.     //在该量子集中的第几个量子,在量子中的偏移   
  192.     s_pos = rest / quantum; q_pos = rest % quantum;   
  193.     /* follow the list up to the right position */  
  194.     //返回该量子集的指针   
  195.     dptr = scull_follow(dev, item);   
  196.     if (dptr == NULL)   
  197.         goto out;   
  198.     //如果该量子集数据为NULL,就申请一块新内存   
  199.     if (!dptr->data) {   
  200.         dptr->data = kmalloc(qset * sizeof(char *), GFP_KERNEL);   
  201.         if (!dptr->data)   
  202.             goto out;   
  203.         memset(dptr->data, 0, qset * sizeof(char *));   
  204.     }   
  205.     //如果第s_pos个量子是NULL,就申请一块新内存   
  206.     if (!dptr->data[s_pos]) {   
  207.         dptr->data[s_pos] = kmalloc(quantum, GFP_KERNEL);   
  208.         if (!dptr->data[s_pos])   
  209.             goto out;   
  210.     }   
  211.     /* write only up to the end of this quantum */  
  212.     // 只在一个量子中写,如果count超出当前量子就截断   
  213.     if (count > quantum - q_pos)   
  214.         count = quantum - q_pos;   
  215.     //从用户空间拷贝数据到内核空间,失败返回没有拷贝的字节数,成功返回0   
  216.     if (copy_from_user(dptr->data[s_pos]+q_pos, buf, count)) {   
  217.         retval = -EFAULT;   
  218.         goto out;   
  219.     }   
  220.     *f_pos += count;   
  221.     retval = count;   
  222.     /* update the size */  
  223.     // 更新字节总数大小   
  224.     if (dev->size < *f_pos)   
  225.         dev->size = *f_pos;   
  226.   out:   
  227.     up(&dev->sem);   
  228.     return retval;   
  229. }   
  230.   
  231. struct file_operations scull_fops = {   
  232.     .owner =    THIS_MODULE,   
  233.     .read =     scull_read,   
  234.     .write =    scull_write,   
  235.     .open =     scull_open,   
  236.     .release =  scull_release,   
  237. };   
  238. /*  
  239.  * Finally, the module stuff  
  240.  */  
  241. /*  
  242.  * The cleanup function is used to handle initialization failures as well.  
  243.  * Thefore, it must be careful to work correctly even if some of the items  
  244.  * have not been initialized  
  245.  */  
  246. void scull_cleanup_module(void)   
  247. {   
  248.     int i;   
  249.     //主次设备号合成一个dev_t结构,即设备编号   
  250.     dev_t devno = MKDEV(scull_major, scull_minor);   
  251.     /* Get rid of our char dev entries */  
  252.     if (scull_devices) {   
  253.         //便利释放每个设备的数据区   
  254.         for (i = 0; i < scull_nr_devs; i++) {   
  255.             //释放数据区   
  256.             scull_trim(scull_devices + i);   
  257.             //移除cdev   
  258.             cdev_del(&scull_devices[i].cdev);   
  259.         }   
  260.         //释放scull_devices本身   
  261.         kfree(scull_devices);   
  262.     }   
  263.     /* cleanup_module is never called if registering failed */  
  264.     unregister_chrdev_region(devno, scull_nr_devs);   
  265. }   
  266.   
  267. /*  
  268.  * Set up the char_dev structure for this device.  
  269.  */  
  270. // 建立char_dev结构   
  271. static void scull_setup_cdev(struct scull_dev *dev, int index)   
  272. {   
  273.     int err, devno = MKDEV(scull_major, scull_minor + index);   
  274.        
  275.     cdev_init(&dev->cdev, &scull_fops);   
  276.     dev->cdev.owner = THIS_MODULE;   
  277. //  dev->cdev.ops = &scull_fops;   
  278.     //添加字符设备dev->cdev,立即生效   
  279.     err = cdev_add (&dev->cdev, devno, 1);   
  280.     /* Fail gracefully if need be */  
  281.     if (err)   
  282.         printk(KERN_NOTICE "Error %d adding scull%d", err, index);   
  283. }   
  284.   
  285. int scull_init_module(void)   
  286. {   
  287.     int result, i;   
  288.     dev_t dev = 0;   
  289. /*  
  290.  * Get a range of minor numbers to work with, asking for a dynamic  
  291.  * major unless directed otherwise at load time.  
  292.  */  
  293.     //申请设备编号,若在加载时没有指定主设备号就动态分配   
  294.     if (scull_major) {   
  295.         dev = MKDEV(scull_major, scull_minor);   
  296.         result = register_chrdev_region(dev, scull_nr_devs, "scull");   
  297.     } else {   
  298.         result = alloc_chrdev_region(&dev, scull_minor, scull_nr_devs,   
  299.                 "scull");   
  300.         scull_major = MAJOR(dev);   
  301.     }   
  302.     if (result < 0) {   
  303.         printk(KERN_WARNING "scull: can't get major %d\n", scull_major);   
  304.         return result;   
  305.     }   
  306.         /*   
  307.      * allocate the devices -- we can't have them static, as the number  
  308.      * can be specified at load time  
  309.      */  
  310.     //给scull_dev对象申请内存   
  311.     scull_devices = kmalloc(scull_nr_devs * sizeof(struct scull_dev), GFP_KERNEL);   
  312.     if (!scull_devices) {   
  313.         result = -ENOMEM;   
  314.         goto fail;  /* Make this more graceful */  
  315.     }   
  316.     memset(scull_devices, 0, scull_nr_devs * sizeof(struct scull_dev));   
  317.         /* Initialize each device. */  
  318.     for (i = 0; i < scull_nr_devs; i++) {   
  319.         scull_devices[i].quantum = scull_quantum;   
  320.         scull_devices[i].qset = scull_qset;   
  321.         //初始化互斥锁,把信号量sem置为1   
  322.         sema_init(&scull_devices[i].sem, 1);       
  323.         //init_MUTEX(&scull_devices[i].sem);   
  324.         //建立char_dev结构   
  325.         scull_setup_cdev(&scull_devices[i], i);   
  326.         }   
  327.     return 0; /* succeed */  
  328.   fail:   
  329.     scull_cleanup_module();   
  330.     return result;   
  331. }   
  332. module_init(scull_init_module);   
  333. module_exit(scull_cleanup_module);   
  334. MODULE_AUTHOR("Tekkamanninja");   
  335. MODULE_LICENSE("Dual BSD/GPL");  
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值