LDD3源码分析之ioctl操作

转载自http://blog.csdn.net/liuhaoyutz/article/details/7386254

作者:刘昊昱 

博客:http://blog.csdn.net/liuhaoyutz

编译环境:Ubuntu 10.10

内核版本:2.6.32-38-generic-pae

LDD3源码路径:examples/scull/main.c

 

本文分析LDD3章中关于ioctl操作的代码,并编写测试程序对ioctl功能进行测试。

 

一、ioctl操作

驱动程序中ioctl函数的函数原型如下:

int (*ioctl)(struct inode *inode, struct file *filp,

             unsigned int cmd, unsigned long arg);

其中cmdarg参数是ioctl与其它驱动程序函数不同的地方。cmd是预先定义好的一些命令编号,对应要求ioctl执行的命令。arg是与cmd配合使用的参数。

ioctl函数的功能比较繁琐,从函数名可以看出,它一般是实现对设备的各种控制操作。可以这样理解,通过常规的readwritelseek等等函数实现不合理的功能,就交给ioctl来实现。例如:要求设备锁门,弹出介质,改变波特率,甚至执行自我破坏,等等。

ioctl的实现一般是通过一个大的switch语句,根据cmd参数执行不同的操作。所以,在实现ioctl函数之前,要先定义好cmd对应的命令编号。为了防止发生混淆,命令编号应该在系统范围内是唯一的。为此,Linux内核将命令编号分为4个部分,即4个位段,分别是:

type: 幻数(magic number),它占8位。个人理解幻数就是一个标志,代表一个()对象。后面我们会看到,scull使用字符’k’作为幻数。

number:序数,即顺序编号,它也占8位。

direction:如果相关命令涉及到数据的传输,则这个位段表示数据传输的方向,可用的值包括_IOC_NONE(没有数据传输)_IOC_READ()_IOC_WRITE()_IOC_READ | _IOC_WRITE(双向传输数据)。注意,数据传输方向是从应用程序的角度看的,也就是说_IOC_READ意味着从设备中读数据,所以驱动程序必须向用户空间写数据。

size:所涉及的用户数据大小。这个位段的宽度与体系结构有关,通常是1314位。

<linux/ioctl.h>中包含的<asm/ioctl.h>头文件定义了一些构造命令编号的宏:

_IO(type, nr),用于构造无数据传输的命令编号。

_IOR(type, nr, datatype),用于构造从驱动程序中读取数据的命令编号。

_IOW(type, nr, datatype),用于构造向设备写入数据的命令编号。

_IOWR(type, nr, datatype),用于双向传输命令编号。

其中,typenumber位段从以上宏的参数中传入,size位段通过对datatype参数取sizeof获得。

另外,<asm/ioctl.h>头文件中还定义了一些用于解析命令编号的宏,如_IOC_DIR(cmd)_IOC_TYPE(cmd)_IOC_NR(cmd)_IOC_SIZE(cmd)

首先我们来看一下scull是如何定义命令编号的,理解scullioctl函数的实现,关键是理解这些命令是什么含义,即要求完成什么工作。在scull.h中有如下定义:

