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 */
2007-05-08 11:33:40 +04:00
static ssize_t
rtc_sysfs_show_name ( 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 ) ;
}
2007-05-08 11:33:40 +04:00
static ssize_t
rtc_sysfs_show_date ( 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 ;
}
2007-05-08 11:33:40 +04:00
static ssize_t
rtc_sysfs_show_time ( 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 ;
}
2007-05-08 11:33:40 +04:00
static ssize_t
rtc_sysfs_show_since_epoch ( 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 ;
}
2007-05-08 11:33:40 +04:00
static struct device_attribute rtc_attrs [ ] = {
2007-05-08 11:33:33 +04:00
__ATTR ( name , S_IRUGO , rtc_sysfs_show_name , NULL ) ,
__ATTR ( date , S_IRUGO , rtc_sysfs_show_date , NULL ) ,
__ATTR ( time , S_IRUGO , rtc_sysfs_show_time , NULL ) ,
__ATTR ( since_epoch , S_IRUGO , rtc_sysfs_show_since_epoch , NULL ) ,
{ } ,
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 ;
/* Don't show disabled alarms; but the RTC could leave the
* alarm enabled after it ' s already triggered . Alarms are
* conceptually one - shot , even though some common hardware
* ( PCs ) doesn ' t actually work that way .
*
* REVISIT maybe we should require RTC implementations to
* disable the RTC alarm after it triggers , for uniformity .
*/
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 ;
struct rtc_wkalrm alm ;
2007-05-08 11:33:30 +04:00
struct rtc_device * rtc = to_rtc_device ( dev ) ;
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 ) ;
alarm = simple_strtoul ( buf , NULL , 0 ) ;
if ( alarm > now ) {
/* 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 ;
if ( alm . enabled )
return - EBUSY ;
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-05-08 11:33:40 +04:00
dev_err ( rtc - > dev . parent , " failed to create "
2007-05-08 11:33:33 +04:00
" alarm attribute, %d " ,
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
{
2007-05-08 11:33:40 +04:00
rtc_class - > dev_attrs = rtc_attrs ;
2006-03-27 13:16:39 +04:00
}