Root/
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 | |
20 | /* |
21 | * NOTE: RTC times displayed in sysfs use the RTC's timezone. That's |
22 | * ideally UTC. However, PCs that also boot to MS-Windows normally use |
23 | * the local time and change to match daylight savings time. That affects |
24 | * attributes including date, time, since_epoch, and wakealarm. |
25 | */ |
26 | |
27 | static ssize_t |
28 | rtc_sysfs_show_name(struct device *dev, struct device_attribute *attr, |
29 | char *buf) |
30 | { |
31 | return sprintf(buf, "%s\n", to_rtc_device(dev)->name); |
32 | } |
33 | |
34 | static ssize_t |
35 | rtc_sysfs_show_date(struct device *dev, struct device_attribute *attr, |
36 | char *buf) |
37 | { |
38 | ssize_t retval; |
39 | struct rtc_time tm; |
40 | |
41 | retval = rtc_read_time(to_rtc_device(dev), &tm); |
42 | if (retval == 0) { |
43 | retval = sprintf(buf, "%04d-%02d-%02d\n", |
44 | tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday); |
45 | } |
46 | |
47 | return retval; |
48 | } |
49 | |
50 | static ssize_t |
51 | rtc_sysfs_show_time(struct device *dev, struct device_attribute *attr, |
52 | char *buf) |
53 | { |
54 | ssize_t retval; |
55 | struct rtc_time tm; |
56 | |
57 | retval = rtc_read_time(to_rtc_device(dev), &tm); |
58 | if (retval == 0) { |
59 | retval = sprintf(buf, "%02d:%02d:%02d\n", |
60 | tm.tm_hour, tm.tm_min, tm.tm_sec); |
61 | } |
62 | |
63 | return retval; |
64 | } |
65 | |
66 | static ssize_t |
67 | rtc_sysfs_show_since_epoch(struct device *dev, struct device_attribute *attr, |
68 | char *buf) |
69 | { |
70 | ssize_t retval; |
71 | struct rtc_time tm; |
72 | |
73 | retval = rtc_read_time(to_rtc_device(dev), &tm); |
74 | if (retval == 0) { |
75 | unsigned long time; |
76 | rtc_tm_to_time(&tm, &time); |
77 | retval = sprintf(buf, "%lu\n", time); |
78 | } |
79 | |
80 | return retval; |
81 | } |
82 | |
83 | static ssize_t |
84 | rtc_sysfs_show_max_user_freq(struct device *dev, struct device_attribute *attr, |
85 | char *buf) |
86 | { |
87 | return sprintf(buf, "%d\n", to_rtc_device(dev)->max_user_freq); |
88 | } |
89 | |
90 | static ssize_t |
91 | rtc_sysfs_set_max_user_freq(struct device *dev, struct device_attribute *attr, |
92 | const char *buf, size_t n) |
93 | { |
94 | struct rtc_device *rtc = to_rtc_device(dev); |
95 | unsigned long val = simple_strtoul(buf, NULL, 0); |
96 | |
97 | if (val >= 4096 || val == 0) |
98 | return -EINVAL; |
99 | |
100 | rtc->max_user_freq = (int)val; |
101 | |
102 | return n; |
103 | } |
104 | |
105 | /** |
106 | * rtc_sysfs_show_hctosys - indicate if the given RTC set the system time |
107 | * |
108 | * Returns 1 if the system clock was set by this RTC at the last |
109 | * boot or resume event. |
110 | */ |
111 | static ssize_t |
112 | rtc_sysfs_show_hctosys(struct device *dev, struct device_attribute *attr, |
113 | char *buf) |
114 | { |
115 | #ifdef CONFIG_RTC_HCTOSYS_DEVICE |
116 | if (rtc_hctosys_ret == 0 && |
117 | strcmp(dev_name(&to_rtc_device(dev)->dev), |
118 | CONFIG_RTC_HCTOSYS_DEVICE) == 0) |
119 | return sprintf(buf, "1\n"); |
120 | else |
121 | #endif |
122 | return sprintf(buf, "0\n"); |
123 | } |
124 | |
125 | static struct device_attribute rtc_attrs[] = { |
126 | __ATTR(name, S_IRUGO, rtc_sysfs_show_name, NULL), |
127 | __ATTR(date, S_IRUGO, rtc_sysfs_show_date, NULL), |
128 | __ATTR(time, S_IRUGO, rtc_sysfs_show_time, NULL), |
129 | __ATTR(since_epoch, S_IRUGO, rtc_sysfs_show_since_epoch, NULL), |
130 | __ATTR(max_user_freq, S_IRUGO | S_IWUSR, rtc_sysfs_show_max_user_freq, |
131 | rtc_sysfs_set_max_user_freq), |
132 | __ATTR(hctosys, S_IRUGO, rtc_sysfs_show_hctosys, NULL), |
133 | { }, |
134 | }; |
135 | |
136 | static ssize_t |
137 | rtc_sysfs_show_wakealarm(struct device *dev, struct device_attribute *attr, |
138 | char *buf) |
139 | { |
140 | ssize_t retval; |
141 | unsigned long alarm; |
142 | struct rtc_wkalrm alm; |
143 | |
144 | /* Don't show disabled alarms. For uniformity, RTC alarms are |
145 | * conceptually one-shot, even though some common RTCs (on PCs) |
146 | * don't actually work that way. |
147 | * |
148 | * NOTE: RTC implementations where the alarm doesn't match an |
149 | * exact YYYY-MM-DD HH:MM[:SS] date *must* disable their RTC |
150 | * alarms after they trigger, to ensure one-shot semantics. |
151 | */ |
152 | retval = rtc_read_alarm(to_rtc_device(dev), &alm); |
153 | if (retval == 0 && alm.enabled) { |
154 | rtc_tm_to_time(&alm.time, &alarm); |
155 | retval = sprintf(buf, "%lu\n", alarm); |
156 | } |
157 | |
158 | return retval; |
159 | } |
160 | |
161 | static ssize_t |
162 | rtc_sysfs_set_wakealarm(struct device *dev, struct device_attribute *attr, |
163 | const char *buf, size_t n) |
164 | { |
165 | ssize_t retval; |
166 | unsigned long now, alarm; |
167 | struct rtc_wkalrm alm; |
168 | struct rtc_device *rtc = to_rtc_device(dev); |
169 | char *buf_ptr; |
170 | int adjust = 0; |
171 | |
172 | /* Only request alarms that trigger in the future. Disable them |
173 | * by writing another time, e.g. 0 meaning Jan 1 1970 UTC. |
174 | */ |
175 | retval = rtc_read_time(rtc, &alm.time); |
176 | if (retval < 0) |
177 | return retval; |
178 | rtc_tm_to_time(&alm.time, &now); |
179 | |
180 | buf_ptr = (char *)buf; |
181 | if (*buf_ptr == '+') { |
182 | buf_ptr++; |
183 | adjust = 1; |
184 | } |
185 | alarm = simple_strtoul(buf_ptr, NULL, 0); |
186 | if (adjust) { |
187 | alarm += now; |
188 | } |
189 | if (alarm > now) { |
190 | /* Avoid accidentally clobbering active alarms; we can't |
191 | * entirely prevent that here, without even the minimal |
192 | * locking from the /dev/rtcN api. |
193 | */ |
194 | retval = rtc_read_alarm(rtc, &alm); |
195 | if (retval < 0) |
196 | return retval; |
197 | if (alm.enabled) |
198 | return -EBUSY; |
199 | |
200 | alm.enabled = 1; |
201 | } else { |
202 | alm.enabled = 0; |
203 | |
204 | /* Provide a valid future alarm time. Linux isn't EFI, |
205 | * this time won't be ignored when disabling the alarm. |
206 | */ |
207 | alarm = now + 300; |
208 | } |
209 | rtc_time_to_tm(alarm, &alm.time); |
210 | |
211 | retval = rtc_set_alarm(rtc, &alm); |
212 | return (retval < 0) ? retval : n; |
213 | } |
214 | static DEVICE_ATTR(wakealarm, S_IRUGO | S_IWUSR, |
215 | rtc_sysfs_show_wakealarm, rtc_sysfs_set_wakealarm); |
216 | |
217 | |
218 | /* The reason to trigger an alarm with no process watching it (via sysfs) |
219 | * is its side effect: waking from a system state like suspend-to-RAM or |
220 | * suspend-to-disk. So: no attribute unless that side effect is possible. |
221 | * (Userspace may disable that mechanism later.) |
222 | */ |
223 | static inline int rtc_does_wakealarm(struct rtc_device *rtc) |
224 | { |
225 | if (!device_can_wakeup(rtc->dev.parent)) |
226 | return 0; |
227 | return rtc->ops->set_alarm != NULL; |
228 | } |
229 | |
230 | |
231 | void rtc_sysfs_add_device(struct rtc_device *rtc) |
232 | { |
233 | int err; |
234 | |
235 | /* not all RTCs support both alarms and wakeup */ |
236 | if (!rtc_does_wakealarm(rtc)) |
237 | return; |
238 | |
239 | err = device_create_file(&rtc->dev, &dev_attr_wakealarm); |
240 | if (err) |
241 | dev_err(rtc->dev.parent, |
242 | "failed to create alarm attribute, %d\n", err); |
243 | } |
244 | |
245 | void rtc_sysfs_del_device(struct rtc_device *rtc) |
246 | { |
247 | /* REVISIT did we add it successfully? */ |
248 | if (rtc_does_wakealarm(rtc)) |
249 | device_remove_file(&rtc->dev, &dev_attr_wakealarm); |
250 | } |
251 | |
252 | void __init rtc_sysfs_init(struct class *rtc_class) |
253 | { |
254 | rtc_class->dev_attrs = rtc_attrs; |
255 | } |
256 |
Branches:
ben-wpan
ben-wpan-stefan
javiroman/ks7010
jz-2.6.34
jz-2.6.34-rc5
jz-2.6.34-rc6
jz-2.6.34-rc7
jz-2.6.35
jz-2.6.36
jz-2.6.37
jz-2.6.38
jz-2.6.39
jz-3.0
jz-3.1
jz-3.11
jz-3.12
jz-3.13
jz-3.15
jz-3.16
jz-3.18-dt
jz-3.2
jz-3.3
jz-3.4
jz-3.5
jz-3.6
jz-3.6-rc2-pwm
jz-3.9
jz-3.9-clk
jz-3.9-rc8
jz47xx
jz47xx-2.6.38
master
Tags:
od-2011-09-04
od-2011-09-18
v2.6.34-rc5
v2.6.34-rc6
v2.6.34-rc7
v3.9