[cpp]  view plain copy
  1. 135/* 
  2. 136 * Ioctl definitions 
  3. 137 */  
  4. 138  
  5. 139/* Use 'k' as magic number */  
  6. 140#define SCULL_IOC_MAGIC  'k'  
  7. 141/* Please use a different 8-bit number in your code */  
  8. 142  
  9. 143#define SCULL_IOCRESET    _IO(SCULL_IOC_MAGIC, 0)  
  10. 144  
  11. 145/* 
  12. 146 * S means "Set" through a ptr, 
  13. 147 * T means "Tell" directly with the argument value 
  14. 148 * G means "Get": reply by setting through a pointer 
  15. 149 * Q means "Query": response is on the return value 
  16. 150 * X means "eXchange": switch G and S atomically 
  17. 151 * H means "sHift": switch T and Q atomically 
  18. 152 */  
  19. 153#define SCULL_IOCSQUANTUM _IOW(SCULL_IOC_MAGIC,  1, int)  
  20. 154#define SCULL_IOCSQSET    _IOW(SCULL_IOC_MAGIC,  2, int)  
  21. 155#define SCULL_IOCTQUANTUM _IO(SCULL_IOC_MAGIC,   3)  
  22. 156#define SCULL_IOCTQSET    _IO(SCULL_IOC_MAGIC,   4)  
  23. 157#define SCULL_IOCGQUANTUM _IOR(SCULL_IOC_MAGIC,  5, int)  
  24. 158#define SCULL_IOCGQSET    _IOR(SCULL_IOC_MAGIC,  6, int)  
  25. 159#define SCULL_IOCQQUANTUM _IO(SCULL_IOC_MAGIC,   7)  
  26. 160#define SCULL_IOCQQSET    _IO(SCULL_IOC_MAGIC,   8)  
  27. 161#define SCULL_IOCXQUANTUM _IOWR(SCULL_IOC_MAGIC, 9, int)  
  28. 162#define SCULL_IOCXQSET    _IOWR(SCULL_IOC_MAGIC,10, int)  
  29. 163#define SCULL_IOCHQUANTUM _IO(SCULL_IOC_MAGIC,  11)  
  30. 164#define SCULL_IOCHQSET    _IO(SCULL_IOC_MAGIC,  12)  
  31. 165  
  32. 166/* 
  33. 167 * The other entities only have "Tell" and "Query", because they're 
  34. 168 * not printed in the book, and there's no need to have all six. 
  35. 169 * (The previous stuff was only there to show different ways to do it. 
  36. 170 */  
  37. 171#define SCULL_P_IOCTSIZE _IO(SCULL_IOC_MAGIC,   13)  
  38. 172#define SCULL_P_IOCQSIZE _IO(SCULL_IOC_MAGIC,   14)  
  39. 173/* ... more to come */  
  40. 174  
  41. 175#define SCULL_IOC_MAXNR 14  

140行,定义scull的幻数是字符'k'

146行,’S’代表通过参数arg指向的内容设置。

147行,’T’代表直接通过参数arg的值设置。

148行,’G’代表通过参数arg指向的地址返回请求的值。

149行,’Q’代表通过ioctl函数的返回值返回请求的值。

150行,’X’代表通过参数arg指向的内容设置,再把原来的值通过arg指向的地址返回。即’S’’G’两个操作合为一步。

151行,’H’代表通过参数arg的值直接设置,再通过ioctl函数的返回值将原来的值返回。即’T’’Q’两个操作合为一步。

153行,定义命令SCULL_IOCSQUANTUM,该命令表示通过参数arg指向的内容设置quantum

154行,定义命令SCULL_IOCSQSET,该命令表示通过参数arg指向的内容设置qset

155行,定义命令SCULL_IOCTQUANTUM,该命令表示通过参数arg的值直接设置quantum

156行,定义命令SCULL_IOCTQSET,该命令表示通过参数arg的值直接设置qset

157行,定义命令SCULL_IOCGQUANTUM,该命令表示通过参数arg指向的地址返回quantum

158行,定义命令SCULL_IOCGQSET,该命令表示通过参数arg指向的地址返回qset

159行,定义命令SCULL_IOCQQUANTUM,该命令表示通过ioctl的返回值返回quantum

160行,定义命令SCULL_IOCQQSET,该命令表示通过ioctl的返回值返回qset

161行,定义命令SCULL_IOCXQUANTUM,该命令表示通过参数arg指向的内容设置quantum,然后,再把quantum原来的值写入arg指向的地址返回。

162行,定义命令SCULL_IOCXQSET,该命令表示通过参数arg指向的内容设置qset,然后,再把qset原来的值写入arg指向的地址返回。

163行,定义命令SCULL_IOCHQUANTUM,该命令表示通过参数arg的值直接设置quantum,然后,再通过ioctl的返回值返回quantum原来的值。

164行,定义命令SCULL_IOCHQSET,该命令表示通过参数arg的值直接设置qset,然后,再通过ioctl的返回值返回qset原来的值。

171行,定义命令SCULL_P_IOCTSIZE,该命令表示通过参数arg的值直接设置scull_p_buffer

172行,定义命令SCULL_P_IOCQSIZE,该命令表示通过ioctl的返回值返回scull_p_buffer

175定义SCULL_IOC_MAXNR14,代表一共有14个命令。

理解了scullioctl命令的含义,我们就可以看ioctl的代码了,下面列出scullioctl函数代码如下:

