static long comedi_unlocked_ioctl()

in comedi_fops.c [2111:2278]


static long comedi_unlocked_ioctl(struct file *file, unsigned int cmd,
				  unsigned long arg)
{
	unsigned int minor = iminor(file_inode(file));
	struct comedi_file *cfp = file->private_data;
	struct comedi_device *dev = cfp->dev;
	int rc;

	mutex_lock(&dev->mutex);

	/*
	 * Device config is special, because it must work on
	 * an unconfigured device.
	 */
	if (cmd == COMEDI_DEVCONFIG) {
		if (minor >= COMEDI_NUM_BOARD_MINORS) {
			/* Device config not appropriate on non-board minors. */
			rc = -ENOTTY;
			goto done;
		}
		rc = do_devconfig_ioctl(dev,
					(struct comedi_devconfig __user *)arg);
		if (rc == 0) {
			if (arg == 0 &&
			    dev->minor >= comedi_num_legacy_minors) {
				/*
				 * Successfully unconfigured a dynamically
				 * allocated device.  Try and remove it.
				 */
				if (comedi_clear_board_dev(dev)) {
					mutex_unlock(&dev->mutex);
					comedi_free_board_dev(dev);
					return rc;
				}
			}
		}
		goto done;
	}

	if (!dev->attached) {
		dev_dbg(dev->class_dev, "no driver attached\n");
		rc = -ENODEV;
		goto done;
	}

	switch (cmd) {
	case COMEDI_BUFCONFIG:
		rc = do_bufconfig_ioctl(dev,
					(struct comedi_bufconfig __user *)arg);
		break;
	case COMEDI_DEVINFO:
		rc = do_devinfo_ioctl(dev, (struct comedi_devinfo __user *)arg,
				      file);
		break;
	case COMEDI_SUBDINFO:
		rc = do_subdinfo_ioctl(dev,
				       (struct comedi_subdinfo __user *)arg,
				       file);
		break;
	case COMEDI_CHANINFO: {
		struct comedi_chaninfo it;

		if (copy_from_user(&it, (void __user *)arg, sizeof(it)))
			rc = -EFAULT;
		else
			rc = do_chaninfo_ioctl(dev, &it);
		break;
	}
	case COMEDI_RANGEINFO: {
		struct comedi_rangeinfo it;

		if (copy_from_user(&it, (void __user *)arg, sizeof(it)))
			rc = -EFAULT;
		else
			rc = do_rangeinfo_ioctl(dev, &it);
		break;
	}
	case COMEDI_BUFINFO:
		rc = do_bufinfo_ioctl(dev,
				      (struct comedi_bufinfo __user *)arg,
				      file);
		break;
	case COMEDI_LOCK:
		rc = do_lock_ioctl(dev, arg, file);
		break;
	case COMEDI_UNLOCK:
		rc = do_unlock_ioctl(dev, arg, file);
		break;
	case COMEDI_CANCEL:
		rc = do_cancel_ioctl(dev, arg, file);
		break;
	case COMEDI_CMD: {
		struct comedi_cmd cmd;
		bool copy = false;

		if (copy_from_user(&cmd, (void __user *)arg, sizeof(cmd))) {
			rc = -EFAULT;
			break;
		}
		rc = do_cmd_ioctl(dev, &cmd, &copy, file);
		if (copy && copy_to_user((void __user *)arg, &cmd, sizeof(cmd)))
			rc = -EFAULT;
		break;
	}
	case COMEDI_CMDTEST: {
		struct comedi_cmd cmd;
		bool copy = false;

		if (copy_from_user(&cmd, (void __user *)arg, sizeof(cmd))) {
			rc = -EFAULT;
			break;
		}
		rc = do_cmdtest_ioctl(dev, &cmd, &copy, file);
		if (copy && copy_to_user((void __user *)arg, &cmd, sizeof(cmd)))
			rc = -EFAULT;
		break;
	}
	case COMEDI_INSNLIST: {
		struct comedi_insnlist insnlist;
		struct comedi_insn *insns = NULL;

		if (copy_from_user(&insnlist, (void __user *)arg,
				   sizeof(insnlist))) {
			rc = -EFAULT;
			break;
		}
		insns = kcalloc(insnlist.n_insns, sizeof(*insns), GFP_KERNEL);
		if (!insns) {
			rc = -ENOMEM;
			break;
		}
		if (copy_from_user(insns, insnlist.insns,
				   sizeof(*insns) * insnlist.n_insns)) {
			rc = -EFAULT;
			kfree(insns);
			break;
		}
		rc = do_insnlist_ioctl(dev, insns, insnlist.n_insns, file);
		kfree(insns);
		break;
	}
	case COMEDI_INSN: {
		struct comedi_insn insn;

		if (copy_from_user(&insn, (void __user *)arg, sizeof(insn)))
			rc = -EFAULT;
		else
			rc = do_insn_ioctl(dev, &insn, file);
		break;
	}
	case COMEDI_POLL:
		rc = do_poll_ioctl(dev, arg, file);
		break;
	case COMEDI_SETRSUBD:
		rc = do_setrsubd_ioctl(dev, arg, file);
		break;
	case COMEDI_SETWSUBD:
		rc = do_setwsubd_ioctl(dev, arg, file);
		break;
	default:
		rc = -ENOTTY;
		break;
	}

done:
	mutex_unlock(&dev->mutex);
	return rc;
}