2006-03-27 13:16:39 +04:00
/*
* RTC subsystem , sysfs interface
*
* Copyright ( C ) 2005 Tower Technologies
* Author : Alessandro Zummo < a . zummo @ towertech . it >
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation .
*/
# include <linux/module.h>
# include <linux/rtc.h>
2007-05-08 11:33:30 +04:00
# include "rtc-core.h"
2006-03-27 13:16:39 +04:00
/* device attributes */
2008-02-06 12:38:45 +03:00
/*
* NOTE : RTC times displayed in sysfs use the RTC ' s timezone . That ' s
* ideally UTC . However , PCs that also boot to MS - Windows normally use
* the local time and change to match daylight savings time . That affects
* attributes including date , time , since_epoch , and wakealarm .
*/
2007-05-08 11:33:40 +04:00
static ssize_t
2013-07-25 02:05:22 +04:00
name_show ( struct device * dev , struct device_attribute * attr , char * buf )
2006-03-27 13:16:39 +04:00
{
return sprintf ( buf , " %s \n " , to_rtc_device ( dev ) - > name ) ;
}
2013-07-25 02:05:22 +04:00
static DEVICE_ATTR_RO ( name ) ;
2006-03-27 13:16:39 +04:00
2007-05-08 11:33:40 +04:00
static ssize_t
2013-07-25 02:05:22 +04:00
date_show ( struct device * dev , struct device_attribute * attr , char * buf )
2006-03-27 13:16:39 +04:00
{
ssize_t retval ;
struct rtc_time tm ;
2007-05-08 11:33:30 +04:00
retval = rtc_read_time ( to_rtc_device ( dev ) , & tm ) ;
2006-03-27 13:16:39 +04:00
if ( retval = = 0 ) {
retval = sprintf ( buf , " %04d-%02d-%02d \n " ,
tm . tm_year + 1900 , tm . tm_mon + 1 , tm . tm_mday ) ;
}
return retval ;
}
2013-07-25 02:05:22 +04:00
static DEVICE_ATTR_RO ( date ) ;
2006-03-27 13:16:39 +04:00
2007-05-08 11:33:40 +04:00
static ssize_t
2013-07-25 02:05:22 +04:00
time_show ( struct device * dev , struct device_attribute * attr , char * buf )
2006-03-27 13:16:39 +04:00
{
ssize_t retval ;
struct rtc_time tm ;
2007-05-08 11:33:30 +04:00
retval = rtc_read_time ( to_rtc_device ( dev ) , & tm ) ;
2006-03-27 13:16:39 +04:00
if ( retval = = 0 ) {
retval = sprintf ( buf , " %02d:%02d:%02d \n " ,
tm . tm_hour , tm . tm_min , tm . tm_sec ) ;
}
return retval ;
}
2013-07-25 02:05:22 +04:00
static DEVICE_ATTR_RO ( time ) ;
2006-03-27 13:16:39 +04:00
2007-05-08 11:33:40 +04:00
static ssize_t
2013-07-25 02:05:22 +04:00
since_epoch_show ( struct device * dev , struct device_attribute * attr , char * buf )
2006-03-27 13:16:39 +04:00
{
ssize_t retval ;
struct rtc_time tm ;
2007-05-08 11:33:30 +04:00
retval = rtc_read_time ( to_rtc_device ( dev ) , & tm ) ;
2006-03-27 13:16:39 +04:00
if ( retval = = 0 ) {
unsigned long time ;
rtc_tm_to_time ( & tm , & time ) ;
retval = sprintf ( buf , " %lu \n " , time ) ;
}
return retval ;
}
2013-07-25 02:05:22 +04:00
static DEVICE_ATTR_RO ( since_epoch ) ;
2006-03-27 13:16:39 +04:00
2007-10-16 12:28:22 +04:00
static ssize_t
2013-07-25 02:05:22 +04:00
max_user_freq_show ( struct device * dev , struct device_attribute * attr , char * buf )
2007-10-16 12:28:22 +04:00
{
return sprintf ( buf , " %d \n " , to_rtc_device ( dev ) - > max_user_freq ) ;
}
static ssize_t
2013-07-25 02:05:22 +04:00
max_user_freq_store ( struct device * dev , struct device_attribute * attr ,
2007-10-16 12:28:22 +04:00
const char * buf , size_t n )
{
struct rtc_device * rtc = to_rtc_device ( dev ) ;
unsigned long val = simple_strtoul ( buf , NULL , 0 ) ;
if ( val > = 4096 | | val = = 0 )
return - EINVAL ;
rtc - > max_user_freq = ( int ) val ;
return n ;
}
2013-07-25 02:05:22 +04:00
static DEVICE_ATTR_RW ( max_user_freq ) ;
2007-10-16 12:28:22 +04:00
2012-10-05 04:14:12 +04:00
/**
* rtc_sysfs_show_hctosys - indicate if the given RTC set the system time
*
* Returns 1 if the system clock was set by this RTC at the last
* boot or resume event .
*/
2009-09-23 03:46:32 +04:00
static ssize_t
2013-07-25 02:05:22 +04:00
hctosys_show ( struct device * dev , struct device_attribute * attr , char * buf )
2009-09-23 03:46:32 +04:00
{
# ifdef CONFIG_RTC_HCTOSYS_DEVICE
2010-03-11 02:20:35 +03:00
if ( rtc_hctosys_ret = = 0 & &
strcmp ( dev_name ( & to_rtc_device ( dev ) - > dev ) ,
CONFIG_RTC_HCTOSYS_DEVICE ) = = 0 )
2009-09-23 03:46:32 +04:00
return sprintf ( buf , " 1 \n " ) ;
else
# endif
return sprintf ( buf , " 0 \n " ) ;
}
2013-07-25 02:05:22 +04:00
static DEVICE_ATTR_RO ( hctosys ) ;
static struct attribute * rtc_attrs [ ] = {
& dev_attr_name . attr ,
& dev_attr_date . attr ,
& dev_attr_time . attr ,
& dev_attr_since_epoch . attr ,
& dev_attr_max_user_freq . attr ,
& dev_attr_hctosys . attr ,
NULL ,
2006-03-27 13:16:39 +04:00
} ;
2013-07-25 02:05:22 +04:00
ATTRIBUTE_GROUPS ( rtc ) ;
2006-03-27 13:16:39 +04:00
2007-02-12 11:52:47 +03:00
static ssize_t
2007-05-08 11:33:40 +04:00
rtc_sysfs_show_wakealarm ( struct device * dev , struct device_attribute * attr ,
char * buf )
2007-02-12 11:52:47 +03:00
{
ssize_t retval ;
unsigned long alarm ;
struct rtc_wkalrm alm ;
2008-02-06 12:38:45 +03:00
/* Don't show disabled alarms. For uniformity, RTC alarms are
* conceptually one - shot , even though some common RTCs ( on PCs )
* don ' t actually work that way .
2007-02-12 11:52:47 +03:00
*
2008-02-06 12:38:45 +03:00
* NOTE : RTC implementations where the alarm doesn ' t match an
* exact YYYY - MM - DD HH : MM [ : SS ] date * must * disable their RTC
* alarms after they trigger , to ensure one - shot semantics .
2007-02-12 11:52:47 +03:00
*/
2007-05-08 11:33:30 +04:00
retval = rtc_read_alarm ( to_rtc_device ( dev ) , & alm ) ;
2007-02-12 11:52:47 +03:00
if ( retval = = 0 & & alm . enabled ) {
rtc_tm_to_time ( & alm . time , & alarm ) ;
retval = sprintf ( buf , " %lu \n " , alarm ) ;
}
return retval ;
}
static ssize_t
2007-05-08 11:33:40 +04:00
rtc_sysfs_set_wakealarm ( struct device * dev , struct device_attribute * attr ,
const char * buf , size_t n )
2007-02-12 11:52:47 +03:00
{
ssize_t retval ;
unsigned long now , alarm ;
2013-07-04 02:07:03 +04:00
unsigned long push = 0 ;
2007-02-12 11:52:47 +03:00
struct rtc_wkalrm alm ;
2007-05-08 11:33:30 +04:00
struct rtc_device * rtc = to_rtc_device ( dev ) ;
2008-04-28 13:11:58 +04:00
char * buf_ptr ;
int adjust = 0 ;
2007-02-12 11:52:47 +03:00
/* Only request alarms that trigger in the future. Disable them
* by writing another time , e . g . 0 meaning Jan 1 1970 UTC .
*/
2007-05-08 11:33:30 +04:00
retval = rtc_read_time ( rtc , & alm . time ) ;
2007-02-12 11:52:47 +03:00
if ( retval < 0 )
return retval ;
rtc_tm_to_time ( & alm . time , & now ) ;
2008-04-28 13:11:58 +04:00
buf_ptr = ( char * ) buf ;
if ( * buf_ptr = = ' + ' ) {
buf_ptr + + ;
2013-07-04 02:07:03 +04:00
if ( * buf_ptr = = ' = ' ) {
buf_ptr + + ;
push = 1 ;
} else
adjust = 1 ;
2008-04-28 13:11:58 +04:00
}
alarm = simple_strtoul ( buf_ptr , NULL , 0 ) ;
if ( adjust ) {
alarm + = now ;
}
2013-07-04 02:07:03 +04:00
if ( alarm > now | | push ) {
2007-02-12 11:52:47 +03:00
/* Avoid accidentally clobbering active alarms; we can't
* entirely prevent that here , without even the minimal
* locking from the / dev / rtcN api .
*/
2007-05-08 11:33:30 +04:00
retval = rtc_read_alarm ( rtc , & alm ) ;
2007-02-12 11:52:47 +03:00
if ( retval < 0 )
return retval ;
2013-07-04 02:07:03 +04:00
if ( alm . enabled ) {
if ( push ) {
rtc_tm_to_time ( & alm . time , & push ) ;
alarm + = push ;
} else
return - EBUSY ;
} else if ( push )
return - EINVAL ;
2007-02-12 11:52:47 +03:00
alm . enabled = 1 ;
} else {
alm . enabled = 0 ;
/* Provide a valid future alarm time. Linux isn't EFI,
* this time won ' t be ignored when disabling the alarm .
*/
alarm = now + 300 ;
}
rtc_time_to_tm ( alarm , & alm . time ) ;
2007-05-08 11:33:30 +04:00
retval = rtc_set_alarm ( rtc , & alm ) ;
2007-02-12 11:52:47 +03:00
return ( retval < 0 ) ? retval : n ;
}
2007-05-08 11:33:40 +04:00
static DEVICE_ATTR ( wakealarm , S_IRUGO | S_IWUSR ,
2007-02-12 11:52:47 +03:00
rtc_sysfs_show_wakealarm , rtc_sysfs_set_wakealarm ) ;
/* The reason to trigger an alarm with no process watching it (via sysfs)
* is its side effect : waking from a system state like suspend - to - RAM or
* suspend - to - disk . So : no attribute unless that side effect is possible .
* ( Userspace may disable that mechanism later . )
*/
2007-05-08 11:33:33 +04:00
static inline int rtc_does_wakealarm ( struct rtc_device * rtc )
2007-02-12 11:52:47 +03:00
{
2007-05-08 11:33:40 +04:00
if ( ! device_can_wakeup ( rtc - > dev . parent ) )
2007-02-12 11:52:47 +03:00
return 0 ;
return rtc - > ops - > set_alarm ! = NULL ;
}
2007-05-08 11:33:33 +04:00
void rtc_sysfs_add_device ( struct rtc_device * rtc )
2006-03-27 13:16:39 +04:00
{
int err ;
2007-05-08 11:33:33 +04:00
/* not all RTCs support both alarms and wakeup */
if ( ! rtc_does_wakealarm ( rtc ) )
return ;
2006-03-27 13:16:39 +04:00
2007-05-08 11:33:40 +04:00
err = device_create_file ( & rtc - > dev , & dev_attr_wakealarm ) ;
2006-03-27 13:16:39 +04:00
if ( err )
2007-10-18 14:06:30 +04:00
dev_err ( rtc - > dev . parent ,
" failed to create alarm attribute, %d \n " , err ) ;
2006-03-27 13:16:39 +04:00
}
2007-05-08 11:33:33 +04:00
void rtc_sysfs_del_device ( struct rtc_device * rtc )
2006-03-27 13:16:39 +04:00
{
2007-05-08 11:33:33 +04:00
/* REVISIT did we add it successfully? */
if ( rtc_does_wakealarm ( rtc ) )
2007-05-08 11:33:40 +04:00
device_remove_file ( & rtc - > dev , & dev_attr_wakealarm ) ;
2006-03-27 13:16:39 +04:00
}
2007-05-08 11:33:33 +04:00
void __init rtc_sysfs_init ( struct class * rtc_class )
2006-03-27 13:16:39 +04:00
{
2013-07-25 02:05:22 +04:00
rtc_class - > dev_groups = rtc_groups ;
2006-03-27 13:16:39 +04:00
}