[cpp]  view plain copy
  1. 389/* 
  2. 390 * The ioctl() implementation 
  3. 391 */  
  4. 392  
  5. 393int scull_ioctl(struct inode *inode, struct file *filp,  
  6. 394                 unsigned int cmd, unsigned long arg)  
  7. 395{  
  8. 396  
  9. 397    int err = 0, tmp;  
  10. 398    int retval = 0;  
  11. 399  
  12. 400    /* 
  13. 401     * extract the type and number bitfields, and don't decode 
  14. 402     * wrong cmds: return ENOTTY (inappropriate ioctl) before access_ok() 
  15. 403     */  
  16. 404    if (_IOC_TYPE(cmd) != SCULL_IOC_MAGIC) return -ENOTTY;  
  17. 405    if (_IOC_NR(cmd) > SCULL_IOC_MAXNR) return -ENOTTY;  
  18. 406  
  19. 407    /* 
  20. 408     * the direction is a bitmask, and VERIFY_WRITE catches R/W 
  21. 409     * transfers. `Type' is user-oriented, while 
  22. 410     * access_ok is kernel-oriented, so the concept of "read" and 
  23. 411     * "write" is reversed 
  24. 412     */  
  25. 413    if (_IOC_DIR(cmd) & _IOC_READ)  
  26. 414        err = !access_ok(VERIFY_WRITE, (void __user *)arg, _IOC_SIZE(cmd));  
  27. 415    else if (_IOC_DIR(cmd) & _IOC_WRITE)  
  28. 416        err =  !access_ok(VERIFY_READ, (void __user *)arg, _IOC_SIZE(cmd));  
  29. 417    if (err) return -EFAULT;  
  30. 418  
  31. 419    switch(cmd) {  
  32. 420  
  33. 421      case SCULL_IOCRESET:  
  34. 422        scull_quantum = SCULL_QUANTUM;  
  35. 423        scull_qset = SCULL_QSET;  
  36. 424        break;  
  37. 425  
  38. 426      case SCULL_IOCSQUANTUM: /* Set: arg points to the value */  
  39. 427        if (! capable (CAP_SYS_ADMIN))  
  40. 428            return -EPERM;  
  41. 429        retval = __get_user(scull_quantum, (int __user *)arg);  
  42. 430        break;  
  43. 431  
  44. 432      case SCULL_IOCTQUANTUM: /* Tell: arg is the value */  
  45. 433        if (! capable (CAP_SYS_ADMIN))  
  46. 434            return -EPERM;  
  47. 435        scull_quantum = arg;  
  48. 436        break;  
  49. 437  
  50. 438      case SCULL_IOCGQUANTUM: /* Get: arg is pointer to result */  
  51. 439        retval = __put_user(scull_quantum, (int __user *)arg);  
  52. 440        break;  
  53. 441  
  54. 442      case SCULL_IOCQQUANTUM: /* Query: return it (it's positive) */  
  55. 443        return scull_quantum;  
  56. 444  
  57. 445      case SCULL_IOCXQUANTUM: /* eXchange: use arg as pointer */  
  58. 446        if (! capable (CAP_SYS_ADMIN))  
  59. 447            return -EPERM;  
  60. 448        tmp = scull_quantum;  
  61. 449        retval = __get_user(scull_quantum, (int __user *)arg);  
  62. 450        if (retval == 0)  
  63. 451            retval = __put_user(tmp, (int __user *)arg);  
  64. 452        break;  
  65. 453  
  66. 454      case SCULL_IOCHQUANTUM: /* sHift: like Tell + Query */  
  67. 455        if (! capable (CAP_SYS_ADMIN))  
  68. 456            return -EPERM;  
  69. 457        tmp = scull_quantum;  
  70. 458        scull_quantum = arg;  
  71. 459        return tmp;  
  72. 460  
  73. 461      case SCULL_IOCSQSET:  
  74. 462        if (! capable (CAP_SYS_ADMIN))  
  75. 463            return -EPERM;  
  76. 464        retval = __get_user(scull_qset, (int __user *)arg);  
  77. 465        break;  
  78. 466  
  79. 467      case SCULL_IOCTQSET:  
  80. 468        if (! capable (CAP_SYS_ADMIN))  
  81. 469            return -EPERM;  
  82. 470        scull_qset = arg;  
  83. 471        break;  
  84. 472  
  85. 473      case SCULL_IOCGQSET:  
  86. 474        retval = __put_user(scull_qset, (int __user *)arg);  
  87. 475        break;  
  88. 476  
  89. 477      case SCULL_IOCQQSET:  
  90. 478        return scull_qset;  
  91. 479  
  92. 480      case SCULL_IOCXQSET:  
  93. 481        if (! capable (CAP_SYS_ADMIN))  
  94. 482            return -EPERM;  
  95. 483        tmp = scull_qset;  
  96. 484        retval = __get_user(scull_qset, (int __user *)arg);  
  97. 485        if (retval == 0)  
  98. 486            retval = put_user(tmp, (int __user *)arg);  
  99. 487        break;  
  100. 488  
  101. 489      case SCULL_IOCHQSET:  
  102. 490        if (! capable (CAP_SYS_ADMIN))  
  103. 491            return -EPERM;  
  104. 492        tmp = scull_qset;  
  105. 493        scull_qset = arg;  
  106. 494        return tmp;  
  107. 495  
  108. 496        /* 
  109. 497         * The following two change the buffer size for scullpipe. 
  110. 498         * The scullpipe device uses this same ioctl method, just to 
  111. 499         * write less code. Actually, it's the same driver, isn't it? 
  112. 500         */  
  113. 501  
  114. 502      case SCULL_P_IOCTSIZE:  
  115. 503        scull_p_buffer = arg;  
  116. 504        break;  
  117. 505  
  118. 506      case SCULL_P_IOCQSIZE:  
  119. 507        return scull_p_buffer;  
  120. 508  
  121. 509  
  122. 510      default:  /* redundant, as cmd was checked against MAXNR */  
  123. 511        return -ENOTTY;  
  124. 512    }  
  125. 513    return retval;  
  126. 514  
  127. 515}  

