Chinaunix首页 | 论坛 | 博客
  • 博客访问: 365447
  • 博文数量: 242
  • 博客积分: 10
  • 博客等级: 民兵
  • 技术积分: 1134
  • 用 户 组: 普通用户
  • 注册时间: 2012-10-20 10:53
文章分类

全部博文(242)

文章存档

2015年(1)

2014年(10)

2013年(18)

2012年(213)

分类:

2012-11-09 17:22:22

原文地址:对scull字符设备的注解 作者:txgc_wm

  1. #ifndef __SCULL_H__
  2. #define __SCULL_H__

  3. #ifndef SCULL_MAJOR
  4. #define SCULL_MAJOR 0
  5. #endif

  6. #ifndef SCULL_MINOR
  7. #define SCULL_MINOR 0
  8. #endif

  9. typedef struct _scull_dev_ {
  10.     void **data; /*数据存储区*/
  11.        int quantum; /*每块数据存储区的大小*/
  12.        int qset; /*数据存储区的块数*/
  13.        unsigned long size;    /*数据量*/
  14.        struct semaphore sem;
  15.     struct cdev cdev;

  16. } SCULL_DEV,pSCULL_DEV;

  17. #endif




  18. #ifndef __KERNEL__
  19. # define __KERNEL__
  20. #endif
  21. #ifndef MODULE
  22. # define MODULE
  23. #endif

  24. #include <linux/init.h>
  25. #include <linux/module.h>
  26. #include <linux/cdev.h>
  27. #include <linux/slab.h>
  28. #include <linux/kernel.h> /* printk() */
  29. #include <linux/fs.h> /* everything... */
  30. #include <linux/errno.h> /* error codes */
  31. #include <linux/types.h> /* size_t */
  32. #include <linux/proc_fs.h>
  33. #include <linux/fcntl.h> /* O_ACCMODE */
  34. #include <asm/system.h> /* cli(), *_flags */
  35. #include <asm/uaccess.h>

  36. #include "scull.h" /* local definitions */


  37. int scull_major = SCULL_MAJOR;
  38. int scull_minor    = SCULL_MINOR;
  39. int scull_mn_dev = 1;
  40. int scull_quantum = 4000;    /*每个指针所指向存储单元的大小*/
  41. int scull_qset = 4000;    /*指针数组的大小*/
  42. static pSCULL_DEV *pscull_dev = NULL;


  43. int scull_trim(SCULL_DEV *pscull)
  44. {
  45.     int i = 0;
  46.     int qset = pscull->qset;

  47.     if (pscull->data)
  48.     {
  49.         for(i = 0; i < qset; i++)
  50.         {
  51.             if (pscull->data[i])
  52.                 kfree(pscull->data[i]);
  53.         }

  54.         kfree(pscull->data);
  55.         pscull->data=NULL;
  56.     }

  57.     pscull->size = 0;
  58.     pscull->quantum = scull_quantum;
  59.     pscull->qset = scull_qset;
  60.  
  61.     return 0;
  62. }

  63. /*open方法提供给驱动程序以初始化的能力。它应完成如下工作:1、检查设备特定的错误;2、如果设备是首次打开,则对其进行初始化;3、如有必要,更新f_op指针;4、分配并填写置于filp->private_data里的数据结构。*/
  64. int scull_open(struct inode *inode, struct file *filp)
  65. {
  66.     SCULL_DEV *pscull = NULL;

  67.     /*获取我们自定义结构的指针*/
  68.     pscull = container_of(inode->i_cdev,pSCULL_DEV,cdev);
  69.     /*将自定义结构的指针保存到file结构的private_data字段中,这样可以方便以后对该指针的访问*/
  70.     filp->private_data = pscull;
  71.     if ( (filp->f_flags & O_ACCMODE) == O_WRONLY)
  72.     {
  73.         scull_trim(pscull);
  74.     }

  75.     return 0;
  76. }

  77. /*release主要的工作是:1、释放由open分配的、保存在filp->private_data中的所有内容;2、在最后一次关闭操作时关闭设备。*/
  78. int scull_release(struct inode *inode, struct file *filp)
  79. {
  80.     return 0;
  81. }

  82. /*
  83.  *调用程序对read的返回值解释如下:
  84.  *1、如果返回值等于传递给read系统调用的count参数,则说明所请求的字节数传输成功完成了。
  85.  *2、如果返回值是正的,但是比count小,则说明只有部分数据成功传送。这种情况因为设备的不同可能有许多原因。大部分情况下,程序会重新读数据。例如,如果用fread函数读数据,这个库函数就会不断调用系统调用,直至所请求的数据传输完毕为止。
  86.  *3、如果返回值为0,则表示已经到达了文件尾。
  87.  *4、负值意味着发生了错误,该值指明了发生什么错误,错误码在<linux/errno.h>中定义
  88. */
  89. ssize_t scull_read(struct file *filp, char *buf, size_t count,loff_t *f_pos)
  90. {
  91.     SCULL_DEV *pscull = filp->private_data;
  92.     int quantum = pscull->quantum;
  93.     int qset = pscull->qset;
  94.     int s_pos = 0, q_pos = 0;
  95.     ssize_t ret = 0;

  96.     if(down_interruptible(&pscull->sem))
  97.         return -ERESTARTSYS;
  98.     
  99.     if(*f_pos >= pscull->size)
  100.         goto out;
  101.     
  102.     if(*f_pos + count > pscull->size)
  103.         count = pscull->size - *f_pos;
  104.     
  105.     s_pos = (long)*f_pos/quantum;
  106.     q_pos = (long)*f_pos%quantum;

  107.     if(s_pos > qset)
  108.         goto out;
  109.     
  110.     if(!pscull->data)
  111.         goto out;
  112.     
  113.     if(!pscull->data[s_pos])
  114.         goto out;
  115.    
  116.     if(count > quantum-q_pos)
  117.         count = quantum-q_pos;

  118.     /*将内核空间的数据拷贝到用户空间*/
  119.     if(copy_to_user(buf, pscull->data[s_pos]+q_pos, count))
  120.     {
  121.         ret = -EFAULT;
  122.         goto out;
  123.     }

  124.     *f_pos += count;
  125.     ret = count;

  126. out:
  127.     up(&pscull->sem);

  128.     return ret;    
  129. }

  130. /*
  131.  *调用程序对write的返回值解释如下:
  132.  *1、如果返回值等于count,则完成了所请求数目的字节传送。
  133.  *2、如果返回值是正的,但小于count,则只传送了部分数据。程序很可能再次试图写入余下的数据。
  134.  *3、如果值为0,意味着什么也没有写入。这个结果不是错误,而且也没有理由返回一个错误码。
  135.  *4、负值意味着发生了错误。
  136. */
  137. ssize_t scull_write(struct file *filp, const char *buf, size_t count,loff_t *f_pos)
  138. {
  139.     SCULL_DEV *pscull = filp->private_data;
  140.     int quantum = pscull->quantum;
  141.     int qset = pscull->qset;
  142.     int s_pos,q_pos;
  143.     ssize_t ret = -ENOMEM;

  144.     if(down_interruptible(&pscull->sem))
  145.         return -ERESTARTSYS;

  146.     s_pos = (long)*f_pos/quantum;
  147.     q_pos = (long)*f_pos%quantum;

  148.     if(!pscull->data)
  149.     {
  150.         pscull->data = kmalloc(qset*sizeof(char *), GFP_KERNEL);
  151.         if (!pscull->data)
  152.             goto out;
  153.         memset(pscull->data, 0, qset*sizeof(char *));
  154.     }

  155.     if(!pscull->data[s_pos])
  156.     {
  157.         pscull->data[s_pos] = kmalloc(quantum, GFP_KERNEL);
  158.         if (!pscull->data[s_pos])
  159.             goto out;
  160.         memset(pscull->data[s_pos],0,quantum);
  161.     }

  162.     if(count > quantum-q_pos)
  163.         count = quantum-q_pos;

  164.     /*将用户空间的数据拷贝到内核空间*/
  165.     if(copy_from_user(pscull->data[s_pos]+q_pos, buf, count))    
  166.     {
  167.         ret = -EFAULT;
  168.         goto out;
  169.     }

  170.     *f_pos += count;
  171.     ret = count;

  172.     if(pscull->size < *f_pos)
  173.         pscull->size = *f_pos;
  174.     
  175. out:
  176.     up(&pscull->sem);

  177.     return ret;
  178. }

  179. int scull_ioctl(struct inode *inode, struct file *filp,unsigned int cmd, unsigned long arg)
  180. {
  181.     return 0;
  182. }

  183. loff_t scull_llseek(struct file *filp, loff_t off, int whence)
  184. {
  185.     return 0;
  186. }

  187. struct file_operations scull_fops = {
  188.     .owner     =    THIS_MODULE,
  189.     .llseek    =    scull_llseek,
  190.     .read    =    scull_read,
  191.     .write    =    scull_write,
  192.     .ioctl    =    scull_ioctl,
  193.     .open    =    scull_open,
  194.     .release =    scull_release,
  195. };

  196. /*模块的清除函数*/
  197. static void scull_cleanup_module(void)
  198. {
  199.     dev_t dev;

  200.     /*获取起始设备号*/
  201.     dev = MKDEV(scull_major,scull_minor);
  202.     /*从系统中移除一个字符设备*/
  203.     cdev_del(&pscull_dev->cdev);
  204.     /*释放不再使用的设备编号,第一个参数是起始设备编号,第二个参数是需要释放的连续编号数*/
  205.     unregister_chrdev_region(dev,scull_mn_dev);
  206.     scull_trim(pscull_dev);
  207.     kfree(pscull_dev);
  208.     pscull_dev = NULL;    

  209.     return;
  210. }

  211. /*模块的初始化函数*/
  212. static int scull_init_module(void)
  213. {
  214.     /*在内核的2.6.0版本中,dev_t是一个32位的数,其中12位用来表示主设备号,而其余20位用来表示次设备号*/
  215.     dev_t dev;
  216.     int result = -1;

  217.     pscull_dev = kmalloc(sizeof(SCULL_DEV),GFP_KERNEL);
  218.     if(pscull_dev == NULL)
  219.     {
  220.         printk(KERN_WARNING"scull: can't kmalloc memory\n");
  221.         result = -ENOMEM;
  222.         return result;
  223.     }
  224.     memset(pscull_dev,0,sizeof(SCULL_DEV));
  225.     pscull_dev->data = NULL;
  226.     pscull_dev->quantum = scull_quantum;
  227.     pscull_dev->qset = scull_qset;

  228.     /*创建信号量,并将信号量的初始值赋值为1*/
  229.     sema_init(&pscull_dev->sem,1);

  230.     /*用来获取主设备号,可由程序指定(或在insmod的时候指定相应值)或采用内核动态分配的方式*/
  231.     if(scull_major)
  232.     {
  233.         /*将主设备号和次设备号转换成dev_t*/
  234.         dev = MKDEV(scull_major,scull_minor);    
  235.         /*获得设备的一个或多个编号,该种方式是由程序指定设备号,函数的第一个参数是设备号的起始值(通常次设备号都被设置成0),第二个参数表示所请求的连续设备编号的个数,第三个参数是和该编号范围关联的设备名称,它将出现在/proc/devices和sysfs中*/
  236.         result = register_chrdev_region(dev,scull_mn_dev,"scull");    
  237.     }
  238.     else
  239.     {
  240.         /*通常情况下,我们都不知道使用哪个设备号。使用以下的函数,内核将恰当的为我们分配所需的主设备号,第一个参数仅用于输出的参数,在成功完成调用后将保存已分配范围的第一个编号,第二个参数是要使用的被请求的第一个次设备号,第三个参数表示所请求的连续设备编号的个数,第四个参数是和该编号范围关联的设备名称,它将出现在/proc/devices和sysfs中*/
  241.         result = alloc_chrdev_region(&dev,scull_minor,scull_mn_dev,"scull");
  242.         /*获取主设备号,采用MINOR(dev)可以获取到次设备号*/
  243.         scull_major = MAJOR(dev);    
  244.     }

  245.     if(result < 0)
  246.     {
  247.         printk(KERN_WARNING"scull: can't get major %d\n",scull_major);
  248.         return result;
  249.     }

  250.     /*在内核内部,使用struct cdev结构来表示字符设备,以下函数可以初始化已分配到的cdev结构。因为本例中我们将cdev结构嵌入到自己的特定设备结构中,所以采用下面的函数。也可以使用cdev_alloc()来分配一个cdev字符设备结构*/
  251.     cdev_init(&pscull_dev->cdev,&scull_fops);
  252.     /*初始化cdev结构的所有者字段*/
  253.     pscull_dev->cdev.owner = THIS_MODULE;
  254.     /*初始化设备的文件操作接口结构的指针*/
  255.     pscull_dev->cdev.ops = &scull_fops;
  256.     /*将cdev结构告诉内核,第一个参数是struct cdev机构,第二个是该设备对应的第一个设备编号,第三个参数是和该设备关联的设备编号的数量。在使用cdev_add时,需要注意:首先,这个调用可能会失败。如果它返回一个负的错误码,则设备不会被添加到系统中。但这个调用总是会成功返回。只要cdev_add成功返回了,设备就启动了,它的操作就会被内核调用。因此,在驱动程序还没有完全准备好处理设备上的操作时,就不能调用cdev_add。*/
  257.     result = cdev_add(&pscull_dev->cdev,dev,1);
  258.     if(result)
  259.     {
  260.         printk(KERN_NOTICE"Error %d add scull device\n",result);
  261.         goto fail;
  262.     }

  263.     return 0;

  264. fail:
  265.     scull_cleanup_module();

  266.     return result;
  267. }



  268. /*用于指定模块的初始化和清除函数的宏*/
  269. module_init(scull_init_module);
  270. module_exit(scull_cleanup_module);

  271. /*在目标文件中添加关于模块的文档信息*/
  272. MODULE_AUTHOR("txgcwm");
  273. MODULE_VERSION("scull_v1.0");
  274. MODULE_LICENSE("GPL");
阅读(437) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~