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