404行,如果_IOC_TYPE(cmd) != SCULL_IOC_MAGIC,即cmd的幻数不是’k’,则退出。

405行,如果_IOC_NR(cmd) > SCULL_IOC_MAXNR,即cmd的序数大于14,则退出。

413 - 417行,如果要使用arg指向的地址进行数据的读或写,必须保证对该地址的访问是合法的,这可通过access_ok函数来验证,如果访问不合法,则退出。

419行,进入switch语句块。根据传入的cmd值,进入不同的分支执行。

420 - 512行,是个各cmd的处理分支,只要我们理解了各个cmd的含义,就很容易实现这些命令要求完成的工作。如果有不理解的地方,回到前面的各个cmd的定义处再研究一下。值得一提的是,驱动程序与用户空间传递数据,采用的是__put_user__get_user函数,相比copy_to_usercopy_from_user来说,这些函数在处理1248个字节的数据传输时,效率更高。另外,scull允许任何用户查询quantumqset的大小,但只允许被授权的用户修改quantumqset的值。这种权能的检查是通过capable()函数实现的。

 

二、测试ioctl

要测试scull驱动中ioctl函数是否实现了我们要求的功能,需要编写用户空间程序对scull模块进行测试。下面是我写的一个比较简单的测试程序:

首先是头文件scull_ioctl.h

