aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-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