Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
at v3.10 255 lines 6.4 kB view raw
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 27static ssize_t 28rtc_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 34static ssize_t 35rtc_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 50static ssize_t 51rtc_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 66static ssize_t 67rtc_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 83static ssize_t 84rtc_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 90static ssize_t 91rtc_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 */ 111static ssize_t 112rtc_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 125static 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 136static ssize_t 137rtc_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 161static ssize_t 162rtc_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} 214static 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 */ 223static 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 231void 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 245void 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 252void __init rtc_sysfs_init(struct class *rtc_class) 253{ 254 rtc_class->dev_attrs = rtc_attrs; 255}