[cpp]  view plain copy
  1. #ifndef _SCULL_IOCTL_H_  
  2. #define _SCULL_IOCTL_H_  
  3.   
  4. #include <linux/ioctl.h> /* needed for the _IOW etc stuff used later */  
  5.   
  6. /* 
  7.  * Ioctl definitions 
  8.  */  
  9.   
  10. /* Use 'k' as magic number */  
  11. #define SCULL_IOC_MAGIC  'k'  
  12. /* Please use a different 8-bit number in your code */  
  13.   
  14. #define SCULL_IOCRESET    _IO(SCULL_IOC_MAGIC, 0)  
  15.   
  16. /* 
  17.  * S means "Set" through a ptr, 
  18.  * T means "Tell" directly with the argument value 
  19.  * G means "Get": reply by setting through a pointer 
  20.  * Q means "Query": response is on the return value 
  21.  * X means "eXchange": switch G and S atomically 
  22.  * H means "sHift": switch T and Q atomically 
  23.  */  
  24. #define SCULL_IOCSQUANTUM _IOW(SCULL_IOC_MAGIC,  1, int)  
  25. #define SCULL_IOCSQSET    _IOW(SCULL_IOC_MAGIC,  2, int)  
  26. #define SCULL_IOCTQUANTUM _IO(SCULL_IOC_MAGIC,   3)  
  27. #define SCULL_IOCTQSET    _IO(SCULL_IOC_MAGIC,   4)  
  28. #define SCULL_IOCGQUANTUM _IOR(SCULL_IOC_MAGIC,  5, int)  
  29. #define SCULL_IOCGQSET    _IOR(SCULL_IOC_MAGIC,  6, int)  
  30. #define SCULL_IOCQQUANTUM _IO(SCULL_IOC_MAGIC,   7)  
  31. #define SCULL_IOCQQSET    _IO(SCULL_IOC_MAGIC,   8)  
  32. #define SCULL_IOCXQUANTUM _IOWR(SCULL_IOC_MAGIC, 9, int)  
  33. #define SCULL_IOCXQSET    _IOWR(SCULL_IOC_MAGIC,10, int)  
  34. #define SCULL_IOCHQUANTUM _IO(SCULL_IOC_MAGIC,  11)  
  35. #define SCULL_IOCHQSET    _IO(SCULL_IOC_MAGIC,  12)  
  36.   
  37. /* 
  38.  * The other entities only have "Tell" and "Query", because they're 
  39.  * not printed in the book, and there's no need to have all six. 
  40.  * (The previous stuff was only there to show different ways to do it. 
  41.  */  
  42. #define SCULL_P_IOCTSIZE _IO(SCULL_IOC_MAGIC,   13)  
  43. #define SCULL_P_IOCQSIZE _IO(SCULL_IOC_MAGIC,   14)  
  44. /* ... more to come */  
  45.   
  46. #define SCULL_IOC_MAXNR 14  
  47.   
  48. #endif /* _SCULL_IOCTL_H_ */  

下面是测试程序scull_ioctl_test.c的代码:

[cpp]  view plain copy
  1. #include <sys/types.h>  
  2. #include <sys/stat.h>  
  3. #include <sys/ioctl.h>  
  4. #include <fcntl.h>  
  5. #include <stdio.h>  
  6. #include "scull_ioctl.h"  
  7.   
  8. #define SCULL_DEVICE "/dev/scull0"  
  9.   
  10. int main(int argc, char *argv[])  
  11. {  
  12.     int fd = 0;  
  13.     int quantum = 8000;  
  14.     int quantum_old = 0;  
  15.     int qset = 2000;  
  16.     int qset_old = 0;  
  17.       
  18.     fd = open(SCULL_DEVICE, O_RDWR);  
  19.     if(fd < 0)  
  20.     {  
  21.         printf("open scull device error!\n");  
  22.         return 0;  
  23.     }  
  24.   
  25.     printf("SCULL_IOCSQUANTUM: quantum = %d\n", quantum);  
  26.     ioctl(fd, SCULL_IOCSQUANTUM, &quantum);  
  27.     quantum -= 500;  
  28.     printf("SCULL_IOCTQUANTUM: quantum = %d\n", quantum);      
  29.     ioctl(fd, SCULL_IOCTQUANTUM, quantum);  
  30.       
  31.     ioctl(fd, SCULL_IOCGQUANTUM, &quantum);  
  32.     printf("SCULL_IOCGQUANTUM: quantum = %d\n", quantum);      
  33.     quantum = ioctl(fd, SCULL_IOCQQUANTUM);  
  34.     printf("SCULL_IOCQQUANTUM: quantum = %d\n", quantum);      
  35.   
  36.     quantum -= 500;  
  37.     quantum_old = ioctl(fd, SCULL_IOCHQUANTUM, quantum);  
  38.     printf("SCULL_IOCHQUANTUM: quantum = %d, quantum_old = %d\n", quantum, quantum_old);      
  39.     quantum -= 500;  
  40.     printf("SCULL_IOCXQUANTUM: quantum = %d\n", quantum);  
  41.     ioctl(fd, SCULL_IOCXQUANTUM, &quantum);  
  42.     printf("SCULL_IOCXQUANTUM: old quantum = %d\n", quantum);  
  43.   
  44.     printf("SCULL_IOCSQSET: qset = %d\n", qset);  
  45.     ioctl(fd, SCULL_IOCSQSET, &qset);  
  46.     qset += 500;  
  47.     printf("SCULL_IOCTQSET: qset = %d\n", qset);  
  48.     ioctl(fd, SCULL_IOCTQSET, qset);  
  49.   
  50.     ioctl(fd, SCULL_IOCGQSET, &qset);  
  51.     printf("SCULL_IOCGQSET: qset = %d\n", qset);  
  52.     qset = ioctl(fd, SCULL_IOCQQSET);  
  53.     printf("SCULL_IOCQQSET: qset = %d\n", qset);  
  54.   
  55.     qset += 500;  
  56.     qset_old = ioctl(fd, SCULL_IOCHQSET, qset);  
  57.     printf("SCULL_IOCHQSET: qset = %d, qset_old = %d\n", qset, qset_old);      
  58.     qset += 500;  
  59.     printf("SCULL_IOCXQSET: qset = %d\n", qset);          
  60.     ioctl(fd, SCULL_IOCXQSET, &qset);  
  61.     printf("SCULL_IOCHQSET: old qset = %d\n", qset);  
  62.   
  63.     return 0;  
  64. }  

