static long pps_cdev_ioctl()

in pps.c [90:246]


static long pps_cdev_ioctl(struct file *file,
		unsigned int cmd, unsigned long arg)
{
	struct pps_device *pps = file->private_data;
	struct pps_kparams params;
	void __user *uarg = (void __user *) arg;
	int __user *iuarg = (int __user *) arg;
	int err;

	switch (cmd) {
	case PPS_GETPARAMS:
		dev_dbg(pps->dev, "PPS_GETPARAMS\n");

		spin_lock_irq(&pps->lock);

		/* Get the current parameters */
		params = pps->params;

		spin_unlock_irq(&pps->lock);

		err = copy_to_user(uarg, &params, sizeof(struct pps_kparams));
		if (err)
			return -EFAULT;

		break;

	case PPS_SETPARAMS:
		dev_dbg(pps->dev, "PPS_SETPARAMS\n");

		/* Check the capabilities */
		if (!capable(CAP_SYS_TIME))
			return -EPERM;

		err = copy_from_user(&params, uarg, sizeof(struct pps_kparams));
		if (err)
			return -EFAULT;
		if (!(params.mode & (PPS_CAPTUREASSERT | PPS_CAPTURECLEAR))) {
			dev_dbg(pps->dev, "capture mode unspecified (%x)\n",
								params.mode);
			return -EINVAL;
		}

		/* Check for supported capabilities */
		if ((params.mode & ~pps->info.mode) != 0) {
			dev_dbg(pps->dev, "unsupported capabilities (%x)\n",
								params.mode);
			return -EINVAL;
		}

		spin_lock_irq(&pps->lock);

		/* Save the new parameters */
		pps->params = params;

		/* Restore the read only parameters */
		if ((params.mode & (PPS_TSFMT_TSPEC | PPS_TSFMT_NTPFP)) == 0) {
			/* section 3.3 of RFC 2783 interpreted */
			dev_dbg(pps->dev, "time format unspecified (%x)\n",
								params.mode);
			pps->params.mode |= PPS_TSFMT_TSPEC;
		}
		if (pps->info.mode & PPS_CANWAIT)
			pps->params.mode |= PPS_CANWAIT;
		pps->params.api_version = PPS_API_VERS;

		/*
		 * Clear unused fields of pps_kparams to avoid leaking
		 * uninitialized data of the PPS_SETPARAMS caller via
		 * PPS_GETPARAMS
		 */
		pps->params.assert_off_tu.flags = 0;
		pps->params.clear_off_tu.flags = 0;

		spin_unlock_irq(&pps->lock);

		break;

	case PPS_GETCAP:
		dev_dbg(pps->dev, "PPS_GETCAP\n");

		err = put_user(pps->info.mode, iuarg);
		if (err)
			return -EFAULT;

		break;

	case PPS_FETCH: {
		struct pps_fdata fdata;

		dev_dbg(pps->dev, "PPS_FETCH\n");

		err = copy_from_user(&fdata, uarg, sizeof(struct pps_fdata));
		if (err)
			return -EFAULT;

		err = pps_cdev_pps_fetch(pps, &fdata);
		if (err)
			return err;

		/* Return the fetched timestamp */
		spin_lock_irq(&pps->lock);

		fdata.info.assert_sequence = pps->assert_sequence;
		fdata.info.clear_sequence = pps->clear_sequence;
		fdata.info.assert_tu = pps->assert_tu;
		fdata.info.clear_tu = pps->clear_tu;
		fdata.info.current_mode = pps->current_mode;

		spin_unlock_irq(&pps->lock);

		err = copy_to_user(uarg, &fdata, sizeof(struct pps_fdata));
		if (err)
			return -EFAULT;

		break;
	}
	case PPS_KC_BIND: {
		struct pps_bind_args bind_args;

		dev_dbg(pps->dev, "PPS_KC_BIND\n");

		/* Check the capabilities */
		if (!capable(CAP_SYS_TIME))
			return -EPERM;

		if (copy_from_user(&bind_args, uarg,
					sizeof(struct pps_bind_args)))
			return -EFAULT;

		/* Check for supported capabilities */
		if ((bind_args.edge & ~pps->info.mode) != 0) {
			dev_err(pps->dev, "unsupported capabilities (%x)\n",
					bind_args.edge);
			return -EINVAL;
		}

		/* Validate parameters roughly */
		if (bind_args.tsformat != PPS_TSFMT_TSPEC ||
				(bind_args.edge & ~PPS_CAPTUREBOTH) != 0 ||
				bind_args.consumer != PPS_KC_HARDPPS) {
			dev_err(pps->dev, "invalid kernel consumer bind"
					" parameters (%x)\n", bind_args.edge);
			return -EINVAL;
		}

		err = pps_kc_bind(pps, &bind_args);
		if (err < 0)
			return err;

		break;
	}
	default:
		return -ENOTTY;
	}

	return 0;
}