ScullC ioctl

long scullc_ioctl(struct file *filp,
                  unsigned int cmd, unsigned long arg)
{

int err = 0, ret = 0, tmp;

/* don't even decode wrong cmds: better returning  ENOTTY than EFAULT */
if (_IOC_TYPE(cmd) != SCULLC_IOC_MAGIC)
   return -ENOTTY;
if (_IOC_NR(cmd) > SCULLC_IOC_MAXNR)
   return -ENOTTY;

/*
   * the type is a bitmask, and VERIFY_WRITE catches R/W
   * transfers. Note that the type is user-oriented, while
   * verify_area is kernel-oriented, so the concept of "read" and
   * "write" is reversed
   */
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 SCULLC_IOCRESET:
   scullc_qset = SCULLC_QSET;
   scullc_quantum = SCULLC_QUANTUM;
   break;

case SCULLC_IOCSQUANTUM: /* Set: arg points to the value */
   ret = __get_user(scullc_quantum, (int __user *)arg);
   break;

case SCULLC_IOCTQUANTUM: /* Tell: arg is the value */
   scullc_quantum = arg;
   break;

case SCULLC_IOCGQUANTUM: /* Get: arg is pointer to result */
   ret = __put_user(scullc_quantum, (int __user *)arg);
   break;

case SCULLC_IOCQQUANTUM: /* Query: return it (it's positive) */
   return scullc_quantum;

case SCULLC_IOCXQUANTUM: /* eXchange: use arg as pointer */
   tmp = scullc_quantum;
   ret = __get_user(scullc_quantum, (int __user *)arg);
   if (ret == 0)
      ret = __put_user(tmp, (int __user *)arg);
   break;

case SCULLC_IOCHQUANTUM: /* sHift: like Tell + Query */
   tmp = scullc_quantum;
   scullc_quantum = arg;
   return tmp;

case SCULLC_IOCSQSET:
   ret = __get_user(scullc_qset, (int __user *)arg);
   break;

case SCULLC_IOCTQSET:
   scullc_qset = arg;
   break;

case SCULLC_IOCGQSET:
   ret = __put_user(scullc_qset, (int __user *)arg);
   break;

case SCULLC_IOCQQSET:
   return scullc_qset;

case SCULLC_IOCXQSET:
   tmp = scullc_qset;
   ret = __get_user(scullc_qset, (int __user *)arg);
   if (ret == 0)
      ret = __put_user(tmp, (int __user *)arg);
   break;

case SCULLC_IOCHQSET:
   tmp = scullc_qset;
   scullc_qset = arg;
   return tmp;

default: /* redundant, as cmd was checked against MAXNR */
   return -ENOTTY;
}

return ret;
}

loff_t scullc_llseek(struct file *filp, loff_t off, int whence)
{
struct scullc_dev *dev = filp->private_data;
long newpos;

switch (whence)
{
case 0: /* SEEK_SET */
   newpos = off;
   break;

case 1: /* SEEK_CUR */
   newpos = filp->f_pos + off;
   break;

case 2: /* SEEK_END */
   newpos = dev->size + off;
   break;

default: /* can't happen */
   return -EINVAL;
}
if (newpos < 0)
   return -EINVAL;
filp->f_pos = newpos;
return newpos;
}