LDD3中scull字符设备源代码完全解析(三) ioctl方法

以下是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方法的源码了,就到这里,谢谢阅读。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值