Blame view
drivers/rtc/rtc-sysfs.c
7.05 KB
c5c3e1922 [PATCH] RTC subsy... |
1 2 3 4 5 6 7 8 9 10 11 12 13 |
/* * RTC subsystem, sysfs interface * * Copyright (C) 2005 Tower Technologies * Author: Alessandro Zummo <a.zummo@towertech.it> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ #include <linux/module.h> #include <linux/rtc.h> |
ab6a2d70d rtc: rtc interfac... |
14 |
#include "rtc-core.h" |
c5c3e1922 [PATCH] RTC subsy... |
15 |
/* device attributes */ |
8a0bdfd7a rtc-cmos alarm ac... |
16 17 18 19 20 21 |
/* * NOTE: RTC times displayed in sysfs use the RTC's timezone. That's * ideally UTC. However, PCs that also boot to MS-Windows normally use * the local time and change to match daylight savings time. That affects * attributes including date, time, since_epoch, and wakealarm. */ |
cd9662094 rtc: remove rest ... |
22 |
static ssize_t |
f21e68350 rtc: convert clas... |
23 |
name_show(struct device *dev, struct device_attribute *attr, char *buf) |
c5c3e1922 [PATCH] RTC subsy... |
24 25 26 27 |
{ return sprintf(buf, "%s ", to_rtc_device(dev)->name); } |
f21e68350 rtc: convert clas... |
28 |
static DEVICE_ATTR_RO(name); |
c5c3e1922 [PATCH] RTC subsy... |
29 |
|
cd9662094 rtc: remove rest ... |
30 |
static ssize_t |
f21e68350 rtc: convert clas... |
31 |
date_show(struct device *dev, struct device_attribute *attr, char *buf) |
c5c3e1922 [PATCH] RTC subsy... |
32 33 34 |
{ ssize_t retval; struct rtc_time tm; |
ab6a2d70d rtc: rtc interfac... |
35 |
retval = rtc_read_time(to_rtc_device(dev), &tm); |
c5c3e1922 [PATCH] RTC subsy... |
36 37 38 39 40 41 42 43 |
if (retval == 0) { retval = sprintf(buf, "%04d-%02d-%02d ", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday); } return retval; } |
f21e68350 rtc: convert clas... |
44 |
static DEVICE_ATTR_RO(date); |
c5c3e1922 [PATCH] RTC subsy... |
45 |
|
cd9662094 rtc: remove rest ... |
46 |
static ssize_t |
f21e68350 rtc: convert clas... |
47 |
time_show(struct device *dev, struct device_attribute *attr, char *buf) |
c5c3e1922 [PATCH] RTC subsy... |
48 49 50 |
{ ssize_t retval; struct rtc_time tm; |
ab6a2d70d rtc: rtc interfac... |
51 |
retval = rtc_read_time(to_rtc_device(dev), &tm); |
c5c3e1922 [PATCH] RTC subsy... |
52 53 54 55 56 57 58 59 |
if (retval == 0) { retval = sprintf(buf, "%02d:%02d:%02d ", tm.tm_hour, tm.tm_min, tm.tm_sec); } return retval; } |
f21e68350 rtc: convert clas... |
60 |
static DEVICE_ATTR_RO(time); |
c5c3e1922 [PATCH] RTC subsy... |
61 |
|
cd9662094 rtc: remove rest ... |
62 |
static ssize_t |
f21e68350 rtc: convert clas... |
63 |
since_epoch_show(struct device *dev, struct device_attribute *attr, char *buf) |
c5c3e1922 [PATCH] RTC subsy... |
64 65 66 |
{ ssize_t retval; struct rtc_time tm; |
ab6a2d70d rtc: rtc interfac... |
67 |
retval = rtc_read_time(to_rtc_device(dev), &tm); |
c5c3e1922 [PATCH] RTC subsy... |
68 69 70 71 72 73 74 75 76 |
if (retval == 0) { unsigned long time; rtc_tm_to_time(&tm, &time); retval = sprintf(buf, "%lu ", time); } return retval; } |
f21e68350 rtc: convert clas... |
77 |
static DEVICE_ATTR_RO(since_epoch); |
c5c3e1922 [PATCH] RTC subsy... |
78 |
|
06c65eb45 rtc: add max_user... |
79 |
static ssize_t |
f21e68350 rtc: convert clas... |
80 |
max_user_freq_show(struct device *dev, struct device_attribute *attr, char *buf) |
06c65eb45 rtc: add max_user... |
81 82 83 84 85 86 |
{ return sprintf(buf, "%d ", to_rtc_device(dev)->max_user_freq); } static ssize_t |
f21e68350 rtc: convert clas... |
87 |
max_user_freq_store(struct device *dev, struct device_attribute *attr, |
06c65eb45 rtc: add max_user... |
88 89 90 |
const char *buf, size_t n) { struct rtc_device *rtc = to_rtc_device(dev); |
f571287bd rtc: Replace simp... |
91 92 93 94 95 96 |
unsigned long val; int err; err = kstrtoul(buf, 0, &val); if (err) return err; |
06c65eb45 rtc: add max_user... |
97 98 99 100 101 102 103 104 |
if (val >= 4096 || val == 0) return -EINVAL; rtc->max_user_freq = (int)val; return n; } |
f21e68350 rtc: convert clas... |
105 |
static DEVICE_ATTR_RW(max_user_freq); |
06c65eb45 rtc: add max_user... |
106 |
|
4c24e29e6 rtc_sysfs_show_hc... |
107 108 109 110 111 112 |
/** * rtc_sysfs_show_hctosys - indicate if the given RTC set the system time * * Returns 1 if the system clock was set by this RTC at the last * boot or resume event. */ |
d8c1acb16 rtc: add boot_tim... |
113 |
static ssize_t |
f21e68350 rtc: convert clas... |
114 |
hctosys_show(struct device *dev, struct device_attribute *attr, char *buf) |
d8c1acb16 rtc: add boot_tim... |
115 116 |
{ #ifdef CONFIG_RTC_HCTOSYS_DEVICE |
d0ab4a4d5 rtc/hctosys: only... |
117 118 119 |
if (rtc_hctosys_ret == 0 && strcmp(dev_name(&to_rtc_device(dev)->dev), CONFIG_RTC_HCTOSYS_DEVICE) == 0) |
d8c1acb16 rtc: add boot_tim... |
120 121 122 123 124 125 126 |
return sprintf(buf, "1 "); else #endif return sprintf(buf, "0 "); } |
f21e68350 rtc: convert clas... |
127 |
static DEVICE_ATTR_RO(hctosys); |
3925a5ce4 [PATCH] RTC gets ... |
128 |
static ssize_t |
a17ccd1c6 rtc: switch wakea... |
129 |
wakealarm_show(struct device *dev, struct device_attribute *attr, char *buf) |
3925a5ce4 [PATCH] RTC gets ... |
130 131 132 133 |
{ ssize_t retval; unsigned long alarm; struct rtc_wkalrm alm; |
8a0bdfd7a rtc-cmos alarm ac... |
134 135 136 |
/* Don't show disabled alarms. For uniformity, RTC alarms are * conceptually one-shot, even though some common RTCs (on PCs) * don't actually work that way. |
3925a5ce4 [PATCH] RTC gets ... |
137 |
* |
8a0bdfd7a rtc-cmos alarm ac... |
138 139 140 |
* NOTE: RTC implementations where the alarm doesn't match an * exact YYYY-MM-DD HH:MM[:SS] date *must* disable their RTC * alarms after they trigger, to ensure one-shot semantics. |
3925a5ce4 [PATCH] RTC gets ... |
141 |
*/ |
ab6a2d70d rtc: rtc interfac... |
142 |
retval = rtc_read_alarm(to_rtc_device(dev), &alm); |
3925a5ce4 [PATCH] RTC gets ... |
143 144 145 146 147 148 149 150 151 152 |
if (retval == 0 && alm.enabled) { rtc_tm_to_time(&alm.time, &alarm); retval = sprintf(buf, "%lu ", alarm); } return retval; } static ssize_t |
a17ccd1c6 rtc: switch wakea... |
153 |
wakealarm_store(struct device *dev, struct device_attribute *attr, |
cd9662094 rtc: remove rest ... |
154 |
const char *buf, size_t n) |
3925a5ce4 [PATCH] RTC gets ... |
155 156 157 |
{ ssize_t retval; unsigned long now, alarm; |
1df0a4711 rtc: add ability ... |
158 |
unsigned long push = 0; |
3925a5ce4 [PATCH] RTC gets ... |
159 |
struct rtc_wkalrm alm; |
ab6a2d70d rtc: rtc interfac... |
160 |
struct rtc_device *rtc = to_rtc_device(dev); |
84281c2d7 rtc: sysfs: fix a... |
161 |
const char *buf_ptr; |
c116bc2ae rtc: add the supp... |
162 |
int adjust = 0; |
3925a5ce4 [PATCH] RTC gets ... |
163 164 165 166 |
/* Only request alarms that trigger in the future. Disable them * by writing another time, e.g. 0 meaning Jan 1 1970 UTC. */ |
ab6a2d70d rtc: rtc interfac... |
167 |
retval = rtc_read_time(rtc, &alm.time); |
3925a5ce4 [PATCH] RTC gets ... |
168 169 170 |
if (retval < 0) return retval; rtc_tm_to_time(&alm.time, &now); |
84281c2d7 rtc: sysfs: fix a... |
171 |
buf_ptr = buf; |
c116bc2ae rtc: add the supp... |
172 173 |
if (*buf_ptr == '+') { buf_ptr++; |
1df0a4711 rtc: add ability ... |
174 175 176 177 178 |
if (*buf_ptr == '=') { buf_ptr++; push = 1; } else adjust = 1; |
c116bc2ae rtc: add the supp... |
179 |
} |
f571287bd rtc: Replace simp... |
180 181 182 |
retval = kstrtoul(buf_ptr, 0, &alarm); if (retval) return retval; |
c116bc2ae rtc: add the supp... |
183 184 185 |
if (adjust) { alarm += now; } |
1df0a4711 rtc: add ability ... |
186 |
if (alarm > now || push) { |
3925a5ce4 [PATCH] RTC gets ... |
187 188 189 190 |
/* Avoid accidentally clobbering active alarms; we can't * entirely prevent that here, without even the minimal * locking from the /dev/rtcN api. */ |
ab6a2d70d rtc: rtc interfac... |
191 |
retval = rtc_read_alarm(rtc, &alm); |
3925a5ce4 [PATCH] RTC gets ... |
192 193 |
if (retval < 0) return retval; |
1df0a4711 rtc: add ability ... |
194 195 196 197 198 199 200 201 |
if (alm.enabled) { if (push) { rtc_tm_to_time(&alm.time, &push); alarm += push; } else return -EBUSY; } else if (push) return -EINVAL; |
3925a5ce4 [PATCH] RTC gets ... |
202 203 204 205 206 207 208 209 210 211 |
alm.enabled = 1; } else { alm.enabled = 0; /* Provide a valid future alarm time. Linux isn't EFI, * this time won't be ignored when disabling the alarm. */ alarm = now + 300; } rtc_time_to_tm(alarm, &alm.time); |
ab6a2d70d rtc: rtc interfac... |
212 |
retval = rtc_set_alarm(rtc, &alm); |
3925a5ce4 [PATCH] RTC gets ... |
213 214 |
return (retval < 0) ? retval : n; } |
a17ccd1c6 rtc: switch wakea... |
215 |
static DEVICE_ATTR_RW(wakealarm); |
3925a5ce4 [PATCH] RTC gets ... |
216 |
|
5495a4159 rtc: implement a ... |
217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 |
static ssize_t offset_show(struct device *dev, struct device_attribute *attr, char *buf) { ssize_t retval; long offset; retval = rtc_read_offset(to_rtc_device(dev), &offset); if (retval == 0) retval = sprintf(buf, "%ld ", offset); return retval; } static ssize_t offset_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t n) { ssize_t retval; long offset; retval = kstrtol(buf, 10, &offset); if (retval == 0) retval = rtc_set_offset(to_rtc_device(dev), offset); return (retval < 0) ? retval : n; } static DEVICE_ATTR_RW(offset); |
3ee2c40b7 rtc: switch to us... |
245 246 247 248 249 250 251 252 |
static struct attribute *rtc_attrs[] = { &dev_attr_name.attr, &dev_attr_date.attr, &dev_attr_time.attr, &dev_attr_since_epoch.attr, &dev_attr_max_user_freq.attr, &dev_attr_hctosys.attr, &dev_attr_wakealarm.attr, |
5495a4159 rtc: implement a ... |
253 |
&dev_attr_offset.attr, |
3ee2c40b7 rtc: switch to us... |
254 255 |
NULL, }; |
3925a5ce4 [PATCH] RTC gets ... |
256 257 258 259 260 261 |
/* The reason to trigger an alarm with no process watching it (via sysfs) * is its side effect: waking from a system state like suspend-to-RAM or * suspend-to-disk. So: no attribute unless that side effect is possible. * (Userspace may disable that mechanism later.) */ |
df100c017 rtc: make rtc_doe... |
262 |
static bool rtc_does_wakealarm(struct rtc_device *rtc) |
3925a5ce4 [PATCH] RTC gets ... |
263 |
{ |
cd9662094 rtc: remove rest ... |
264 |
if (!device_can_wakeup(rtc->dev.parent)) |
df100c017 rtc: make rtc_doe... |
265 |
return false; |
3925a5ce4 [PATCH] RTC gets ... |
266 267 |
return rtc->ops->set_alarm != NULL; } |
3ee2c40b7 rtc: switch to us... |
268 269 |
static umode_t rtc_attr_is_visible(struct kobject *kobj, struct attribute *attr, int n) |
c5c3e1922 [PATCH] RTC subsy... |
270 |
{ |
3ee2c40b7 rtc: switch to us... |
271 272 273 |
struct device *dev = container_of(kobj, struct device, kobj); struct rtc_device *rtc = to_rtc_device(dev); umode_t mode = attr->mode; |
c5c3e1922 [PATCH] RTC subsy... |
274 |
|
5495a4159 rtc: implement a ... |
275 |
if (attr == &dev_attr_wakealarm.attr) { |
3ee2c40b7 rtc: switch to us... |
276 277 |
if (!rtc_does_wakealarm(rtc)) mode = 0; |
5495a4159 rtc: implement a ... |
278 279 280 281 |
} else if (attr == &dev_attr_offset.attr) { if (!rtc->ops->set_offset) mode = 0; } |
c5c3e1922 [PATCH] RTC subsy... |
282 |
|
3ee2c40b7 rtc: switch to us... |
283 |
return mode; |
c5c3e1922 [PATCH] RTC subsy... |
284 |
} |
3ee2c40b7 rtc: switch to us... |
285 286 287 288 289 290 291 292 293 |
static struct attribute_group rtc_attr_group = { .is_visible = rtc_attr_is_visible, .attrs = rtc_attrs, }; static const struct attribute_group *rtc_attr_groups[] = { &rtc_attr_group, NULL }; |
c5c3e1922 [PATCH] RTC subsy... |
294 |
|
3ee2c40b7 rtc: switch to us... |
295 |
const struct attribute_group **rtc_get_dev_attribute_groups(void) |
c5c3e1922 [PATCH] RTC subsy... |
296 |
{ |
3ee2c40b7 rtc: switch to us... |
297 |
return rtc_attr_groups; |
c5c3e1922 [PATCH] RTC subsy... |
298 |
} |