以下是scull源代码中ioctl方法的部分,先整体浏览一下,接着慢慢分析。
int scull_ioctl(struct inode *inode, struct file *filp,unsigned int cmd, unsigned long arg)
{
int err = 0, tmp;
int retval = 0;
if (_IOC_TYPE(cmd) != SCULL_IOC_MAGIC) return -ENOTTY;
if (_IOC_NR(cmd) > SCULL_IOC_MAXNR) return -ENOTTY;
if (_IOC_DIR(cmd) & _IOC_READ)
err = !access_ok(VERIFY_WRITE, (void __user *)arg, _IOC_SIZE(cmd));
else if (_IOC_DIR(cmd) & _IOC_WRITE)
err = !access_ok(VERIFY_READ, (void __user *)arg, _IOC_SIZE(cmd));
if (err) return -EFAULT;
switch(cmd) {
case SCULL_IOCRESET:
scull_quantum = SCULL_QUANTUM;
scull_qset = SCULL_QSET;
break;
case SCULL_IOCSQUANTUM:
if (! capable (CAP_SYS_ADMIN))
return -EPERM;
retval = __get_user(scull_quantum, (int __user *)arg);
break;
case SCULL_IOCTQUANTUM: /* Tell: arg is the value */
if (! capable (CAP_SYS_ADMIN))
return -EPERM;
scull_quantum = arg;
break;
case SCULL_IOCGQUANTUM:
retval = __put_user(scull_quantum, (int __user *)arg);
break;
case SCULL_IOCQQUANTUM:
return scull_quantum;
case SCULL_IOCXQUANTUM:
if (! capable (CAP_SYS_ADMIN))
return -EPERM;
tmp = scull_quantum;
retval = __get_user(scull_quantum, (int __user *)arg);
if (retval == 0)
retval = __put_user(tmp, (int __user *)arg);
break;
case SCULL_IOCHQUANTUM:
if (! capable (CAP_SYS_ADMIN))
return -EPERM;
tmp = scull_quantum;
scull_quantum = arg;
return tmp;
case SCULL_IOCSQSET:
if (! capable (CAP_SYS_ADMIN))
return -EPERM;
retval = __get_user(scull_qset, (int __user *)arg);
break;
case SCULL_IOCTQSET:
if (! capable (CAP_SYS_ADMIN))
return -EPERM;
scull_qset = arg;
break;
case SCULL_IOCGQSET:
retval = __put_user(scull_qset, (int __user *)arg);
break;
case SCULL_IOCQQSET:
return scull_qset;
case SCULL_IOCXQSET:
if (! capable (CAP_SYS_ADMIN))
return -EPERM;
tmp = scull_qset;
retval = __get_user(scull_qset, (int __user *)arg);
if (retval == 0)
retval = put_user(tmp, (int __user *)arg);
break;
case SCULL_IOCHQSET:
if (! capable (CAP_SYS_ADMIN))
return -EPERM;
tmp = scull_qset;
scull_qset = arg;
return tmp;
case SCULL_P_IOCTSIZE:
scull_p_buffer = arg;
break;
case SCULL_P_IOCQSIZE:
return scull_p_buffer;
default:
return -ENOTTY;
}
return retval;
}
首先ioctl方法的原型是int (*ioctl) (struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg),这个方法是用于设备控制的公共接口。前两个参数我们已经很熟悉了,cmd是用户空间传递给驱动程序的命令,arg参数是可选的,由用户空间以规定的unsigned long的形式传递给驱动程序。
首先驱动程序要分析ioctl编号的位字段来检查命令号是否合法,利用_IOC_TYPE,_IOC_NR两个宏检查,如果不合法,按照POSIX标准的规定则放回-ENOTTY。接着利用access_ok函数检查要读写的用户空间地址是否合法。
接下来就是对cmd命令的判断选择不同的操作,以下分别阐述:
SCULL_IOCRESET:驱动设备内存空间的重置,主要对quantum、qset(以下也是对这两个变量的改变)
SCULL_IOCSQUANTUM:由于只有授权用户才能更改这两个值,先进行用户的级别判断,特权不够则返回。利用__get_user(scull_quantum, (int __user *)arg)获得用户空间的数据,arg指向参数值。
SCULL_IOCTQUANTUM: :此时arg本身就是参数值,赋给scull_quantum。
SCULL_IOCGQUANTUM:利用__put_user(scull_quantum, (int __user *)arg)把scull_quantum传到arg指向的用户空间。
SCULL_IOCQQUANTUM:比较简单,直接返回结果。
SCULL_IOCXQUANTUM:获得arg指向的值存到scull_quantum,并把先前scull_quantum的值传到arg指向的用户空间,达到交换的功能。
SCULL_IOCHQUANTUM::SCULL_IOCTQUANTUM+SCULL_IOCQQUANTUM
以下关于qset的操作与上面差不多,在此不再赘述,好了这就是ioctl方法的源码了,就到这里,谢谢阅读。