linux/drivers/char/genrtc.c
<<
>>
Prefs
   1/*
   2 *      Real Time Clock interface for
   3 *              - q40 and other m68k machines,
   4 *              - HP PARISC machines
   5 *              - PowerPC machines
   6 *      emulate some RTC irq capabilities in software
   7 *
   8 *      Copyright (C) 1999 Richard Zidlicky
   9 *
  10 *      based on Paul Gortmaker's rtc.c device and
  11 *           Sam Creasey Generic rtc driver
  12 *
  13 *      This driver allows use of the real time clock (built into
  14 *      nearly all computers) from user space. It exports the /dev/rtc
  15 *      interface supporting various ioctl() and also the /proc/driver/rtc
  16 *      pseudo-file for status information.
  17 *
  18 *      The ioctls can be used to set the interrupt behaviour where
  19 *      supported.
  20 *
  21 *      The /dev/rtc interface will block on reads until an interrupt
  22 *      has been received. If a RTC interrupt has already happened,
  23 *      it will output an unsigned long and then block. The output value
  24 *      contains the interrupt status in the low byte and the number of
  25 *      interrupts since the last read in the remaining high bytes. The
  26 *      /dev/rtc interface can also be used with the select(2) call.
  27 *
  28 *      This program is free software; you can redistribute it and/or
  29 *      modify it under the terms of the GNU General Public License
  30 *      as published by the Free Software Foundation; either version
  31 *      2 of the License, or (at your option) any later version.
  32 *
  33
  34 *      1.01 fix for 2.3.X                    rz@linux-m68k.org
  35 *      1.02 merged with code from genrtc.c   rz@linux-m68k.org
  36 *      1.03 make it more portable            zippel@linux-m68k.org
  37 *      1.04 removed useless timer code       rz@linux-m68k.org
  38 *      1.05 portable RTC_UIE emulation       rz@linux-m68k.org
  39 *      1.06 set_rtc_time can return an error trini@kernel.crashing.org
  40 *      1.07 ported to HP PARISC (hppa)       Helge Deller <deller@gmx.de>
  41 */
  42
  43#define RTC_VERSION     "1.07"
  44
  45#include <linux/module.h>
  46#include <linux/sched.h>
  47#include <linux/errno.h>
  48#include <linux/miscdevice.h>
  49#include <linux/fcntl.h>
  50
  51#include <linux/rtc.h>
  52#include <linux/init.h>
  53#include <linux/poll.h>
  54#include <linux/proc_fs.h>
  55#include <linux/seq_file.h>
  56#include <linux/mutex.h>
  57#include <linux/workqueue.h>
  58
  59#include <asm/uaccess.h>
  60#include <asm/rtc.h>
  61
  62/*
  63 *      We sponge a minor off of the misc major. No need slurping
  64 *      up another valuable major dev number for this. If you add
  65 *      an ioctl, make sure you don't conflict with SPARC's RTC
  66 *      ioctls.
  67 */
  68
  69static DEFINE_MUTEX(gen_rtc_mutex);
  70static DECLARE_WAIT_QUEUE_HEAD(gen_rtc_wait);
  71
  72/*
  73 *      Bits in gen_rtc_status.
  74 */
  75
  76#define RTC_IS_OPEN             0x01    /* means /dev/rtc is in use     */
  77
  78static unsigned char gen_rtc_status;    /* bitmapped status byte.       */
  79static unsigned long gen_rtc_irq_data;  /* our output to the world      */
  80
  81/* months start at 0 now */
  82static unsigned char days_in_mo[] =
  83{31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
  84
  85static int irq_active;
  86
  87#ifdef CONFIG_GEN_RTC_X
  88static struct work_struct genrtc_task;
  89static struct timer_list timer_task;
  90
  91static unsigned int oldsecs;
  92static int lostint;
  93static unsigned long tt_exp;
  94
  95static void gen_rtc_timer(unsigned long data);
  96
  97static volatile int stask_active;              /* schedule_work */
  98static volatile int ttask_active;              /* timer_task */
  99static int stop_rtc_timers;                    /* don't requeue tasks */
 100static DEFINE_SPINLOCK(gen_rtc_lock);
 101
 102static void gen_rtc_interrupt(unsigned long arg);
 103
 104/*
 105 * Routine to poll RTC seconds field for change as often as possible,
 106 * after first RTC_UIE use timer to reduce polling
 107 */
 108static void genrtc_troutine(struct work_struct *work)
 109{
 110        unsigned int tmp = get_rtc_ss();
 111        
 112        if (stop_rtc_timers) {
 113                stask_active = 0;
 114                return;
 115        }
 116
 117        if (oldsecs != tmp){
 118                oldsecs = tmp;
 119
 120                timer_task.function = gen_rtc_timer;
 121                timer_task.expires = jiffies + HZ - (HZ/10);
 122                tt_exp=timer_task.expires;
 123                ttask_active=1;
 124                stask_active=0;
 125                add_timer(&timer_task);
 126
 127                gen_rtc_interrupt(0);
 128        } else if (schedule_work(&genrtc_task) == 0)
 129                stask_active = 0;
 130}
 131
 132static void gen_rtc_timer(unsigned long data)
 133{
 134        lostint = get_rtc_ss() - oldsecs ;
 135        if (lostint<0) 
 136                lostint = 60 - lostint;
 137        if (time_after(jiffies, tt_exp))
 138                printk(KERN_INFO "genrtc: timer task delayed by %ld jiffies\n",
 139                       jiffies-tt_exp);
 140        ttask_active=0;
 141        stask_active=1;
 142        if ((schedule_work(&genrtc_task) == 0))
 143                stask_active = 0;
 144}
 145
 146/* 
 147 * call gen_rtc_interrupt function to signal an RTC_UIE,
 148 * arg is unused.
 149 * Could be invoked either from a real interrupt handler or
 150 * from some routine that periodically (eg 100HZ) monitors
 151 * whether RTC_SECS changed
 152 */
 153static void gen_rtc_interrupt(unsigned long arg)
 154{
 155        /*  We store the status in the low byte and the number of
 156         *      interrupts received since the last read in the remainder
 157         *      of rtc_irq_data.  */
 158
 159        gen_rtc_irq_data += 0x100;
 160        gen_rtc_irq_data &= ~0xff;
 161        gen_rtc_irq_data |= RTC_UIE;
 162
 163        if (lostint){
 164                printk("genrtc: system delaying clock ticks?\n");
 165                /* increment count so that userspace knows something is wrong */
 166                gen_rtc_irq_data += ((lostint-1)<<8);
 167                lostint = 0;
 168        }
 169
 170        wake_up_interruptible(&gen_rtc_wait);
 171}
 172
 173/*
 174 *      Now all the various file operations that we export.
 175 */
 176static ssize_t gen_rtc_read(struct file *file, char __user *buf,
 177                        size_t count, loff_t *ppos)
 178{
 179        unsigned long data;
 180        ssize_t retval;
 181
 182        if (count != sizeof (unsigned int) && count != sizeof (unsigned long))
 183                return -EINVAL;
 184
 185        if (file->f_flags & O_NONBLOCK && !gen_rtc_irq_data)
 186                return -EAGAIN;
 187
 188        retval = wait_event_interruptible(gen_rtc_wait,
 189                        (data = xchg(&gen_rtc_irq_data, 0)));
 190        if (retval)
 191                goto out;
 192
 193        /* first test allows optimizer to nuke this case for 32-bit machines */
 194        if (sizeof (int) != sizeof (long) && count == sizeof (unsigned int)) {
 195                unsigned int uidata = data;
 196                retval = put_user(uidata, (unsigned int __user *)buf) ?:
 197                        sizeof(unsigned int);
 198        }
 199        else {
 200                retval = put_user(data, (unsigned long __user *)buf) ?:
 201                        sizeof(unsigned long);
 202        }
 203out:
 204        return retval;
 205}
 206
 207static unsigned int gen_rtc_poll(struct file *file,
 208                                 struct poll_table_struct *wait)
 209{
 210        poll_wait(file, &gen_rtc_wait, wait);
 211        if (gen_rtc_irq_data != 0)
 212                return POLLIN | POLLRDNORM;
 213        return 0;
 214}
 215
 216#endif
 217
 218/*
 219 * Used to disable/enable interrupts, only RTC_UIE supported
 220 * We also clear out any old irq data after an ioctl() that
 221 * meddles with the interrupt enable/disable bits.
 222 */
 223
 224static inline void gen_clear_rtc_irq_bit(unsigned char bit)
 225{
 226#ifdef CONFIG_GEN_RTC_X
 227        stop_rtc_timers = 1;
 228        if (ttask_active){
 229                del_timer_sync(&timer_task);
 230                ttask_active = 0;
 231        }
 232        while (stask_active)
 233                schedule();
 234
 235        spin_lock(&gen_rtc_lock);
 236        irq_active = 0;
 237        spin_unlock(&gen_rtc_lock);
 238#endif
 239}
 240
 241static inline int gen_set_rtc_irq_bit(unsigned char bit)
 242{
 243#ifdef CONFIG_GEN_RTC_X
 244        spin_lock(&gen_rtc_lock);
 245        if ( !irq_active ) {
 246                irq_active = 1;
 247                stop_rtc_timers = 0;
 248                lostint = 0;
 249                INIT_WORK(&genrtc_task, genrtc_troutine);
 250                oldsecs = get_rtc_ss();
 251                init_timer(&timer_task);
 252
 253                stask_active = 1;
 254                if (schedule_work(&genrtc_task) == 0){
 255                        stask_active = 0;
 256                }
 257        }
 258        spin_unlock(&gen_rtc_lock);
 259        gen_rtc_irq_data = 0;
 260        return 0;
 261#else
 262        return -EINVAL;
 263#endif
 264}
 265
 266static int gen_rtc_ioctl(struct file *file,
 267                         unsigned int cmd, unsigned long arg)
 268{
 269        struct rtc_time wtime;
 270        struct rtc_pll_info pll;
 271        void __user *argp = (void __user *)arg;
 272
 273        switch (cmd) {
 274
 275        case RTC_PLL_GET:
 276            if (get_rtc_pll(&pll))
 277                    return -EINVAL;
 278            else
 279                    return copy_to_user(argp, &pll, sizeof pll) ? -EFAULT : 0;
 280
 281        case RTC_PLL_SET:
 282                if (!capable(CAP_SYS_TIME))
 283                        return -EACCES;
 284                if (copy_from_user(&pll, argp, sizeof(pll)))
 285                        return -EFAULT;
 286            return set_rtc_pll(&pll);
 287
 288        case RTC_UIE_OFF:       /* disable ints from RTC updates.       */
 289                gen_clear_rtc_irq_bit(RTC_UIE);
 290                return 0;
 291
 292        case RTC_UIE_ON:        /* enable ints for RTC updates. */
 293                return gen_set_rtc_irq_bit(RTC_UIE);
 294
 295        case RTC_RD_TIME:       /* Read the time/date from RTC  */
 296                /* this doesn't get week-day, who cares */
 297                memset(&wtime, 0, sizeof(wtime));
 298                get_rtc_time(&wtime);
 299
 300                return copy_to_user(argp, &wtime, sizeof(wtime)) ? -EFAULT : 0;
 301
 302        case RTC_SET_TIME:      /* Set the RTC */
 303            {
 304                int year;
 305                unsigned char leap_yr;
 306
 307                if (!capable(CAP_SYS_TIME))
 308                        return -EACCES;
 309
 310                if (copy_from_user(&wtime, argp, sizeof(wtime)))
 311                        return -EFAULT;
 312
 313                year = wtime.tm_year + 1900;
 314                leap_yr = ((!(year % 4) && (year % 100)) ||
 315                           !(year % 400));
 316
 317                if ((wtime.tm_mon < 0 || wtime.tm_mon > 11) || (wtime.tm_mday < 1))
 318                        return -EINVAL;
 319
 320                if (wtime.tm_mday < 0 || wtime.tm_mday >
 321                    (days_in_mo[wtime.tm_mon] + ((wtime.tm_mon == 1) && leap_yr)))
 322                        return -EINVAL;
 323
 324                if (wtime.tm_hour < 0 || wtime.tm_hour >= 24 ||
 325                    wtime.tm_min < 0 || wtime.tm_min >= 60 ||
 326                    wtime.tm_sec < 0 || wtime.tm_sec >= 60)
 327                        return -EINVAL;
 328
 329                return set_rtc_time(&wtime);
 330            }
 331        }
 332
 333        return -EINVAL;
 334}
 335
 336static long gen_rtc_unlocked_ioctl(struct file *file, unsigned int cmd,
 337                                   unsigned long arg)
 338{
 339        int ret;
 340
 341        mutex_lock(&gen_rtc_mutex);
 342        ret = gen_rtc_ioctl(file, cmd, arg);
 343        mutex_unlock(&gen_rtc_mutex);
 344
 345        return ret;
 346}
 347
 348/*
 349 *      We enforce only one user at a time here with the open/close.
 350 *      Also clear the previous interrupt data on an open, and clean
 351 *      up things on a close.
 352 */
 353
 354static int gen_rtc_open(struct inode *inode, struct file *file)
 355{
 356        mutex_lock(&gen_rtc_mutex);
 357        if (gen_rtc_status & RTC_IS_OPEN) {
 358                mutex_unlock(&gen_rtc_mutex);
 359                return -EBUSY;
 360        }
 361
 362        gen_rtc_status |= RTC_IS_OPEN;
 363        gen_rtc_irq_data = 0;
 364        irq_active = 0;
 365        mutex_unlock(&gen_rtc_mutex);
 366
 367        return 0;
 368}
 369
 370static int gen_rtc_release(struct inode *inode, struct file *file)
 371{
 372        /*
 373         * Turn off all interrupts once the device is no longer
 374         * in use and clear the data.
 375         */
 376
 377        gen_clear_rtc_irq_bit(RTC_PIE|RTC_AIE|RTC_UIE);
 378
 379        gen_rtc_status &= ~RTC_IS_OPEN;
 380        return 0;
 381}
 382
 383
 384#ifdef CONFIG_PROC_FS
 385
 386/*
 387 *      Info exported via "/proc/driver/rtc".
 388 */
 389
 390static int gen_rtc_proc_show(struct seq_file *m, void *v)
 391{
 392        struct rtc_time tm;
 393        unsigned int flags;
 394        struct rtc_pll_info pll;
 395
 396        flags = get_rtc_time(&tm);
 397
 398        seq_printf(m,
 399                     "rtc_time\t: %02d:%02d:%02d\n"
 400                     "rtc_date\t: %04d-%02d-%02d\n"
 401                     "rtc_epoch\t: %04u\n",
 402                     tm.tm_hour, tm.tm_min, tm.tm_sec,
 403                     tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, 1900);
 404
 405        tm.tm_hour = tm.tm_min = tm.tm_sec = 0;
 406
 407        seq_puts(m, "alarm\t\t: ");
 408        if (tm.tm_hour <= 24)
 409                seq_printf(m, "%02d:", tm.tm_hour);
 410        else
 411                seq_puts(m, "**:");
 412
 413        if (tm.tm_min <= 59)
 414                seq_printf(m, "%02d:", tm.tm_min);
 415        else
 416                seq_puts(m, "**:");
 417
 418        if (tm.tm_sec <= 59)
 419                seq_printf(m, "%02d\n", tm.tm_sec);
 420        else
 421                seq_puts(m, "**\n");
 422
 423        seq_printf(m,
 424                     "DST_enable\t: %s\n"
 425                     "BCD\t\t: %s\n"
 426                     "24hr\t\t: %s\n"
 427                     "square_wave\t: %s\n"
 428                     "alarm_IRQ\t: %s\n"
 429                     "update_IRQ\t: %s\n"
 430                     "periodic_IRQ\t: %s\n"
 431                     "periodic_freq\t: %ld\n"
 432                     "batt_status\t: %s\n",
 433                     (flags & RTC_DST_EN) ? "yes" : "no",
 434                     (flags & RTC_DM_BINARY) ? "no" : "yes",
 435                     (flags & RTC_24H) ? "yes" : "no",
 436                     (flags & RTC_SQWE) ? "yes" : "no",
 437                     (flags & RTC_AIE) ? "yes" : "no",
 438                     irq_active ? "yes" : "no",
 439                     (flags & RTC_PIE) ? "yes" : "no",
 440                     0L /* freq */,
 441                     (flags & RTC_BATT_BAD) ? "bad" : "okay");
 442        if (!get_rtc_pll(&pll))
 443            seq_printf(m,
 444                         "PLL adjustment\t: %d\n"
 445                         "PLL max +ve adjustment\t: %d\n"
 446                         "PLL max -ve adjustment\t: %d\n"
 447                         "PLL +ve adjustment factor\t: %d\n"
 448                         "PLL -ve adjustment factor\t: %d\n"
 449                         "PLL frequency\t: %ld\n",
 450                         pll.pll_value,
 451                         pll.pll_max,
 452                         pll.pll_min,
 453                         pll.pll_posmult,
 454                         pll.pll_negmult,
 455                         pll.pll_clock);
 456        return 0;
 457}
 458
 459static int gen_rtc_proc_open(struct inode *inode, struct file *file)
 460{
 461        return single_open(file, gen_rtc_proc_show, NULL);
 462}
 463
 464static const struct file_operations gen_rtc_proc_fops = {
 465        .open           = gen_rtc_proc_open,
 466        .read           = seq_read,
 467        .llseek         = seq_lseek,
 468        .release        = single_release,
 469};
 470
 471static int __init gen_rtc_proc_init(void)
 472{
 473        struct proc_dir_entry *r;
 474
 475        r = proc_create("driver/rtc", 0, NULL, &gen_rtc_proc_fops);
 476        if (!r)
 477                return -ENOMEM;
 478        return 0;
 479}
 480#else
 481static inline int gen_rtc_proc_init(void) { return 0; }
 482#endif /* CONFIG_PROC_FS */
 483
 484
 485/*
 486 *      The various file operations we support.
 487 */
 488
 489static const struct file_operations gen_rtc_fops = {
 490        .owner          = THIS_MODULE,
 491#ifdef CONFIG_GEN_RTC_X
 492        .read           = gen_rtc_read,
 493        .poll           = gen_rtc_poll,
 494#endif
 495        .unlocked_ioctl = gen_rtc_unlocked_ioctl,
 496        .open           = gen_rtc_open,
 497        .release        = gen_rtc_release,
 498        .llseek         = noop_llseek,
 499};
 500
 501static struct miscdevice rtc_gen_dev =
 502{
 503        .minor          = RTC_MINOR,
 504        .name           = "rtc",
 505        .fops           = &gen_rtc_fops,
 506};
 507
 508static int __init rtc_generic_init(void)
 509{
 510        int retval;
 511
 512        printk(KERN_INFO "Generic RTC Driver v%s\n", RTC_VERSION);
 513
 514        retval = misc_register(&rtc_gen_dev);
 515        if (retval < 0)
 516                return retval;
 517
 518        retval = gen_rtc_proc_init();
 519        if (retval) {
 520                misc_deregister(&rtc_gen_dev);
 521                return retval;
 522        }
 523
 524        return 0;
 525}
 526
 527static void __exit rtc_generic_exit(void)
 528{
 529        remove_proc_entry ("driver/rtc", NULL);
 530        misc_deregister(&rtc_gen_dev);
 531}
 532
 533
 534module_init(rtc_generic_init);
 535module_exit(rtc_generic_exit);
 536
 537MODULE_AUTHOR("Richard Zidlicky");
 538MODULE_LICENSE("GPL");
 539MODULE_ALIAS_MISCDEV(RTC_MINOR);
 540