2018-10-31 21:21:09 +03:00
// SPDX-License-Identifier: GPL-2.0+
2011-02-01 16:52:35 +03:00
/*
2018-10-31 21:21:08 +03:00
* Support for dynamic clock devices
2011-02-01 16:52:35 +03:00
*
* Copyright ( C ) 2010 OMICRON electronics GmbH
*/
# include <linux/device.h>
2011-05-27 00:00:52 +04:00
# include <linux/export.h>
2011-02-01 16:52:35 +03:00
# include <linux/file.h>
# include <linux/posix-clock.h>
# include <linux/slab.h>
# include <linux/syscalls.h>
# include <linux/uaccess.h>
2017-05-31 00:15:41 +03:00
# include "posix-timers.h"
2011-02-01 16:52:35 +03:00
static void delete_clock ( struct kref * kref ) ;
/*
* Returns NULL if the posix_clock instance attached to ' fp ' is old and stale .
*/
static struct posix_clock * get_posix_clock ( struct file * fp )
{
struct posix_clock * clk = fp - > private_data ;
2011-03-30 17:24:21 +04:00
down_read ( & clk - > rwsem ) ;
2011-02-01 16:52:35 +03:00
if ( ! clk - > zombie )
return clk ;
2011-03-30 17:24:21 +04:00
up_read ( & clk - > rwsem ) ;
2011-02-01 16:52:35 +03:00
return NULL ;
}
static void put_posix_clock ( struct posix_clock * clk )
{
2011-03-30 17:24:21 +04:00
up_read ( & clk - > rwsem ) ;
2011-02-01 16:52:35 +03:00
}
static ssize_t posix_clock_read ( struct file * fp , char __user * buf ,
size_t count , loff_t * ppos )
{
struct posix_clock * clk = get_posix_clock ( fp ) ;
int err = - EINVAL ;
if ( ! clk )
return - ENODEV ;
if ( clk - > ops . read )
err = clk - > ops . read ( clk , fp - > f_flags , buf , count ) ;
put_posix_clock ( clk ) ;
return err ;
}
2017-07-03 07:42:43 +03:00
static __poll_t posix_clock_poll ( struct file * fp , poll_table * wait )
2011-02-01 16:52:35 +03:00
{
struct posix_clock * clk = get_posix_clock ( fp ) ;
2017-07-03 07:42:43 +03:00
__poll_t result = 0 ;
2011-02-01 16:52:35 +03:00
if ( ! clk )
2018-02-12 01:34:03 +03:00
return EPOLLERR ;
2011-02-01 16:52:35 +03:00
if ( clk - > ops . poll )
result = clk - > ops . poll ( clk , fp , wait ) ;
put_posix_clock ( clk ) ;
return result ;
}
static long posix_clock_ioctl ( struct file * fp ,
unsigned int cmd , unsigned long arg )
{
struct posix_clock * clk = get_posix_clock ( fp ) ;
int err = - ENOTTY ;
if ( ! clk )
return - ENODEV ;
if ( clk - > ops . ioctl )
err = clk - > ops . ioctl ( clk , cmd , arg ) ;
put_posix_clock ( clk ) ;
return err ;
}
# ifdef CONFIG_COMPAT
static long posix_clock_compat_ioctl ( struct file * fp ,
unsigned int cmd , unsigned long arg )
{
struct posix_clock * clk = get_posix_clock ( fp ) ;
int err = - ENOTTY ;
if ( ! clk )
return - ENODEV ;
if ( clk - > ops . ioctl )
err = clk - > ops . ioctl ( clk , cmd , arg ) ;
put_posix_clock ( clk ) ;
return err ;
}
# endif
static int posix_clock_open ( struct inode * inode , struct file * fp )
{
int err ;
struct posix_clock * clk =
container_of ( inode - > i_cdev , struct posix_clock , cdev ) ;
2011-03-30 17:24:21 +04:00
down_read ( & clk - > rwsem ) ;
2011-02-01 16:52:35 +03:00
if ( clk - > zombie ) {
err = - ENODEV ;
goto out ;
}
if ( clk - > ops . open )
err = clk - > ops . open ( clk , fp - > f_mode ) ;
else
err = 0 ;
if ( ! err ) {
kref_get ( & clk - > kref ) ;
fp - > private_data = clk ;
}
out :
2011-03-30 17:24:21 +04:00
up_read ( & clk - > rwsem ) ;
2011-02-01 16:52:35 +03:00
return err ;
}
static int posix_clock_release ( struct inode * inode , struct file * fp )
{
struct posix_clock * clk = fp - > private_data ;
int err = 0 ;
if ( clk - > ops . release )
err = clk - > ops . release ( clk ) ;
kref_put ( & clk - > kref , delete_clock ) ;
fp - > private_data = NULL ;
return err ;
}
static const struct file_operations posix_clock_file_operations = {
. owner = THIS_MODULE ,
. llseek = no_llseek ,
. read = posix_clock_read ,
. poll = posix_clock_poll ,
. unlocked_ioctl = posix_clock_ioctl ,
. open = posix_clock_open ,
. release = posix_clock_release ,
# ifdef CONFIG_COMPAT
. compat_ioctl = posix_clock_compat_ioctl ,
# endif
} ;
int posix_clock_register ( struct posix_clock * clk , dev_t devid )
{
int err ;
kref_init ( & clk - > kref ) ;
2011-03-30 17:24:21 +04:00
init_rwsem ( & clk - > rwsem ) ;
2011-02-01 16:52:35 +03:00
cdev_init ( & clk - > cdev , & posix_clock_file_operations ) ;
clk - > cdev . owner = clk - > ops . owner ;
err = cdev_add ( & clk - > cdev , devid , 1 ) ;
return err ;
}
EXPORT_SYMBOL_GPL ( posix_clock_register ) ;
static void delete_clock ( struct kref * kref )
{
struct posix_clock * clk = container_of ( kref , struct posix_clock , kref ) ;
2011-03-30 17:24:21 +04:00
2011-02-01 16:52:35 +03:00
if ( clk - > release )
clk - > release ( clk ) ;
}
void posix_clock_unregister ( struct posix_clock * clk )
{
cdev_del ( & clk - > cdev ) ;
2011-03-30 17:24:21 +04:00
down_write ( & clk - > rwsem ) ;
2011-02-01 16:52:35 +03:00
clk - > zombie = true ;
2011-03-30 17:24:21 +04:00
up_write ( & clk - > rwsem ) ;
2011-02-01 16:52:35 +03:00
kref_put ( & clk - > kref , delete_clock ) ;
}
EXPORT_SYMBOL_GPL ( posix_clock_unregister ) ;
struct posix_clock_desc {
struct file * fp ;
struct posix_clock * clk ;
} ;
static int get_clock_desc ( const clockid_t id , struct posix_clock_desc * cd )
{
2017-12-29 06:11:36 +03:00
struct file * fp = fget ( clockid_to_fd ( id ) ) ;
2011-02-01 16:52:35 +03:00
int err = - EINVAL ;
if ( ! fp )
return err ;
if ( fp - > f_op - > open ! = posix_clock_open | | ! fp - > private_data )
goto out ;
cd - > fp = fp ;
cd - > clk = get_posix_clock ( fp ) ;
err = cd - > clk ? 0 : - ENODEV ;
out :
if ( err )
fput ( fp ) ;
return err ;
}
static void put_clock_desc ( struct posix_clock_desc * cd )
{
put_posix_clock ( cd - > clk ) ;
fput ( cd - > fp ) ;
}
2018-07-03 08:44:21 +03:00
static int pc_clock_adjtime ( clockid_t id , struct __kernel_timex * tx )
2011-02-01 16:52:35 +03:00
{
struct posix_clock_desc cd ;
int err ;
err = get_clock_desc ( id , & cd ) ;
if ( err )
return err ;
2011-03-03 20:26:14 +03:00
if ( ( cd . fp - > f_mode & FMODE_WRITE ) = = 0 ) {
err = - EACCES ;
goto out ;
}
2011-02-01 16:52:35 +03:00
if ( cd . clk - > ops . clock_adjtime )
err = cd . clk - > ops . clock_adjtime ( cd . clk , tx ) ;
else
err = - EOPNOTSUPP ;
2011-03-03 20:26:14 +03:00
out :
2011-02-01 16:52:35 +03:00
put_clock_desc ( & cd ) ;
return err ;
}
2017-03-26 22:04:14 +03:00
static int pc_clock_gettime ( clockid_t id , struct timespec64 * ts )
2011-02-01 16:52:35 +03:00
{
struct posix_clock_desc cd ;
int err ;
err = get_clock_desc ( id , & cd ) ;
if ( err )
return err ;
2017-03-26 22:04:14 +03:00
if ( cd . clk - > ops . clock_gettime )
err = cd . clk - > ops . clock_gettime ( cd . clk , ts ) ;
2011-02-01 16:52:35 +03:00
else
err = - EOPNOTSUPP ;
put_clock_desc ( & cd ) ;
return err ;
}
2017-03-26 22:04:15 +03:00
static int pc_clock_getres ( clockid_t id , struct timespec64 * ts )
2011-02-01 16:52:35 +03:00
{
struct posix_clock_desc cd ;
int err ;
err = get_clock_desc ( id , & cd ) ;
if ( err )
return err ;
2017-03-26 22:04:15 +03:00
if ( cd . clk - > ops . clock_getres )
err = cd . clk - > ops . clock_getres ( cd . clk , ts ) ;
2011-02-01 16:52:35 +03:00
else
err = - EOPNOTSUPP ;
put_clock_desc ( & cd ) ;
return err ;
}
2017-03-26 22:04:16 +03:00
static int pc_clock_settime ( clockid_t id , const struct timespec64 * ts )
2011-02-01 16:52:35 +03:00
{
struct posix_clock_desc cd ;
int err ;
err = get_clock_desc ( id , & cd ) ;
if ( err )
return err ;
2011-03-03 20:26:14 +03:00
if ( ( cd . fp - > f_mode & FMODE_WRITE ) = = 0 ) {
err = - EACCES ;
goto out ;
}
2011-02-01 16:52:35 +03:00
if ( cd . clk - > ops . clock_settime )
2017-03-26 22:04:16 +03:00
err = cd . clk - > ops . clock_settime ( cd . clk , ts ) ;
2011-02-01 16:52:35 +03:00
else
err = - EOPNOTSUPP ;
2011-03-03 20:26:14 +03:00
out :
2011-02-01 16:52:35 +03:00
put_clock_desc ( & cd ) ;
return err ;
}
2017-05-26 12:03:11 +03:00
const struct k_clock clock_posix_dynamic = {
2011-02-01 16:52:35 +03:00
. clock_getres = pc_clock_getres ,
. clock_set = pc_clock_settime ,
. clock_get = pc_clock_gettime ,
. clock_adj = pc_clock_adjtime ,
} ;