blob: 97444b45cc26e08b2d898db4bf71daea23822d37 [file] [log] [blame]
Alessandro Zummoc5c3e192006-03-27 01:16:39 -08001/*
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
David Brownellab6a2d72007-05-08 00:33:30 -070015#include "rtc-core.h"
16
17
Alessandro Zummoc5c3e192006-03-27 01:16:39 -080018/* device attributes */
19
20static ssize_t rtc_sysfs_show_name(struct class_device *dev, char *buf)
21{
22 return sprintf(buf, "%s\n", to_rtc_device(dev)->name);
23}
24static CLASS_DEVICE_ATTR(name, S_IRUGO, rtc_sysfs_show_name, NULL);
25
26static ssize_t rtc_sysfs_show_date(struct class_device *dev, char *buf)
27{
28 ssize_t retval;
29 struct rtc_time tm;
30
David Brownellab6a2d72007-05-08 00:33:30 -070031 retval = rtc_read_time(to_rtc_device(dev), &tm);
Alessandro Zummoc5c3e192006-03-27 01:16:39 -080032 if (retval == 0) {
33 retval = sprintf(buf, "%04d-%02d-%02d\n",
34 tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday);
35 }
36
37 return retval;
38}
39static CLASS_DEVICE_ATTR(date, S_IRUGO, rtc_sysfs_show_date, NULL);
40
41static ssize_t rtc_sysfs_show_time(struct class_device *dev, char *buf)
42{
43 ssize_t retval;
44 struct rtc_time tm;
45
David Brownellab6a2d72007-05-08 00:33:30 -070046 retval = rtc_read_time(to_rtc_device(dev), &tm);
Alessandro Zummoc5c3e192006-03-27 01:16:39 -080047 if (retval == 0) {
48 retval = sprintf(buf, "%02d:%02d:%02d\n",
49 tm.tm_hour, tm.tm_min, tm.tm_sec);
50 }
51
52 return retval;
53}
54static CLASS_DEVICE_ATTR(time, S_IRUGO, rtc_sysfs_show_time, NULL);
55
56static ssize_t rtc_sysfs_show_since_epoch(struct class_device *dev, char *buf)
57{
58 ssize_t retval;
59 struct rtc_time tm;
60
David Brownellab6a2d72007-05-08 00:33:30 -070061 retval = rtc_read_time(to_rtc_device(dev), &tm);
Alessandro Zummoc5c3e192006-03-27 01:16:39 -080062 if (retval == 0) {
63 unsigned long time;
64 rtc_tm_to_time(&tm, &time);
65 retval = sprintf(buf, "%lu\n", time);
66 }
67
68 return retval;
69}
70static CLASS_DEVICE_ATTR(since_epoch, S_IRUGO, rtc_sysfs_show_since_epoch, NULL);
71
72static struct attribute *rtc_attrs[] = {
73 &class_device_attr_name.attr,
74 &class_device_attr_date.attr,
75 &class_device_attr_time.attr,
76 &class_device_attr_since_epoch.attr,
77 NULL,
78};
79
80static struct attribute_group rtc_attr_group = {
81 .attrs = rtc_attrs,
82};
83
David Brownell3925a5c2007-02-12 00:52:47 -080084
85static ssize_t
86rtc_sysfs_show_wakealarm(struct class_device *dev, char *buf)
87{
88 ssize_t retval;
89 unsigned long alarm;
90 struct rtc_wkalrm alm;
91
92 /* Don't show disabled alarms; but the RTC could leave the
93 * alarm enabled after it's already triggered. Alarms are
94 * conceptually one-shot, even though some common hardware
95 * (PCs) doesn't actually work that way.
96 *
97 * REVISIT maybe we should require RTC implementations to
98 * disable the RTC alarm after it triggers, for uniformity.
99 */
David Brownellab6a2d72007-05-08 00:33:30 -0700100 retval = rtc_read_alarm(to_rtc_device(dev), &alm);
David Brownell3925a5c2007-02-12 00:52:47 -0800101 if (retval == 0 && alm.enabled) {
102 rtc_tm_to_time(&alm.time, &alarm);
103 retval = sprintf(buf, "%lu\n", alarm);
104 }
105
106 return retval;
107}
108
109static ssize_t
110rtc_sysfs_set_wakealarm(struct class_device *dev, const char *buf, size_t n)
111{
112 ssize_t retval;
113 unsigned long now, alarm;
114 struct rtc_wkalrm alm;
David Brownellab6a2d72007-05-08 00:33:30 -0700115 struct rtc_device *rtc = to_rtc_device(dev);
David Brownell3925a5c2007-02-12 00:52:47 -0800116
117 /* Only request alarms that trigger in the future. Disable them
118 * by writing another time, e.g. 0 meaning Jan 1 1970 UTC.
119 */
David Brownellab6a2d72007-05-08 00:33:30 -0700120 retval = rtc_read_time(rtc, &alm.time);
David Brownell3925a5c2007-02-12 00:52:47 -0800121 if (retval < 0)
122 return retval;
123 rtc_tm_to_time(&alm.time, &now);
124
125 alarm = simple_strtoul(buf, NULL, 0);
126 if (alarm > now) {
127 /* Avoid accidentally clobbering active alarms; we can't
128 * entirely prevent that here, without even the minimal
129 * locking from the /dev/rtcN api.
130 */
David Brownellab6a2d72007-05-08 00:33:30 -0700131 retval = rtc_read_alarm(rtc, &alm);
David Brownell3925a5c2007-02-12 00:52:47 -0800132 if (retval < 0)
133 return retval;
134 if (alm.enabled)
135 return -EBUSY;
136
137 alm.enabled = 1;
138 } else {
139 alm.enabled = 0;
140
141 /* Provide a valid future alarm time. Linux isn't EFI,
142 * this time won't be ignored when disabling the alarm.
143 */
144 alarm = now + 300;
145 }
146 rtc_time_to_tm(alarm, &alm.time);
147
David Brownellab6a2d72007-05-08 00:33:30 -0700148 retval = rtc_set_alarm(rtc, &alm);
David Brownell3925a5c2007-02-12 00:52:47 -0800149 return (retval < 0) ? retval : n;
150}
151static const CLASS_DEVICE_ATTR(wakealarm, S_IRUGO | S_IWUSR,
152 rtc_sysfs_show_wakealarm, rtc_sysfs_set_wakealarm);
153
154
155/* The reason to trigger an alarm with no process watching it (via sysfs)
156 * is its side effect: waking from a system state like suspend-to-RAM or
157 * suspend-to-disk. So: no attribute unless that side effect is possible.
158 * (Userspace may disable that mechanism later.)
159 */
160static inline int rtc_does_wakealarm(struct class_device *class_dev)
161{
162 struct rtc_device *rtc;
163
164 if (!device_can_wakeup(class_dev->dev))
165 return 0;
166 rtc = to_rtc_device(class_dev);
167 return rtc->ops->set_alarm != NULL;
168}
169
170
Mike Frysingera8d814b2007-01-26 00:57:08 -0800171static int rtc_sysfs_add_device(struct class_device *class_dev,
Alessandro Zummoc5c3e192006-03-27 01:16:39 -0800172 struct class_interface *class_intf)
173{
174 int err;
175
David Brownell5a6534e2006-12-13 00:35:06 -0800176 dev_dbg(class_dev->dev, "rtc intf: sysfs\n");
Alessandro Zummoc5c3e192006-03-27 01:16:39 -0800177
178 err = sysfs_create_group(&class_dev->kobj, &rtc_attr_group);
179 if (err)
David Brownell3925a5c2007-02-12 00:52:47 -0800180 dev_err(class_dev->dev, "failed to create %s\n",
181 "sysfs attributes");
182 else if (rtc_does_wakealarm(class_dev)) {
183 /* not all RTCs support both alarms and wakeup */
184 err = class_device_create_file(class_dev,
185 &class_device_attr_wakealarm);
186 if (err) {
187 dev_err(class_dev->dev, "failed to create %s\n",
188 "alarm attribute");
189 sysfs_remove_group(&class_dev->kobj, &rtc_attr_group);
190 }
191 }
Alessandro Zummoc5c3e192006-03-27 01:16:39 -0800192
193 return err;
194}
195
196static void rtc_sysfs_remove_device(struct class_device *class_dev,
197 struct class_interface *class_intf)
198{
David Brownell3925a5c2007-02-12 00:52:47 -0800199 if (rtc_does_wakealarm(class_dev))
200 class_device_remove_file(class_dev,
201 &class_device_attr_wakealarm);
Alessandro Zummoc5c3e192006-03-27 01:16:39 -0800202 sysfs_remove_group(&class_dev->kobj, &rtc_attr_group);
203}
204
205/* interface registration */
206
207static struct class_interface rtc_sysfs_interface = {
208 .add = &rtc_sysfs_add_device,
209 .remove = &rtc_sysfs_remove_device,
210};
211
212static int __init rtc_sysfs_init(void)
213{
214 return rtc_interface_register(&rtc_sysfs_interface);
215}
216
217static void __exit rtc_sysfs_exit(void)
218{
219 class_interface_unregister(&rtc_sysfs_interface);
220}
221
Andrew Morton4e9011d2006-10-01 02:22:41 -0700222subsys_initcall(rtc_sysfs_init);
Alessandro Zummoc5c3e192006-03-27 01:16:39 -0800223module_exit(rtc_sysfs_exit);
224
225MODULE_AUTHOR("Alessandro Zummo <a.zummo@towertech.it>");
226MODULE_DESCRIPTION("RTC class sysfs interface");
227MODULE_LICENSE("GPL");