148104Syokota// SPDX-License-Identifier: GPL-2.0+
248104Syokota/*
348104Syokota * Support for dynamic clock devices
448104Syokota *
548104Syokota * Copyright (C) 2010 OMICRON electronics GmbH
648104Syokota */
748104Syokota#include <linux/device.h>
848104Syokota#include <linux/export.h>
948104Syokota#include <linux/file.h>
1048104Syokota#include <linux/posix-clock.h>
1148104Syokota#include <linux/slab.h>
1248104Syokota#include <linux/syscalls.h>
1348104Syokota#include <linux/uaccess.h>
1448104Syokota
1548104Syokota#include "posix-timers.h"
1648104Syokota
1748104Syokota/*
1848104Syokota * Returns NULL if the posix_clock instance attached to 'fp' is old and stale.
1948104Syokota */
2048104Syokotastatic struct posix_clock *get_posix_clock(struct file *fp)
2148104Syokota{
2248104Syokota	struct posix_clock_context *pccontext = fp->private_data;
2348104Syokota	struct posix_clock *clk = pccontext->clk;
2448104Syokota
2548104Syokota	down_read(&clk->rwsem);
2648104Syokota
2748104Syokota	if (!clk->zombie)
2850477Speter		return clk;
2948104Syokota
3048104Syokota	up_read(&clk->rwsem);
3148104Syokota
3248104Syokota	return NULL;
3348104Syokota}
3455205Speter
3548104Syokotastatic void put_posix_clock(struct posix_clock *clk)
3648104Syokota{
3748104Syokota	up_read(&clk->rwsem);
3848104Syokota}
3948104Syokota
4048104Syokotastatic ssize_t posix_clock_read(struct file *fp, char __user *buf,
4148104Syokota				size_t count, loff_t *ppos)
4248104Syokota{
4348104Syokota	struct posix_clock_context *pccontext = fp->private_data;
4448104Syokota	struct posix_clock *clk = get_posix_clock(fp);
4548104Syokota	int err = -EINVAL;
4648104Syokota
4748104Syokota	if (!clk)
4848104Syokota		return -ENODEV;
4948104Syokota
5048104Syokota	if (clk->ops.read)
5148104Syokota		err = clk->ops.read(pccontext, fp->f_flags, buf, count);
5248104Syokota
5348104Syokota	put_posix_clock(clk);
5448104Syokota
5548104Syokota	return err;
5648104Syokota}
5748104Syokota
5848104Syokotastatic __poll_t posix_clock_poll(struct file *fp, poll_table *wait)
5948104Syokota{
6096724Ssobomax	struct posix_clock_context *pccontext = fp->private_data;
6148104Syokota	struct posix_clock *clk = get_posix_clock(fp);
6248104Syokota	__poll_t result = 0;
6396724Ssobomax
6448104Syokota	if (!clk)
6548104Syokota		return EPOLLERR;
6648104Syokota
6748104Syokota	if (clk->ops.poll)
6896724Ssobomax		result = clk->ops.poll(pccontext, fp, wait);
6948104Syokota
7048104Syokota	put_posix_clock(clk);
7196724Ssobomax
7248104Syokota	return result;
7348104Syokota}
7448104Syokota
7548104Syokotastatic long posix_clock_ioctl(struct file *fp,
7648104Syokota			      unsigned int cmd, unsigned long arg)
7748104Syokota{
7848104Syokota	struct posix_clock_context *pccontext = fp->private_data;
7948104Syokota	struct posix_clock *clk = get_posix_clock(fp);
8048104Syokota	int err = -ENOTTY;
8148104Syokota
8248104Syokota	if (!clk)
8348104Syokota		return -ENODEV;
8448104Syokota
8548104Syokota	if (clk->ops.ioctl)
8648104Syokota		err = clk->ops.ioctl(pccontext, cmd, arg);
8748104Syokota
8848104Syokota	put_posix_clock(clk);
8948104Syokota
9048104Syokota	return err;
9148104Syokota}
9248104Syokota
9348104Syokota#ifdef CONFIG_COMPAT
9448104Syokotastatic long posix_clock_compat_ioctl(struct file *fp,
9548104Syokota				     unsigned int cmd, unsigned long arg)
9648104Syokota{
9748104Syokota	struct posix_clock_context *pccontext = fp->private_data;
9848104Syokota	struct posix_clock *clk = get_posix_clock(fp);
9948104Syokota	int err = -ENOTTY;
10048104Syokota
10148104Syokota	if (!clk)
10248104Syokota		return -ENODEV;
10348104Syokota
10448104Syokota	if (clk->ops.ioctl)
10548104Syokota		err = clk->ops.ioctl(pccontext, cmd, arg);
10681030Syokota
10781030Syokota	put_posix_clock(clk);
10848104Syokota
10948104Syokota	return err;
11081030Syokota}
11148104Syokota#endif
11248104Syokota
11348104Syokotastatic int posix_clock_open(struct inode *inode, struct file *fp)
11448104Syokota{
11548104Syokota	int err;
11648104Syokota	struct posix_clock *clk =
11748104Syokota		container_of(inode->i_cdev, struct posix_clock, cdev);
11848104Syokota	struct posix_clock_context *pccontext;
11948104Syokota
12048104Syokota	down_read(&clk->rwsem);
12177251Sdd
12277251Sdd	if (clk->zombie) {
12377251Sdd		err = -ENODEV;
12448104Syokota		goto out;
12548104Syokota	}
12648104Syokota	pccontext = kzalloc(sizeof(*pccontext), GFP_KERNEL);
12748104Syokota	if (!pccontext) {
12848104Syokota		err = -ENOMEM;
12948104Syokota		goto out;
13048104Syokota	}
13148104Syokota	pccontext->clk = clk;
13248104Syokota	if (clk->ops.open) {
13348104Syokota		err = clk->ops.open(pccontext, fp->f_mode);
13448104Syokota		if (err) {
13548104Syokota			kfree(pccontext);
13648104Syokota			goto out;
13748104Syokota		}
13848104Syokota	}
13948104Syokota
14048104Syokota	fp->private_data = pccontext;
14148104Syokota	get_device(clk->dev);
14248104Syokota	err = 0;
14348104Syokotaout:
14448104Syokota	up_read(&clk->rwsem);
14548104Syokota	return err;
14648104Syokota}
14748104Syokota
14848104Syokotastatic int posix_clock_release(struct inode *inode, struct file *fp)
14948104Syokota{
15048104Syokota	struct posix_clock_context *pccontext = fp->private_data;
15148104Syokota	struct posix_clock *clk;
15248104Syokota	int err = 0;
15348104Syokota
15448104Syokota	if (!pccontext)
15548104Syokota		return -ENODEV;
15655849Syokota	clk = pccontext->clk;
15748104Syokota
15848104Syokota	if (clk->ops.release)
15948104Syokota		err = clk->ops.release(pccontext);
16048104Syokota
16155849Syokota	put_device(clk->dev);
16248104Syokota
16348104Syokota	kfree(pccontext);
16448104Syokota	fp->private_data = NULL;
16548104Syokota
16648104Syokota	return err;
16748104Syokota}
16848104Syokota
16948104Syokotastatic const struct file_operations posix_clock_file_operations = {
17048104Syokota	.owner		= THIS_MODULE,
17148104Syokota	.llseek		= no_llseek,
17279622Syokota	.read		= posix_clock_read,
17348104Syokota	.poll		= posix_clock_poll,
17448104Syokota	.unlocked_ioctl	= posix_clock_ioctl,
17548104Syokota	.open		= posix_clock_open,
17648104Syokota	.release	= posix_clock_release,
17748104Syokota#ifdef CONFIG_COMPAT
17848104Syokota	.compat_ioctl	= posix_clock_compat_ioctl,
17948104Syokota#endif
18081030Syokota};
18181030Syokota
18281030Syokotaint posix_clock_register(struct posix_clock *clk, struct device *dev)
18381030Syokota{
18481030Syokota	int err;
18581030Syokota
18681030Syokota	init_rwsem(&clk->rwsem);
18781030Syokota
18881030Syokota	cdev_init(&clk->cdev, &posix_clock_file_operations);
18981030Syokota	err = cdev_device_add(&clk->cdev, dev);
19081030Syokota	if (err) {
19181030Syokota		pr_err("%s unable to add device %d:%d\n",
19281030Syokota			dev_name(dev), MAJOR(dev->devt), MINOR(dev->devt));
19381030Syokota		return err;
19481030Syokota	}
19581030Syokota	clk->cdev.owner = clk->ops.owner;
19648104Syokota	clk->dev = dev;
19748104Syokota
19848104Syokota	return 0;
19948104Syokota}
20048104SyokotaEXPORT_SYMBOL_GPL(posix_clock_register);
20148104Syokota
20248104Syokotavoid posix_clock_unregister(struct posix_clock *clk)
20348104Syokota{
20448104Syokota	cdev_device_del(&clk->cdev, clk->dev);
20548104Syokota
20648104Syokota	down_write(&clk->rwsem);
20748104Syokota	clk->zombie = true;
20848104Syokota	up_write(&clk->rwsem);
20948104Syokota
21048104Syokota	put_device(clk->dev);
21148104Syokota}
21248104SyokotaEXPORT_SYMBOL_GPL(posix_clock_unregister);
21348104Syokota
21448104Syokotastruct posix_clock_desc {
21548104Syokota	struct file *fp;
21648104Syokota	struct posix_clock *clk;
21748104Syokota};
21848104Syokota
21948104Syokotastatic int get_clock_desc(const clockid_t id, struct posix_clock_desc *cd)
22048104Syokota{
22148104Syokota	struct file *fp = fget(clockid_to_fd(id));
22248104Syokota	int err = -EINVAL;
22348104Syokota
22448104Syokota	if (!fp)
22548104Syokota		return err;
22648104Syokota
22748104Syokota	if (fp->f_op->open != posix_clock_open || !fp->private_data)
22892459Ssobomax		goto out;
22948104Syokota
23048104Syokota	cd->fp = fp;
23148104Syokota	cd->clk = get_posix_clock(fp);
23248104Syokota
23348104Syokota	err = cd->clk ? 0 : -ENODEV;
23448104Syokotaout:
23548104Syokota	if (err)
23648104Syokota		fput(fp);
23748104Syokota	return err;
23848104Syokota}
23948104Syokota
24048104Syokotastatic void put_clock_desc(struct posix_clock_desc *cd)
24148104Syokota{
24248104Syokota	put_posix_clock(cd->clk);
24348104Syokota	fput(cd->fp);
24448104Syokota}
24548104Syokota
24648104Syokotastatic int pc_clock_adjtime(clockid_t id, struct __kernel_timex *tx)
24748104Syokota{
24848104Syokota	struct posix_clock_desc cd;
24948104Syokota	int err;
25048104Syokota
25148104Syokota	err = get_clock_desc(id, &cd);
25248104Syokota	if (err)
25348104Syokota		return err;
25448104Syokota
25548104Syokota	if ((cd.fp->f_mode & FMODE_WRITE) == 0) {
25648104Syokota		err = -EACCES;
25748104Syokota		goto out;
25848104Syokota	}
25948104Syokota
26048104Syokota	if (cd.clk->ops.clock_adjtime)
26148104Syokota		err = cd.clk->ops.clock_adjtime(cd.clk, tx);
26248104Syokota	else
26348104Syokota		err = -EOPNOTSUPP;
26448104Syokotaout:
26576798Snik	put_clock_desc(&cd);
26676798Snik
26776798Snik	return err;
26876798Snik}
26976798Snik
27076798Snikstatic int pc_clock_gettime(clockid_t id, struct timespec64 *ts)
27176798Snik{
27280387Ssobomax	struct posix_clock_desc cd;
27380387Ssobomax	int err;
27480387Ssobomax
27556043Syokota	err = get_clock_desc(id, &cd);
27656043Syokota	if (err)
27756043Syokota		return err;
27856043Syokota
27956043Syokota	if (cd.clk->ops.clock_gettime)
28056043Syokota		err = cd.clk->ops.clock_gettime(cd.clk, ts);
28156043Syokota	else
28256043Syokota		err = -EOPNOTSUPP;
28356043Syokota
28456043Syokota	put_clock_desc(&cd);
28556043Syokota
28656043Syokota	return err;
28756043Syokota}
28856043Syokota
28956043Syokotastatic int pc_clock_getres(clockid_t id, struct timespec64 *ts)
29048104Syokota{
29148104Syokota	struct posix_clock_desc cd;
29248104Syokota	int err;
29348104Syokota
29448104Syokota	err = get_clock_desc(id, &cd);
29548104Syokota	if (err)
29648104Syokota		return err;
29748104Syokota
29848104Syokota	if (cd.clk->ops.clock_getres)
29948104Syokota		err = cd.clk->ops.clock_getres(cd.clk, ts);
30048104Syokota	else
30148104Syokota		err = -EOPNOTSUPP;
30248104Syokota
30348104Syokota	put_clock_desc(&cd);
30448104Syokota
30548104Syokota	return err;
30648104Syokota}
30748104Syokota
30848104Syokotastatic int pc_clock_settime(clockid_t id, const struct timespec64 *ts)
30948104Syokota{
31048104Syokota	struct posix_clock_desc cd;
31148104Syokota	int err;
31248104Syokota
31348104Syokota	err = get_clock_desc(id, &cd);
31448104Syokota	if (err)
31548104Syokota		return err;
31648104Syokota
31748104Syokota	if ((cd.fp->f_mode & FMODE_WRITE) == 0) {
31848104Syokota		err = -EACCES;
31948104Syokota		goto out;
32048104Syokota	}
32148104Syokota
32248104Syokota	if (cd.clk->ops.clock_settime)
32348104Syokota		err = cd.clk->ops.clock_settime(cd.clk, ts);
32448104Syokota	else
32548104Syokota		err = -EOPNOTSUPP;
32648104Syokotaout:
32748104Syokota	put_clock_desc(&cd);
32848104Syokota
32948104Syokota	return err;
33048104Syokota}
33148104Syokota
33248104Syokotaconst struct k_clock clock_posix_dynamic = {
33348104Syokota	.clock_getres		= pc_clock_getres,
33448104Syokota	.clock_set		= pc_clock_settime,
33548104Syokota	.clock_get_timespec	= pc_clock_gettime,
33648104Syokota	.clock_adj		= pc_clock_adjtime,
33748104Syokota};
33848104Syokota