linux/arch/arm/common/rtctime.c
<<
>>
Prefs
   1/*
   2 *  linux/arch/arm/common/rtctime.c
   3 *
   4 *  Copyright (C) 2003 Deep Blue Solutions Ltd.
   5 *  Based on sa1100-rtc.c, Nils Faerber, CIH, Nicolas Pitre.
   6 *  Based on rtc.c by Paul Gortmaker
   7 *
   8 * This program is free software; you can redistribute it and/or modify
   9 * it under the terms of the GNU General Public License version 2 as
  10 * published by the Free Software Foundation.
  11 */
  12#include <linux/module.h>
  13#include <linux/kernel.h>
  14#include <linux/time.h>
  15#include <linux/rtc.h>
  16#include <linux/poll.h>
  17#include <linux/proc_fs.h>
  18#include <linux/miscdevice.h>
  19#include <linux/spinlock.h>
  20#include <linux/capability.h>
  21#include <linux/device.h>
  22#include <linux/mutex.h>
  23#include <linux/rtc.h>
  24
  25#include <asm/rtc.h>
  26#include <asm/semaphore.h>
  27
  28static DECLARE_WAIT_QUEUE_HEAD(rtc_wait);
  29static struct fasync_struct *rtc_async_queue;
  30
  31/*
  32 * rtc_lock protects rtc_irq_data
  33 */
  34static DEFINE_SPINLOCK(rtc_lock);
  35static unsigned long rtc_irq_data;
  36
  37/*
  38 * rtc_sem protects rtc_inuse and rtc_ops
  39 */
  40static DEFINE_MUTEX(rtc_mutex);
  41static unsigned long rtc_inuse;
  42static struct rtc_ops *rtc_ops;
  43
  44#define rtc_epoch 1900UL
  45
  46/*
  47 * Calculate the next alarm time given the requested alarm time mask
  48 * and the current time.
  49 */
  50void rtc_next_alarm_time(struct rtc_time *next, struct rtc_time *now, struct rtc_time *alrm)
  51{
  52        unsigned long next_time;
  53        unsigned long now_time;
  54
  55        next->tm_year = now->tm_year;
  56        next->tm_mon = now->tm_mon;
  57        next->tm_mday = now->tm_mday;
  58        next->tm_hour = alrm->tm_hour;
  59        next->tm_min = alrm->tm_min;
  60        next->tm_sec = alrm->tm_sec;
  61
  62        rtc_tm_to_time(now, &now_time);
  63        rtc_tm_to_time(next, &next_time);
  64
  65        if (next_time < now_time) {
  66                /* Advance one day */
  67                next_time += 60 * 60 * 24;
  68                rtc_time_to_tm(next_time, next);
  69        }
  70}
  71EXPORT_SYMBOL(rtc_next_alarm_time);
  72
  73static inline int rtc_arm_read_time(struct rtc_ops *ops, struct rtc_time *tm)
  74{
  75        memset(tm, 0, sizeof(struct rtc_time));
  76        return ops->read_time(tm);
  77}
  78
  79static inline int rtc_arm_set_time(struct rtc_ops *ops, struct rtc_time *tm)
  80{
  81        int ret;
  82
  83        ret = rtc_valid_tm(tm);
  84        if (ret == 0)
  85                ret = ops->set_time(tm);
  86
  87        return ret;
  88}
  89
  90static inline int rtc_arm_read_alarm(struct rtc_ops *ops, struct rtc_wkalrm *alrm)
  91{
  92        int ret = -EINVAL;
  93        if (ops->read_alarm) {
  94                memset(alrm, 0, sizeof(struct rtc_wkalrm));
  95                ret = ops->read_alarm(alrm);
  96        }
  97        return ret;
  98}
  99
 100static inline int rtc_arm_set_alarm(struct rtc_ops *ops, struct rtc_wkalrm *alrm)
 101{
 102        int ret = -EINVAL;
 103        if (ops->set_alarm)
 104                ret = ops->set_alarm(alrm);
 105        return ret;
 106}
 107
 108void rtc_update(unsigned long num, unsigned long events)
 109{
 110        spin_lock(&rtc_lock);
 111        rtc_irq_data = (rtc_irq_data + (num << 8)) | events;
 112        spin_unlock(&rtc_lock);
 113
 114        wake_up_interruptible(&rtc_wait);
 115        kill_fasync(&rtc_async_queue, SIGIO, POLL_IN);
 116}
 117EXPORT_SYMBOL(rtc_update);
 118
 119
 120static ssize_t
 121rtc_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
 122{
 123        DECLARE_WAITQUEUE(wait, current);
 124        unsigned long data;
 125        ssize_t ret;
 126
 127        if (count < sizeof(unsigned long))
 128                return -EINVAL;
 129
 130        add_wait_queue(&rtc_wait, &wait);
 131        do {
 132                __set_current_state(TASK_INTERRUPTIBLE);
 133
 134                spin_lock_irq(&rtc_lock);
 135                data = rtc_irq_data;
 136                rtc_irq_data = 0;
 137                spin_unlock_irq(&rtc_lock);
 138
 139                if (data != 0) {
 140                        ret = 0;
 141                        break;
 142                }
 143                if (file->f_flags & O_NONBLOCK) {
 144                        ret = -EAGAIN;
 145                        break;
 146                }
 147                if (signal_pending(current)) {
 148                        ret = -ERESTARTSYS;
 149                        break;
 150                }
 151                schedule();
 152        } while (1);
 153        set_current_state(TASK_RUNNING);
 154        remove_wait_queue(&rtc_wait, &wait);
 155
 156        if (ret == 0) {
 157                ret = put_user(data, (unsigned long __user *)buf);
 158                if (ret == 0)
 159                        ret = sizeof(unsigned long);
 160        }
 161        return ret;
 162}
 163
 164static unsigned int rtc_poll(struct file *file, poll_table *wait)
 165{
 166        unsigned long data;
 167
 168        poll_wait(file, &rtc_wait, wait);
 169
 170        spin_lock_irq(&rtc_lock);
 171        data = rtc_irq_data;
 172        spin_unlock_irq(&rtc_lock);
 173
 174        return data != 0 ? POLLIN | POLLRDNORM : 0;
 175}
 176
 177static int rtc_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
 178                     unsigned long arg)
 179{
 180        struct rtc_ops *ops = file->private_data;
 181        struct rtc_time tm;
 182        struct rtc_wkalrm alrm;
 183        void __user *uarg = (void __user *)arg;
 184        int ret = -EINVAL;
 185
 186        switch (cmd) {
 187        case RTC_ALM_READ:
 188                ret = rtc_arm_read_alarm(ops, &alrm);
 189                if (ret)
 190                        break;
 191                ret = copy_to_user(uarg, &alrm.time, sizeof(tm));
 192                if (ret)
 193                        ret = -EFAULT;
 194                break;
 195
 196        case RTC_ALM_SET:
 197                ret = copy_from_user(&alrm.time, uarg, sizeof(tm));
 198                if (ret) {
 199                        ret = -EFAULT;
 200                        break;
 201                }
 202                alrm.enabled = 0;
 203                alrm.pending = 0;
 204                alrm.time.tm_mday = -1;
 205                alrm.time.tm_mon = -1;
 206                alrm.time.tm_year = -1;
 207                alrm.time.tm_wday = -1;
 208                alrm.time.tm_yday = -1;
 209                alrm.time.tm_isdst = -1;
 210                ret = rtc_arm_set_alarm(ops, &alrm);
 211                break;
 212
 213        case RTC_RD_TIME:
 214                ret = rtc_arm_read_time(ops, &tm);
 215                if (ret)
 216                        break;
 217                ret = copy_to_user(uarg, &tm, sizeof(tm));
 218                if (ret)
 219                        ret = -EFAULT;
 220                break;
 221
 222        case RTC_SET_TIME:
 223                if (!capable(CAP_SYS_TIME)) {
 224                        ret = -EACCES;
 225                        break;
 226                }
 227                ret = copy_from_user(&tm, uarg, sizeof(tm));
 228                if (ret) {
 229                        ret = -EFAULT;
 230                        break;
 231                }
 232                ret = rtc_arm_set_time(ops, &tm);
 233                break;
 234
 235        case RTC_EPOCH_SET:
 236#ifndef rtc_epoch
 237                /*
 238                 * There were no RTC clocks before 1900.
 239                 */
 240                if (arg < 1900) {
 241                        ret = -EINVAL;
 242                        break;
 243                }
 244                if (!capable(CAP_SYS_TIME)) {
 245                        ret = -EACCES;
 246                        break;
 247                }
 248                rtc_epoch = arg;
 249                ret = 0;
 250#endif
 251                break;
 252
 253        case RTC_EPOCH_READ:
 254                ret = put_user(rtc_epoch, (unsigned long __user *)uarg);
 255                break;
 256
 257        case RTC_WKALM_SET:
 258                ret = copy_from_user(&alrm, uarg, sizeof(alrm));
 259                if (ret) {
 260                        ret = -EFAULT;
 261                        break;
 262                }
 263                ret = rtc_arm_set_alarm(ops, &alrm);
 264                break;
 265
 266        case RTC_WKALM_RD:
 267                ret = rtc_arm_read_alarm(ops, &alrm);
 268                if (ret)
 269                        break;
 270                ret = copy_to_user(uarg, &alrm, sizeof(alrm));
 271                if (ret)
 272                        ret = -EFAULT;
 273                break;
 274
 275        default:
 276                if (ops->ioctl)
 277                        ret = ops->ioctl(cmd, arg);
 278                break;
 279        }
 280        return ret;
 281}
 282
 283static int rtc_open(struct inode *inode, struct file *file)
 284{
 285        int ret;
 286
 287        mutex_lock(&rtc_mutex);
 288
 289        if (rtc_inuse) {
 290                ret = -EBUSY;
 291        } else if (!rtc_ops || !try_module_get(rtc_ops->owner)) {
 292                ret = -ENODEV;
 293        } else {
 294                file->private_data = rtc_ops;
 295
 296                ret = rtc_ops->open ? rtc_ops->open() : 0;
 297                if (ret == 0) {
 298                        spin_lock_irq(&rtc_lock);
 299                        rtc_irq_data = 0;
 300                        spin_unlock_irq(&rtc_lock);
 301
 302                        rtc_inuse = 1;
 303                }
 304        }
 305        mutex_unlock(&rtc_mutex);
 306
 307        return ret;
 308}
 309
 310static int rtc_release(struct inode *inode, struct file *file)
 311{
 312        struct rtc_ops *ops = file->private_data;
 313
 314        if (ops->release)
 315                ops->release();
 316
 317        spin_lock_irq(&rtc_lock);
 318        rtc_irq_data = 0;
 319        spin_unlock_irq(&rtc_lock);
 320
 321        module_put(rtc_ops->owner);
 322        rtc_inuse = 0;
 323
 324        return 0;
 325}
 326
 327static int rtc_fasync(int fd, struct file *file, int on)
 328{
 329        return fasync_helper(fd, file, on, &rtc_async_queue);
 330}
 331
 332static const struct file_operations rtc_fops = {
 333        .owner          = THIS_MODULE,
 334        .llseek         = no_llseek,
 335        .read           = rtc_read,
 336        .poll           = rtc_poll,
 337        .ioctl          = rtc_ioctl,
 338        .open           = rtc_open,
 339        .release        = rtc_release,
 340        .fasync         = rtc_fasync,
 341};
 342
 343static struct miscdevice rtc_miscdev = {
 344        .minor          = RTC_MINOR,
 345        .name           = "rtc",
 346        .fops           = &rtc_fops,
 347};
 348
 349
 350static int rtc_read_proc(char *page, char **start, off_t off, int count, int *eof, void *data)
 351{
 352        struct rtc_ops *ops = data;
 353        struct rtc_wkalrm alrm;
 354        struct rtc_time tm;
 355        char *p = page;
 356
 357        if (rtc_arm_read_time(ops, &tm) == 0) {
 358                p += sprintf(p,
 359                        "rtc_time\t: %02d:%02d:%02d\n"
 360                        "rtc_date\t: %04d-%02d-%02d\n"
 361                        "rtc_epoch\t: %04lu\n",
 362                        tm.tm_hour, tm.tm_min, tm.tm_sec,
 363                        tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
 364                        rtc_epoch);
 365        }
 366
 367        if (rtc_arm_read_alarm(ops, &alrm) == 0) {
 368                p += sprintf(p, "alrm_time\t: ");
 369                if ((unsigned int)alrm.time.tm_hour <= 24)
 370                        p += sprintf(p, "%02d:", alrm.time.tm_hour);
 371                else
 372                        p += sprintf(p, "**:");
 373                if ((unsigned int)alrm.time.tm_min <= 59)
 374                        p += sprintf(p, "%02d:", alrm.time.tm_min);
 375                else
 376                        p += sprintf(p, "**:");
 377                if ((unsigned int)alrm.time.tm_sec <= 59)
 378                        p += sprintf(p, "%02d\n", alrm.time.tm_sec);
 379                else
 380                        p += sprintf(p, "**\n");
 381
 382                p += sprintf(p, "alrm_date\t: ");
 383                if ((unsigned int)alrm.time.tm_year <= 200)
 384                        p += sprintf(p, "%04d-", alrm.time.tm_year + 1900);
 385                else
 386                        p += sprintf(p, "****-");
 387                if ((unsigned int)alrm.time.tm_mon <= 11)
 388                        p += sprintf(p, "%02d-", alrm.time.tm_mon + 1);
 389                else
 390                        p += sprintf(p, "**-");
 391                if ((unsigned int)alrm.time.tm_mday <= 31)
 392                        p += sprintf(p, "%02d\n", alrm.time.tm_mday);
 393                else
 394                        p += sprintf(p, "**\n");
 395                p += sprintf(p, "alrm_wakeup\t: %s\n",
 396                             alrm.enabled ? "yes" : "no");
 397                p += sprintf(p, "alrm_pending\t: %s\n",
 398                             alrm.pending ? "yes" : "no");
 399        }
 400
 401        if (ops->proc)
 402                p += ops->proc(p);
 403
 404        return p - page;
 405}
 406
 407int register_rtc(struct rtc_ops *ops)
 408{
 409        int ret = -EBUSY;
 410
 411        mutex_lock(&rtc_mutex);
 412        if (rtc_ops == NULL) {
 413                rtc_ops = ops;
 414
 415                ret = misc_register(&rtc_miscdev);
 416                if (ret == 0)
 417                        create_proc_read_entry("driver/rtc", 0, NULL,
 418                                               rtc_read_proc, ops);
 419        }
 420        mutex_unlock(&rtc_mutex);
 421
 422        return ret;
 423}
 424EXPORT_SYMBOL(register_rtc);
 425
 426void unregister_rtc(struct rtc_ops *rtc)
 427{
 428        mutex_lock(&rtc_mutex);
 429        if (rtc == rtc_ops) {
 430                remove_proc_entry("driver/rtc", NULL);
 431                misc_deregister(&rtc_miscdev);
 432                rtc_ops = NULL;
 433        }
 434        mutex_unlock(&rtc_mutex);
 435}
 436EXPORT_SYMBOL(unregister_rtc);
 437