aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/rtc/rtc-sysfs.c
diff options
context:
space:
mode:
authorDavid Brownell <david-b@pacbell.net>2007-02-12 03:52:47 -0500
committerLinus Torvalds <torvalds@woody.linux-foundation.org>2007-02-12 12:48:31 -0500
commit3925a5ce44330767f7f0de5c58c6a797009f0f75 (patch)
tree67ccd2220047161031596c2e6a4f8ff3171745f7 /drivers/rtc/rtc-sysfs.c
parent802245611adea5e5877d8c5d9a20f94d8131bfdd (diff)
[PATCH] RTC gets sysfs wakealarm attribute
This adds a new "wakealarm" sysfs attribute to RTC class devices which support alarm operations and are wakeup-capable: - It reads as either empty, or the scheduled alarm time as seconds since the POSIX epoch. (That time may already have passed, since nothing currently enforces one-shot alarm semantics.) - It can be written with an alarm time in the future, again seconds since the POSIX epoch, which enables the alarm. - It can be written with an alarm time not in the future (such as 0, the start of the POSIX epoch) to disable the alarm. Usage examples (some need GNU date) after "cd /sys/class/rtc/rtcN": alarm after 10 minutes: # echo $(( $(cat since_epoch) + 10 * 60 )) > wakealarm alarm tuesday evening 10pm: # date -d '10pm tuesday' "+%s" > wakealarm disable alarm: # echo 0 > wakealarm This resembles the /proc/acpi/alarm file in that nothing happens when the alarm triggers ... except possibly waking the system from sleep. It's also like that in a nasty way: not much can be done to prevent one task from clobbering another task's alarm settings. It differs from that file in that there's no in-kernel date parser. Note that a few RTCs ignore rtc_wkalrm.enabled when setting alarms, or aren't set up correctly, so they won't yet behave with this attribute. Signed-off-by: David Brownell <dbrownell@users.sourceforge.net> Acked-by: Pavel Machek <pavel@ucw.cz> Cc: Alessandro Zummo <a.zummo@towertech.it> Cc: Greg KH <greg@kroah.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'drivers/rtc/rtc-sysfs.c')
-rw-r--r--drivers/rtc/rtc-sysfs.c103
1 files changed, 101 insertions, 2 deletions
diff --git a/drivers/rtc/rtc-sysfs.c b/drivers/rtc/rtc-sysfs.c
index 2ddd0cf07140..899ab8c514fa 100644
--- a/drivers/rtc/rtc-sysfs.c
+++ b/drivers/rtc/rtc-sysfs.c
@@ -78,6 +78,92 @@ static struct attribute_group rtc_attr_group = {
78 .attrs = rtc_attrs, 78 .attrs = rtc_attrs,
79}; 79};
80 80
81
82static ssize_t
83rtc_sysfs_show_wakealarm(struct class_device *dev, char *buf)
84{
85 ssize_t retval;
86 unsigned long alarm;
87 struct rtc_wkalrm alm;
88
89 /* Don't show disabled alarms; but the RTC could leave the
90 * alarm enabled after it's already triggered. Alarms are
91 * conceptually one-shot, even though some common hardware
92 * (PCs) doesn't actually work that way.
93 *
94 * REVISIT maybe we should require RTC implementations to
95 * disable the RTC alarm after it triggers, for uniformity.
96 */
97 retval = rtc_read_alarm(dev, &alm);
98 if (retval == 0 && alm.enabled) {
99 rtc_tm_to_time(&alm.time, &alarm);
100 retval = sprintf(buf, "%lu\n", alarm);
101 }
102
103 return retval;
104}
105
106static ssize_t
107rtc_sysfs_set_wakealarm(struct class_device *dev, const char *buf, size_t n)
108{
109 ssize_t retval;
110 unsigned long now, alarm;
111 struct rtc_wkalrm alm;
112
113 /* Only request alarms that trigger in the future. Disable them
114 * by writing another time, e.g. 0 meaning Jan 1 1970 UTC.
115 */
116 retval = rtc_read_time(dev, &alm.time);
117 if (retval < 0)
118 return retval;
119 rtc_tm_to_time(&alm.time, &now);
120
121 alarm = simple_strtoul(buf, NULL, 0);
122 if (alarm > now) {
123 /* Avoid accidentally clobbering active alarms; we can't
124 * entirely prevent that here, without even the minimal
125 * locking from the /dev/rtcN api.
126 */
127 retval = rtc_read_alarm(dev, &alm);
128 if (retval < 0)
129 return retval;
130 if (alm.enabled)
131 return -EBUSY;
132
133 alm.enabled = 1;
134 } else {
135 alm.enabled = 0;
136
137 /* Provide a valid future alarm time. Linux isn't EFI,
138 * this time won't be ignored when disabling the alarm.
139 */
140 alarm = now + 300;
141 }
142 rtc_time_to_tm(alarm, &alm.time);
143
144 retval = rtc_set_alarm(dev, &alm);
145 return (retval < 0) ? retval : n;
146}
147static const CLASS_DEVICE_ATTR(wakealarm, S_IRUGO | S_IWUSR,
148 rtc_sysfs_show_wakealarm, rtc_sysfs_set_wakealarm);
149
150
151/* The reason to trigger an alarm with no process watching it (via sysfs)
152 * is its side effect: waking from a system state like suspend-to-RAM or
153 * suspend-to-disk. So: no attribute unless that side effect is possible.
154 * (Userspace may disable that mechanism later.)
155 */
156static inline int rtc_does_wakealarm(struct class_device *class_dev)
157{
158 struct rtc_device *rtc;
159
160 if (!device_can_wakeup(class_dev->dev))
161 return 0;
162 rtc = to_rtc_device(class_dev);
163 return rtc->ops->set_alarm != NULL;
164}
165
166
81static int rtc_sysfs_add_device(struct class_device *class_dev, 167static int rtc_sysfs_add_device(struct class_device *class_dev,
82 struct class_interface *class_intf) 168 struct class_interface *class_intf)
83{ 169{
@@ -87,8 +173,18 @@ static int rtc_sysfs_add_device(struct class_device *class_dev,
87 173
88 err = sysfs_create_group(&class_dev->kobj, &rtc_attr_group); 174 err = sysfs_create_group(&class_dev->kobj, &rtc_attr_group);
89 if (err) 175 if (err)
90 dev_err(class_dev->dev, 176 dev_err(class_dev->dev, "failed to create %s\n",
91 "failed to create sysfs attributes\n"); 177 "sysfs attributes");
178 else if (rtc_does_wakealarm(class_dev)) {
179 /* not all RTCs support both alarms and wakeup */
180 err = class_device_create_file(class_dev,
181 &class_device_attr_wakealarm);
182 if (err) {
183 dev_err(class_dev->dev, "failed to create %s\n",
184 "alarm attribute");
185 sysfs_remove_group(&class_dev->kobj, &rtc_attr_group);
186 }
187 }
92 188
93 return err; 189 return err;
94} 190}
@@ -96,6 +192,9 @@ static int rtc_sysfs_add_device(struct class_device *class_dev,
96static void rtc_sysfs_remove_device(struct class_device *class_dev, 192static void rtc_sysfs_remove_device(struct class_device *class_dev,
97 struct class_interface *class_intf) 193 struct class_interface *class_intf)
98{ 194{
195 if (rtc_does_wakealarm(class_dev))
196 class_device_remove_file(class_dev,
197 &class_device_attr_wakealarm);
99 sysfs_remove_group(&class_dev->kobj, &rtc_attr_group); 198 sysfs_remove_group(&class_dev->kobj, &rtc_attr_group);
100} 199}
101 200