static long uinput_ioctl_handler()

in misc/uinput.c [839:1037]


static long uinput_ioctl_handler(struct file *file, unsigned int cmd,
				 unsigned long arg, void __user *p)
{
	int			retval;
	struct uinput_device	*udev = file->private_data;
	struct uinput_ff_upload ff_up;
	struct uinput_ff_erase  ff_erase;
	struct uinput_request   *req;
	char			*phys;
	const char		*name;
	unsigned int		size;

	retval = mutex_lock_interruptible(&udev->mutex);
	if (retval)
		return retval;

	if (!udev->dev) {
		udev->dev = input_allocate_device();
		if (!udev->dev) {
			retval = -ENOMEM;
			goto out;
		}
	}

	switch (cmd) {
	case UI_GET_VERSION:
		if (put_user(UINPUT_VERSION, (unsigned int __user *)p))
			retval = -EFAULT;
		goto out;

	case UI_DEV_CREATE:
		retval = uinput_create_device(udev);
		goto out;

	case UI_DEV_DESTROY:
		uinput_destroy_device(udev);
		goto out;

	case UI_DEV_SETUP:
		retval = uinput_dev_setup(udev, p);
		goto out;

	/* UI_ABS_SETUP is handled in the variable size ioctls */

	case UI_SET_EVBIT:
		retval = uinput_set_bit(arg, evbit, EV_MAX);
		goto out;

	case UI_SET_KEYBIT:
		retval = uinput_set_bit(arg, keybit, KEY_MAX);
		goto out;

	case UI_SET_RELBIT:
		retval = uinput_set_bit(arg, relbit, REL_MAX);
		goto out;

	case UI_SET_ABSBIT:
		retval = uinput_set_bit(arg, absbit, ABS_MAX);
		goto out;

	case UI_SET_MSCBIT:
		retval = uinput_set_bit(arg, mscbit, MSC_MAX);
		goto out;

	case UI_SET_LEDBIT:
		retval = uinput_set_bit(arg, ledbit, LED_MAX);
		goto out;

	case UI_SET_SNDBIT:
		retval = uinput_set_bit(arg, sndbit, SND_MAX);
		goto out;

	case UI_SET_FFBIT:
		retval = uinput_set_bit(arg, ffbit, FF_MAX);
		goto out;

	case UI_SET_SWBIT:
		retval = uinput_set_bit(arg, swbit, SW_MAX);
		goto out;

	case UI_SET_PROPBIT:
		retval = uinput_set_bit(arg, propbit, INPUT_PROP_MAX);
		goto out;

	case UI_SET_PHYS:
		if (udev->state == UIST_CREATED) {
			retval = -EINVAL;
			goto out;
		}

		phys = strndup_user(p, 1024);
		if (IS_ERR(phys)) {
			retval = PTR_ERR(phys);
			goto out;
		}

		kfree(udev->dev->phys);
		udev->dev->phys = phys;
		goto out;

	case UI_BEGIN_FF_UPLOAD:
		retval = uinput_ff_upload_from_user(p, &ff_up);
		if (retval)
			goto out;

		req = uinput_request_find(udev, ff_up.request_id);
		if (!req || req->code != UI_FF_UPLOAD ||
		    !req->u.upload.effect) {
			retval = -EINVAL;
			goto out;
		}

		ff_up.retval = 0;
		ff_up.effect = *req->u.upload.effect;
		if (req->u.upload.old)
			ff_up.old = *req->u.upload.old;
		else
			memset(&ff_up.old, 0, sizeof(struct ff_effect));

		retval = uinput_ff_upload_to_user(p, &ff_up);
		goto out;

	case UI_BEGIN_FF_ERASE:
		if (copy_from_user(&ff_erase, p, sizeof(ff_erase))) {
			retval = -EFAULT;
			goto out;
		}

		req = uinput_request_find(udev, ff_erase.request_id);
		if (!req || req->code != UI_FF_ERASE) {
			retval = -EINVAL;
			goto out;
		}

		ff_erase.retval = 0;
		ff_erase.effect_id = req->u.effect_id;
		if (copy_to_user(p, &ff_erase, sizeof(ff_erase))) {
			retval = -EFAULT;
			goto out;
		}

		goto out;

	case UI_END_FF_UPLOAD:
		retval = uinput_ff_upload_from_user(p, &ff_up);
		if (retval)
			goto out;

		req = uinput_request_find(udev, ff_up.request_id);
		if (!req || req->code != UI_FF_UPLOAD ||
		    !req->u.upload.effect) {
			retval = -EINVAL;
			goto out;
		}

		req->retval = ff_up.retval;
		complete(&req->done);
		goto out;

	case UI_END_FF_ERASE:
		if (copy_from_user(&ff_erase, p, sizeof(ff_erase))) {
			retval = -EFAULT;
			goto out;
		}

		req = uinput_request_find(udev, ff_erase.request_id);
		if (!req || req->code != UI_FF_ERASE) {
			retval = -EINVAL;
			goto out;
		}

		req->retval = ff_erase.retval;
		complete(&req->done);
		goto out;
	}

	size = _IOC_SIZE(cmd);

	/* Now check variable-length commands */
	switch (cmd & ~IOCSIZE_MASK) {
	case UI_GET_SYSNAME(0):
		if (udev->state != UIST_CREATED) {
			retval = -ENOENT;
			goto out;
		}
		name = dev_name(&udev->dev->dev);
		retval = uinput_str_to_user(p, name, size);
		goto out;

	case UI_ABS_SETUP & ~IOCSIZE_MASK:
		retval = uinput_abs_setup(udev, p, size);
		goto out;
	}

	retval = -EINVAL;
 out:
	mutex_unlock(&udev->mutex);
	return retval;
}