linux/drivers/rtc/rtc-sysfs.c
<<
>>
Prefs
   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
  28name_show(struct device *dev, struct device_attribute *attr, char *buf)
  29{
  30        return sprintf(buf, "%s\n", to_rtc_device(dev)->name);
  31}
  32static DEVICE_ATTR_RO(name);
  33
  34static ssize_t
  35date_show(struct device *dev, struct device_attribute *attr, char *buf)
  36{
  37        ssize_t retval;
  38        struct rtc_time tm;
  39
  40        retval = rtc_read_time(to_rtc_device(dev), &tm);
  41        if (retval == 0) {
  42                retval = sprintf(buf, "%04d-%02d-%02d\n",
  43                        tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday);
  44        }
  45
  46        return retval;
  47}
  48static DEVICE_ATTR_RO(date);
  49
  50static ssize_t
  51time_show(struct device *dev, struct device_attribute *attr, char *buf)
  52{
  53        ssize_t retval;
  54        struct rtc_time tm;
  55
  56        retval = rtc_read_time(to_rtc_device(dev), &tm);
  57        if (retval == 0) {
  58                retval = sprintf(buf, "%02d:%02d:%02d\n",
  59                        tm.tm_hour, tm.tm_min, tm.tm_sec);
  60        }
  61
  62        return retval;
  63}
  64static DEVICE_ATTR_RO(time);
  65
  66static ssize_t
  67since_epoch_show(struct device *dev, struct device_attribute *attr, char *buf)
  68{
  69        ssize_t retval;
  70        struct rtc_time tm;
  71
  72        retval = rtc_read_time(to_rtc_device(dev), &tm);
  73        if (retval == 0) {
  74                unsigned long time;
  75                rtc_tm_to_time(&tm, &time);
  76                retval = sprintf(buf, "%lu\n", time);
  77        }
  78
  79        return retval;
  80}
  81static DEVICE_ATTR_RO(since_epoch);
  82
  83static ssize_t
  84max_user_freq_show(struct device *dev, struct device_attribute *attr, char *buf)
  85{
  86        return sprintf(buf, "%d\n", to_rtc_device(dev)->max_user_freq);
  87}
  88
  89static ssize_t
  90max_user_freq_store(struct device *dev, struct device_attribute *attr,
  91                const char *buf, size_t n)
  92{
  93        struct rtc_device *rtc = to_rtc_device(dev);
  94        unsigned long val = simple_strtoul(buf, NULL, 0);
  95
  96        if (val >= 4096 || val == 0)
  97                return -EINVAL;
  98
  99        rtc->max_user_freq = (int)val;
 100
 101        return n;
 102}
 103static DEVICE_ATTR_RW(max_user_freq);
 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
 112hctosys_show(struct device *dev, struct device_attribute *attr, char *buf)
 113{
 114#ifdef CONFIG_RTC_HCTOSYS_DEVICE
 115        if (rtc_hctosys_ret == 0 &&
 116                        strcmp(dev_name(&to_rtc_device(dev)->dev),
 117                                CONFIG_RTC_HCTOSYS_DEVICE) == 0)
 118                return sprintf(buf, "1\n");
 119        else
 120#endif
 121                return sprintf(buf, "0\n");
 122}
 123static DEVICE_ATTR_RO(hctosys);
 124
 125static struct attribute *rtc_attrs[] = {
 126        &dev_attr_name.attr,
 127        &dev_attr_date.attr,
 128        &dev_attr_time.attr,
 129        &dev_attr_since_epoch.attr,
 130        &dev_attr_max_user_freq.attr,
 131        &dev_attr_hctosys.attr,
 132        NULL,
 133};
 134ATTRIBUTE_GROUPS(rtc);
 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        unsigned long push = 0;
 168        struct rtc_wkalrm alm;
 169        struct rtc_device *rtc = to_rtc_device(dev);
 170        char *buf_ptr;
 171        int adjust = 0;
 172
 173        /* Only request alarms that trigger in the future.  Disable them
 174         * by writing another time, e.g. 0 meaning Jan 1 1970 UTC.
 175         */
 176        retval = rtc_read_time(rtc, &alm.time);
 177        if (retval < 0)
 178                return retval;
 179        rtc_tm_to_time(&alm.time, &now);
 180
 181        buf_ptr = (char *)buf;
 182        if (*buf_ptr == '+') {
 183                buf_ptr++;
 184                if (*buf_ptr == '=') {
 185                        buf_ptr++;
 186                        push = 1;
 187                } else
 188                        adjust = 1;
 189        }
 190        alarm = simple_strtoul(buf_ptr, NULL, 0);
 191        if (adjust) {
 192                alarm += now;
 193        }
 194        if (alarm > now || push) {
 195                /* Avoid accidentally clobbering active alarms; we can't
 196                 * entirely prevent that here, without even the minimal
 197                 * locking from the /dev/rtcN api.
 198                 */
 199                retval = rtc_read_alarm(rtc, &alm);
 200                if (retval < 0)
 201                        return retval;
 202                if (alm.enabled) {
 203                        if (push) {
 204                                rtc_tm_to_time(&alm.time, &push);
 205                                alarm += push;
 206                        } else
 207                                return -EBUSY;
 208                } else if (push)
 209                        return -EINVAL;
 210                alm.enabled = 1;
 211        } else {
 212                alm.enabled = 0;
 213
 214                /* Provide a valid future alarm time.  Linux isn't EFI,
 215                 * this time won't be ignored when disabling the alarm.
 216                 */
 217                alarm = now + 300;
 218        }
 219        rtc_time_to_tm(alarm, &alm.time);
 220
 221        retval = rtc_set_alarm(rtc, &alm);
 222        return (retval < 0) ? retval : n;
 223}
 224static DEVICE_ATTR(wakealarm, S_IRUGO | S_IWUSR,
 225                rtc_sysfs_show_wakealarm, rtc_sysfs_set_wakealarm);
 226
 227
 228/* The reason to trigger an alarm with no process watching it (via sysfs)
 229 * is its side effect:  waking from a system state like suspend-to-RAM or
 230 * suspend-to-disk.  So: no attribute unless that side effect is possible.
 231 * (Userspace may disable that mechanism later.)
 232 */
 233static inline int rtc_does_wakealarm(struct rtc_device *rtc)
 234{
 235        if (!device_can_wakeup(rtc->dev.parent))
 236                return 0;
 237        return rtc->ops->set_alarm != NULL;
 238}
 239
 240
 241void rtc_sysfs_add_device(struct rtc_device *rtc)
 242{
 243        int err;
 244
 245        /* not all RTCs support both alarms and wakeup */
 246        if (!rtc_does_wakealarm(rtc))
 247                return;
 248
 249        err = device_create_file(&rtc->dev, &dev_attr_wakealarm);
 250        if (err)
 251                dev_err(rtc->dev.parent,
 252                        "failed to create alarm attribute, %d\n", err);
 253}
 254
 255void rtc_sysfs_del_device(struct rtc_device *rtc)
 256{
 257        /* REVISIT did we add it successfully? */
 258        if (rtc_does_wakealarm(rtc))
 259                device_remove_file(&rtc->dev, &dev_attr_wakealarm);
 260}
 261
 262void __init rtc_sysfs_init(struct class *rtc_class)
 263{
 264        rtc_class->dev_groups = rtc_groups;
 265}
 266
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.