at v2.6.24-rc7 217 lines 5.4 kB view raw
1/* 2 * RTC subsystem, sysfs interface 3 * 4 * Copyright (C) 2005 Tower Technologies 5 * Author: Alessandro Zummo <a.zummo@towertech.it> 6 * 7 * This program is free software; you can redistribute it and/or modify 8 * it under the terms of the GNU General Public License version 2 as 9 * published by the Free Software Foundation. 10*/ 11 12#include <linux/module.h> 13#include <linux/rtc.h> 14 15#include "rtc-core.h" 16 17 18/* device attributes */ 19 20static ssize_t 21rtc_sysfs_show_name(struct device *dev, struct device_attribute *attr, 22 char *buf) 23{ 24 return sprintf(buf, "%s\n", to_rtc_device(dev)->name); 25} 26 27static ssize_t 28rtc_sysfs_show_date(struct device *dev, struct device_attribute *attr, 29 char *buf) 30{ 31 ssize_t retval; 32 struct rtc_time tm; 33 34 retval = rtc_read_time(to_rtc_device(dev), &tm); 35 if (retval == 0) { 36 retval = sprintf(buf, "%04d-%02d-%02d\n", 37 tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday); 38 } 39 40 return retval; 41} 42 43static ssize_t 44rtc_sysfs_show_time(struct device *dev, struct device_attribute *attr, 45 char *buf) 46{ 47 ssize_t retval; 48 struct rtc_time tm; 49 50 retval = rtc_read_time(to_rtc_device(dev), &tm); 51 if (retval == 0) { 52 retval = sprintf(buf, "%02d:%02d:%02d\n", 53 tm.tm_hour, tm.tm_min, tm.tm_sec); 54 } 55 56 return retval; 57} 58 59static ssize_t 60rtc_sysfs_show_since_epoch(struct device *dev, struct device_attribute *attr, 61 char *buf) 62{ 63 ssize_t retval; 64 struct rtc_time tm; 65 66 retval = rtc_read_time(to_rtc_device(dev), &tm); 67 if (retval == 0) { 68 unsigned long time; 69 rtc_tm_to_time(&tm, &time); 70 retval = sprintf(buf, "%lu\n", time); 71 } 72 73 return retval; 74} 75 76static ssize_t 77rtc_sysfs_show_max_user_freq(struct device *dev, struct device_attribute *attr, 78 char *buf) 79{ 80 return sprintf(buf, "%d\n", to_rtc_device(dev)->max_user_freq); 81} 82 83static ssize_t 84rtc_sysfs_set_max_user_freq(struct device *dev, struct device_attribute *attr, 85 const char *buf, size_t n) 86{ 87 struct rtc_device *rtc = to_rtc_device(dev); 88 unsigned long val = simple_strtoul(buf, NULL, 0); 89 90 if (val >= 4096 || val == 0) 91 return -EINVAL; 92 93 rtc->max_user_freq = (int)val; 94 95 return n; 96} 97 98static struct device_attribute rtc_attrs[] = { 99 __ATTR(name, S_IRUGO, rtc_sysfs_show_name, NULL), 100 __ATTR(date, S_IRUGO, rtc_sysfs_show_date, NULL), 101 __ATTR(time, S_IRUGO, rtc_sysfs_show_time, NULL), 102 __ATTR(since_epoch, S_IRUGO, rtc_sysfs_show_since_epoch, NULL), 103 __ATTR(max_user_freq, S_IRUGO | S_IWUSR, rtc_sysfs_show_max_user_freq, 104 rtc_sysfs_set_max_user_freq), 105 { }, 106}; 107 108static ssize_t 109rtc_sysfs_show_wakealarm(struct device *dev, struct device_attribute *attr, 110 char *buf) 111{ 112 ssize_t retval; 113 unsigned long alarm; 114 struct rtc_wkalrm alm; 115 116 /* Don't show disabled alarms; but the RTC could leave the 117 * alarm enabled after it's already triggered. Alarms are 118 * conceptually one-shot, even though some common hardware 119 * (PCs) doesn't actually work that way. 120 * 121 * REVISIT maybe we should require RTC implementations to 122 * disable the RTC alarm after it triggers, for uniformity. 123 */ 124 retval = rtc_read_alarm(to_rtc_device(dev), &alm); 125 if (retval == 0 && alm.enabled) { 126 rtc_tm_to_time(&alm.time, &alarm); 127 retval = sprintf(buf, "%lu\n", alarm); 128 } 129 130 return retval; 131} 132 133static ssize_t 134rtc_sysfs_set_wakealarm(struct device *dev, struct device_attribute *attr, 135 const char *buf, size_t n) 136{ 137 ssize_t retval; 138 unsigned long now, alarm; 139 struct rtc_wkalrm alm; 140 struct rtc_device *rtc = to_rtc_device(dev); 141 142 /* Only request alarms that trigger in the future. Disable them 143 * by writing another time, e.g. 0 meaning Jan 1 1970 UTC. 144 */ 145 retval = rtc_read_time(rtc, &alm.time); 146 if (retval < 0) 147 return retval; 148 rtc_tm_to_time(&alm.time, &now); 149 150 alarm = simple_strtoul(buf, NULL, 0); 151 if (alarm > now) { 152 /* Avoid accidentally clobbering active alarms; we can't 153 * entirely prevent that here, without even the minimal 154 * locking from the /dev/rtcN api. 155 */ 156 retval = rtc_read_alarm(rtc, &alm); 157 if (retval < 0) 158 return retval; 159 if (alm.enabled) 160 return -EBUSY; 161 162 alm.enabled = 1; 163 } else { 164 alm.enabled = 0; 165 166 /* Provide a valid future alarm time. Linux isn't EFI, 167 * this time won't be ignored when disabling the alarm. 168 */ 169 alarm = now + 300; 170 } 171 rtc_time_to_tm(alarm, &alm.time); 172 173 retval = rtc_set_alarm(rtc, &alm); 174 return (retval < 0) ? retval : n; 175} 176static DEVICE_ATTR(wakealarm, S_IRUGO | S_IWUSR, 177 rtc_sysfs_show_wakealarm, rtc_sysfs_set_wakealarm); 178 179 180/* The reason to trigger an alarm with no process watching it (via sysfs) 181 * is its side effect: waking from a system state like suspend-to-RAM or 182 * suspend-to-disk. So: no attribute unless that side effect is possible. 183 * (Userspace may disable that mechanism later.) 184 */ 185static inline int rtc_does_wakealarm(struct rtc_device *rtc) 186{ 187 if (!device_can_wakeup(rtc->dev.parent)) 188 return 0; 189 return rtc->ops->set_alarm != NULL; 190} 191 192 193void rtc_sysfs_add_device(struct rtc_device *rtc) 194{ 195 int err; 196 197 /* not all RTCs support both alarms and wakeup */ 198 if (!rtc_does_wakealarm(rtc)) 199 return; 200 201 err = device_create_file(&rtc->dev, &dev_attr_wakealarm); 202 if (err) 203 dev_err(rtc->dev.parent, 204 "failed to create alarm attribute, %d\n", err); 205} 206 207void rtc_sysfs_del_device(struct rtc_device *rtc) 208{ 209 /* REVISIT did we add it successfully? */ 210 if (rtc_does_wakealarm(rtc)) 211 device_remove_file(&rtc->dev, &dev_attr_wakealarm); 212} 213 214void __init rtc_sysfs_init(struct class *rtc_class) 215{ 216 rtc_class->dev_attrs = rtc_attrs; 217}