为了能看到测试效果,在修改驱动程序中的ioctl函数,打印一些语句。下面直接列出修改后的ioctl函数的实现:

[cpp]  view plain copy
  1. /* 
  2.  * The ioctl() implementation 
  3.  */  
  4.   
  5. int scull_ioctl(struct inode *inode, struct file *filp,  
  6.                  unsigned int cmd, unsigned long arg)  
  7. {  
  8.   
  9.     int err = 0, tmp;  
  10.     int retval = 0;  
  11.       
  12.     /* 
  13.      * extract the type and number bitfields, and don't decode 
  14.      * wrong cmds: return ENOTTY (inappropriate ioctl) before access_ok() 
  15.      */  
  16.     if (_IOC_TYPE(cmd) != SCULL_IOC_MAGIC) return -ENOTTY;  
  17.     if (_IOC_NR(cmd) > SCULL_IOC_MAXNR) return -ENOTTY;  
  18.   
  19.     /* 
  20.      * the direction is a bitmask, and VERIFY_WRITE catches R/W 
  21.      * transfers. `Type' is user-oriented, while 
  22.      * access_ok is kernel-oriented, so the concept of "read" and 
  23.      * "write" is reversed 
  24.      */  
  25.     if (_IOC_DIR(cmd) & _IOC_READ)  
  26.         err = !access_ok(VERIFY_WRITE, (void __user *)arg, _IOC_SIZE(cmd));  
  27.     else if (_IOC_DIR(cmd) & _IOC_WRITE)  
  28.         err =  !access_ok(VERIFY_READ, (void __user *)arg, _IOC_SIZE(cmd));  
  29.     if (err) return -EFAULT;  
  30.   
  31.     switch(cmd) {  
  32.   
  33.       case SCULL_IOCRESET:  
  34.         scull_quantum = SCULL_QUANTUM;  
  35.         scull_qset = SCULL_QSET;  
  36.         printk("SCULL_IOCRESET: scull_quantum = %d, scull_qset = %d\n", scull_quantum, scull_qset);  
  37.         break;  
  38.           
  39.       case SCULL_IOCSQUANTUM: /* Set: arg points to the value */  
  40.         if (! capable (CAP_SYS_ADMIN))  
  41.             return -EPERM;  
  42.         retval = __get_user(scull_quantum, (int __user *)arg);  
  43.         printk("SCULL_IOCSQUANTUM: scull_quantum = %d\n", scull_quantum);  
  44.         break;  
  45.   
  46.       case SCULL_IOCTQUANTUM: /* Tell: arg is the value */  
  47.         if (! capable (CAP_SYS_ADMIN))  
  48.             return -EPERM;  
  49.         scull_quantum = arg;  
  50.         printk("SCULL_IOCTQUANTUM: scull_quantum = %d\n", scull_quantum);  
  51.         break;  
  52.   
  53.       case SCULL_IOCGQUANTUM: /* Get: arg is pointer to result */  
  54.         retval = __put_user(scull_quantum, (int __user *)arg);  
  55.         printk("SCULL_IOCGQUANTUM: use arg return scull_quantum = %d\n", scull_quantum);  
  56.         break;  
  57.   
  58.       case SCULL_IOCQQUANTUM: /* Query: return it (it's positive) */  
  59.         printk("SCULL_IOCQQUANTUM: return scull_quantum = %d\n", scull_quantum);  
  60.         return scull_quantum;  
  61.   
  62.       case SCULL_IOCXQUANTUM: /* eXchange: use arg as pointer */  
  63.         if (! capable (CAP_SYS_ADMIN))  
  64.             return -EPERM;  
  65.         tmp = scull_quantum;  
  66.         retval = __get_user(scull_quantum, (int __user *)arg);  
  67.         if (retval == 0)  
  68.             retval = __put_user(tmp, (int __user *)arg);  
  69.         printk("SCULL_IOCXQUANTUM: scull_quantum = %d, and use arg return old scull_quantum = %d\n", scull_quantum, tmp);          
  70.         break;  
  71.   
  72.       case SCULL_IOCHQUANTUM: /* sHift: like Tell + Query */  
  73.         if (! capable (CAP_SYS_ADMIN))  
  74.             return -EPERM;  
  75.         tmp = scull_quantum;  
  76.         scull_quantum = arg;  
  77.         printk("SCULL_IOCHQUANTUM: scull_quantum = %d, and return old scull_quantum = %d\n", scull_quantum, tmp);          
  78.         return tmp;  
  79.           
  80.       case SCULL_IOCSQSET:  
  81.         if (! capable (CAP_SYS_ADMIN))  
  82.             return -EPERM;  
  83.         retval = __get_user(scull_qset, (int __user *)arg);  
  84.         printk("SCULL_IOCSQSET: scull_qset = %d\n", scull_qset);  
  85.         break;  
  86.   
  87.       case SCULL_IOCTQSET:  
  88.         if (! capable (CAP_SYS_ADMIN))  
  89.             return -EPERM;  
  90.         scull_qset = arg;  
  91.         printk("SCULL_IOCTQSET: scull_qset = %d\n", scull_qset);  
  92.         break;  
  93.   
  94.       case SCULL_IOCGQSET:  
  95.         retval = __put_user(scull_qset, (int __user *)arg);  
  96.         printk("SCULL_IOCGQSET: use arg return scull_qset = %d\n", scull_qset);          
  97.         break;  
  98.   
  99.       case SCULL_IOCQQSET:  
  100.         printk("SCULL_IOCQQSET: return scull_qset = %d\n", scull_qset);                    
  101.         return scull_qset;  
  102.   
  103.       case SCULL_IOCXQSET:  
  104.         if (! capable (CAP_SYS_ADMIN))  
  105.             return -EPERM;  
  106.         tmp = scull_qset;  
  107.         retval = __get_user(scull_qset, (int __user *)arg);  
  108.         if (retval == 0)  
  109.             retval = put_user(tmp, (int __user *)arg);  
  110.         printk("SCULL_IOCXQSET: scull_qset = %d, and use arg return old scull_qset = %d\n", scull_qset, tmp);          
  111.         break;  
  112.   
  113.       case SCULL_IOCHQSET:  
  114.         if (! capable (CAP_SYS_ADMIN))  
  115.             return -EPERM;  
  116.         tmp = scull_qset;  
  117.         scull_qset = arg;  
  118.         printk("SCULL_IOCHQSET: scull_qet = %d, and return old scull_qset = %d\n", scull_qset, tmp);                  
  119.         return tmp;  
  120.   
  121.         /* 
  122.          * The following two change the buffer size for scullpipe. 
  123.          * The scullpipe device uses this same ioctl method, just to 
  124.          * write less code. Actually, it's the same driver, isn't it? 
  125.          */  
  126.   
  127.       case SCULL_P_IOCTSIZE:  
  128.         scull_p_buffer = arg;  
  129.         break;  
  130.   
  131.       case SCULL_P_IOCQSIZE:  
  132.         return scull_p_buffer;  
  133.   
  134.   
  135.       default:  /* redundant, as cmd was checked against MAXNR */  
  136.         return -ENOTTY;  
  137.     }  
  138.     return retval;  
  139.   
  140. }  

在我的系统上,测试过程如图所示。需要注意的是测试程序必须以root权限运行,因为普通用户只能读quantumqset的值,只有root用户才能修改。

LDD3源码分析之ioctl操作_第1张图片


你可能感兴趣的:(IO,user,cmd,测试,IOC,Access)