static int pp_do_ioctl()

in ppdev.c [352:665]


static int pp_do_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
	unsigned int minor = iminor(file_inode(file));
	struct pp_struct *pp = file->private_data;
	struct parport *port;
	void __user *argp = (void __user *)arg;
	struct ieee1284_info *info;
	unsigned char reg;
	unsigned char mask;
	int mode;
	s32 time32[2];
	s64 time64[2];
	struct timespec64 ts;
	int ret;

	/* First handle the cases that don't take arguments. */
	switch (cmd) {
	case PPCLAIM:
	    {
		if (pp->flags & PP_CLAIMED) {
			dev_dbg(&pp->pdev->dev, "you've already got it!\n");
			return -EINVAL;
		}

		/* Deferred device registration. */
		if (!pp->pdev) {
			int err = register_device(minor, pp);

			if (err)
				return err;
		}

		ret = parport_claim_or_block(pp->pdev);
		if (ret < 0)
			return ret;

		pp->flags |= PP_CLAIMED;

		/* For interrupt-reporting to work, we need to be
		 * informed of each interrupt. */
		pp_enable_irq(pp);

		/* We may need to fix up the state machine. */
		info = &pp->pdev->port->ieee1284;
		pp->saved_state.mode = info->mode;
		pp->saved_state.phase = info->phase;
		info->mode = pp->state.mode;
		info->phase = pp->state.phase;
		pp->default_inactivity = parport_set_timeout(pp->pdev, 0);
		parport_set_timeout(pp->pdev, pp->default_inactivity);

		return 0;
	    }
	case PPEXCL:
		if (pp->pdev) {
			dev_dbg(&pp->pdev->dev,
				"too late for PPEXCL; already registered\n");
			if (pp->flags & PP_EXCL)
				/* But it's not really an error. */
				return 0;
			/* There's no chance of making the driver happy. */
			return -EINVAL;
		}

		/* Just remember to register the device exclusively
		 * when we finally do the registration. */
		pp->flags |= PP_EXCL;
		return 0;
	case PPSETMODE:
	    {
		int mode;

		if (copy_from_user(&mode, argp, sizeof(mode)))
			return -EFAULT;
		/* FIXME: validate mode */
		pp->state.mode = mode;
		pp->state.phase = init_phase(mode);

		if (pp->flags & PP_CLAIMED) {
			pp->pdev->port->ieee1284.mode = mode;
			pp->pdev->port->ieee1284.phase = pp->state.phase;
		}

		return 0;
	    }
	case PPGETMODE:
	    {
		int mode;

		if (pp->flags & PP_CLAIMED)
			mode = pp->pdev->port->ieee1284.mode;
		else
			mode = pp->state.mode;

		if (copy_to_user(argp, &mode, sizeof(mode)))
			return -EFAULT;
		return 0;
	    }
	case PPSETPHASE:
	    {
		int phase;

		if (copy_from_user(&phase, argp, sizeof(phase)))
			return -EFAULT;

		/* FIXME: validate phase */
		pp->state.phase = phase;

		if (pp->flags & PP_CLAIMED)
			pp->pdev->port->ieee1284.phase = phase;

		return 0;
	    }
	case PPGETPHASE:
	    {
		int phase;

		if (pp->flags & PP_CLAIMED)
			phase = pp->pdev->port->ieee1284.phase;
		else
			phase = pp->state.phase;
		if (copy_to_user(argp, &phase, sizeof(phase)))
			return -EFAULT;
		return 0;
	    }
	case PPGETMODES:
	    {
		unsigned int modes;

		port = parport_find_number(minor);
		if (!port)
			return -ENODEV;

		modes = port->modes;
		parport_put_port(port);
		if (copy_to_user(argp, &modes, sizeof(modes)))
			return -EFAULT;
		return 0;
	    }
	case PPSETFLAGS:
	    {
		int uflags;

		if (copy_from_user(&uflags, argp, sizeof(uflags)))
			return -EFAULT;
		pp->flags &= ~PP_FLAGMASK;
		pp->flags |= (uflags & PP_FLAGMASK);
		return 0;
	    }
	case PPGETFLAGS:
	    {
		int uflags;

		uflags = pp->flags & PP_FLAGMASK;
		if (copy_to_user(argp, &uflags, sizeof(uflags)))
			return -EFAULT;
		return 0;
	    }
	}	/* end switch() */

	/* Everything else requires the port to be claimed, so check
	 * that now. */
	if ((pp->flags & PP_CLAIMED) == 0) {
		pr_debug(CHRDEV "%x: claim the port first\n", minor);
		return -EINVAL;
	}

	port = pp->pdev->port;
	switch (cmd) {
	case PPRSTATUS:
		reg = parport_read_status(port);
		if (copy_to_user(argp, &reg, sizeof(reg)))
			return -EFAULT;
		return 0;
	case PPRDATA:
		reg = parport_read_data(port);
		if (copy_to_user(argp, &reg, sizeof(reg)))
			return -EFAULT;
		return 0;
	case PPRCONTROL:
		reg = parport_read_control(port);
		if (copy_to_user(argp, &reg, sizeof(reg)))
			return -EFAULT;
		return 0;
	case PPYIELD:
		parport_yield_blocking(pp->pdev);
		return 0;

	case PPRELEASE:
		/* Save the state machine's state. */
		info = &pp->pdev->port->ieee1284;
		pp->state.mode = info->mode;
		pp->state.phase = info->phase;
		info->mode = pp->saved_state.mode;
		info->phase = pp->saved_state.phase;
		parport_release(pp->pdev);
		pp->flags &= ~PP_CLAIMED;
		return 0;

	case PPWCONTROL:
		if (copy_from_user(&reg, argp, sizeof(reg)))
			return -EFAULT;
		parport_write_control(port, reg);
		return 0;

	case PPWDATA:
		if (copy_from_user(&reg, argp, sizeof(reg)))
			return -EFAULT;
		parport_write_data(port, reg);
		return 0;

	case PPFCONTROL:
		if (copy_from_user(&mask, argp,
				   sizeof(mask)))
			return -EFAULT;
		if (copy_from_user(&reg, 1 + (unsigned char __user *) arg,
				   sizeof(reg)))
			return -EFAULT;
		parport_frob_control(port, mask, reg);
		return 0;

	case PPDATADIR:
		if (copy_from_user(&mode, argp, sizeof(mode)))
			return -EFAULT;
		if (mode)
			port->ops->data_reverse(port);
		else
			port->ops->data_forward(port);
		return 0;

	case PPNEGOT:
		if (copy_from_user(&mode, argp, sizeof(mode)))
			return -EFAULT;
		switch ((ret = parport_negotiate(port, mode))) {
		case 0: break;
		case -1: /* handshake failed, peripheral not IEEE 1284 */
			ret = -EIO;
			break;
		case 1:  /* handshake succeeded, peripheral rejected mode */
			ret = -ENXIO;
			break;
		}
		pp_enable_irq(pp);
		return ret;

	case PPWCTLONIRQ:
		if (copy_from_user(&reg, argp, sizeof(reg)))
			return -EFAULT;

		/* Remember what to set the control lines to, for next
		 * time we get an interrupt. */
		pp->irqctl = reg;
		pp->irqresponse = 1;
		return 0;

	case PPCLRIRQ:
		ret = atomic_read(&pp->irqc);
		if (copy_to_user(argp, &ret, sizeof(ret)))
			return -EFAULT;
		atomic_sub(ret, &pp->irqc);
		return 0;

	case PPSETTIME32:
		if (copy_from_user(time32, argp, sizeof(time32)))
			return -EFAULT;

		if ((time32[0] < 0) || (time32[1] < 0))
			return -EINVAL;

		return pp_set_timeout(pp->pdev, time32[0], time32[1]);

	case PPSETTIME64:
		if (copy_from_user(time64, argp, sizeof(time64)))
			return -EFAULT;

		if ((time64[0] < 0) || (time64[1] < 0))
			return -EINVAL;

		if (IS_ENABLED(CONFIG_SPARC64) && !in_compat_syscall())
			time64[1] >>= 32;

		return pp_set_timeout(pp->pdev, time64[0], time64[1]);

	case PPGETTIME32:
		jiffies_to_timespec64(pp->pdev->timeout, &ts);
		time32[0] = ts.tv_sec;
		time32[1] = ts.tv_nsec / NSEC_PER_USEC;

		if (copy_to_user(argp, time32, sizeof(time32)))
			return -EFAULT;

		return 0;

	case PPGETTIME64:
		jiffies_to_timespec64(pp->pdev->timeout, &ts);
		time64[0] = ts.tv_sec;
		time64[1] = ts.tv_nsec / NSEC_PER_USEC;

		if (IS_ENABLED(CONFIG_SPARC64) && !in_compat_syscall())
			time64[1] <<= 32;

		if (copy_to_user(argp, time64, sizeof(time64)))
			return -EFAULT;

		return 0;

	default:
		dev_dbg(&pp->pdev->dev, "What? (cmd=0x%x)\n", cmd);
		return -EINVAL;
	}

	/* Keep the compiler happy */
	return 0;
}