linux-old/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/dev/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/config.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
  56#include <asm/uaccess.h>
  57#include <asm/system.h>
  58#include <asm/rtc.h>
  59
  60/*
  61 *      We sponge a minor off of the misc major. No need slurping
  62 *      up another valuable major dev number for this. If you add
  63 *      an ioctl, make sure you don't conflict with SPARC's RTC
  64 *      ioctls.
  65 */
  66
  67static DECLARE_WAIT_QUEUE_HEAD(gen_rtc_wait);
  68
  69/*
  70 *      Bits in gen_rtc_status.
  71 */
  72
  73#define RTC_IS_OPEN             0x01    /* means /dev/rtc is in use     */
  74
  75static unsigned char gen_rtc_status;    /* bitmapped status byte.       */
  76static unsigned long gen_rtc_irq_data;  /* our output to the world      */
  77
  78/* months start at 0 now */
  79static unsigned char days_in_mo[] =
  80{31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
  81
  82static int irq_active;
  83
  84#ifdef CONFIG_GEN_RTC_X
  85struct tq_struct genrtc_task;
  86static struct timer_list timer_task;
  87
  88static unsigned int oldsecs;
  89static int lostint;
  90static int tt_exp;
  91
  92static void gen_rtc_timer(unsigned long data);
  93
  94static volatile int stask_active;              /* schedule_task */
  95static volatile int ttask_active;              /* timer_task */
  96static int stop_rtc_timers;                    /* don't requeue tasks */
  97static spinlock_t gen_rtc_lock = SPIN_LOCK_UNLOCKED;
  98
  99static void gen_rtc_interrupt(unsigned long arg);
 100
 101/*
 102 * Routine to poll RTC seconds field for change as often as possible,
 103 * after first RTC_UIE use timer to reduce polling
 104 */
 105static void genrtc_troutine(void *data)
 106{
 107        unsigned int tmp = get_rtc_ss();
 108        
 109        if (stop_rtc_timers) {
 110                stask_active = 0;
 111                return;
 112        }
 113
 114        if (oldsecs != tmp){
 115                oldsecs = tmp;
 116
 117                timer_task.function = gen_rtc_timer;
 118                timer_task.expires = jiffies + HZ - (HZ/10);
 119                tt_exp=timer_task.expires;
 120                ttask_active=1;
 121                stask_active=0;
 122                add_timer(&timer_task);
 123
 124                gen_rtc_interrupt(0);
 125        } else if (schedule_task(&genrtc_task) == 0)
 126                stask_active = 0;
 127}
 128
 129static void gen_rtc_timer(unsigned long data)
 130{
 131        lostint = get_rtc_ss() - oldsecs ;
 132        if (lostint<0) 
 133                lostint = 60 - lostint;
 134        if (time_after(jiffies, tt_exp))
 135                printk(KERN_INFO "genrtc: timer task delayed by %ld jiffies\n",
 136                       jiffies-tt_exp);
 137        ttask_active=0;
 138        stask_active=1;
 139        if ((schedule_task(&genrtc_task) == 0))
 140                stask_active = 0;
 141}
 142
 143/* 
 144 * call gen_rtc_interrupt function to signal an RTC_UIE,
 145 * arg is unused.
 146 * Could be invoked either from a real interrupt handler or
 147 * from some routine that periodically (eg 100HZ) monitors
 148 * whether RTC_SECS changed
 149 */
 150static void gen_rtc_interrupt(unsigned long arg)
 151{
 152        /*  We store the status in the low byte and the number of
 153         *      interrupts received since the last read in the remainder
 154         *      of rtc_irq_data.  */
 155
 156        gen_rtc_irq_data += 0x100;
 157        gen_rtc_irq_data &= ~0xff;
 158        gen_rtc_irq_data |= RTC_UIE;
 159
 160        if (lostint){
 161                printk("genrtc: system delaying clock ticks?\n");
 162                /* increment count so that userspace knows something is wrong */
 163                gen_rtc_irq_data += ((lostint-1)<<8);
 164                lostint = 0;
 165        }
 166
 167        wake_up_interruptible(&gen_rtc_wait);
 168}
 169
 170/*
 171 *      Now all the various file operations that we export.
 172 */
 173static ssize_t gen_rtc_read(struct file *file, char *buf,
 174                        size_t count, loff_t *ppos)
 175{
 176        DECLARE_WAITQUEUE(wait, current);
 177        unsigned long data;
 178        ssize_t retval;
 179
 180        if (count != sizeof (unsigned int) && count != sizeof (unsigned long))
 181                return -EINVAL;
 182
 183        if (file->f_flags & O_NONBLOCK && !gen_rtc_irq_data)
 184                return -EAGAIN;
 185
 186        add_wait_queue(&gen_rtc_wait, &wait);
 187        retval = -ERESTARTSYS;
 188
 189        while (1) {
 190                set_current_state(TASK_INTERRUPTIBLE);
 191                data = xchg(&gen_rtc_irq_data, 0);
 192                if (data)
 193                        break;
 194                if (signal_pending(current))
 195                        goto out;
 196                schedule();
 197        }
 198
 199        /* first test allows optimizer to nuke this case for 32-bit machines */
 200        if (sizeof (int) != sizeof (long) && count == sizeof (unsigned int)) {
 201                unsigned int uidata = data;
 202                retval = put_user(uidata, (unsigned long *)buf);
 203        }
 204        else {
 205                retval = put_user(data, (unsigned long *)buf);
 206        }
 207        if (!retval)
 208                retval = sizeof(unsigned long);
 209 out:
 210        current->state = TASK_RUNNING;
 211        remove_wait_queue(&gen_rtc_wait, &wait);
 212
 213        return retval;
 214}
 215
 216static unsigned int gen_rtc_poll(struct file *file,
 217                                 struct poll_table_struct *wait)
 218{
 219        poll_wait(file, &gen_rtc_wait, wait);
 220        if (gen_rtc_irq_data != 0)
 221                return POLLIN | POLLRDNORM;
 222        return 0;
 223}
 224
 225#endif
 226
 227/*
 228 * Used to disable/enable interrupts, only RTC_UIE supported
 229 * We also clear out any old irq data after an ioctl() that
 230 * meddles with the interrupt enable/disable bits.
 231 */
 232
 233static inline void gen_clear_rtc_irq_bit(unsigned char bit)
 234{
 235#ifdef CONFIG_GEN_RTC_X
 236        stop_rtc_timers = 1;
 237        if (ttask_active){
 238                del_timer_sync(&timer_task);
 239                ttask_active = 0;
 240        }
 241        while (stask_active)
 242                schedule();
 243
 244        spin_lock(&gen_rtc_lock);
 245        irq_active = 0;
 246        spin_unlock(&gen_rtc_lock);
 247#endif
 248}
 249
 250static inline int gen_set_rtc_irq_bit(unsigned char bit)
 251{
 252#ifdef CONFIG_GEN_RTC_X
 253        spin_lock(&gen_rtc_lock);
 254        if ( !irq_active ) {
 255                irq_active = 1;
 256                stop_rtc_timers = 0;
 257                lostint = 0;
 258                genrtc_task.routine = genrtc_troutine;
 259                oldsecs = get_rtc_ss();
 260                init_timer(&timer_task);
 261
 262                stask_active = 1;
 263                if (schedule_task(&genrtc_task) == 0){
 264                        stask_active = 0;
 265                }
 266        }
 267        spin_unlock(&gen_rtc_lock);
 268        gen_rtc_irq_data = 0;
 269        return 0;
 270#else
 271        return -EINVAL;
 272#endif
 273}
 274
 275static int gen_rtc_ioctl(struct inode *inode, struct file *file,
 276                         unsigned int cmd, unsigned long arg)
 277{
 278        struct rtc_time wtime;
 279        struct rtc_pll_info pll;
 280
 281        switch (cmd) {
 282
 283        case RTC_PLL_GET:
 284            if (get_rtc_pll(&pll))
 285                    return -EINVAL;
 286            else
 287                    return copy_to_user((void *)arg, &pll, sizeof pll) ? -EFAULT : 0;
 288
 289        case RTC_PLL_SET:
 290                if (!capable(CAP_SYS_TIME))
 291                        return -EACCES;
 292                if (copy_from_user(&pll, (struct rtc_pll_info*)arg,
 293                                   sizeof(pll)))
 294                        return -EFAULT;
 295            return set_rtc_pll(&pll);
 296
 297        case RTC_UIE_OFF:       /* disable ints from RTC updates.       */
 298                gen_clear_rtc_irq_bit(RTC_UIE);
 299                return 0;
 300
 301        case RTC_UIE_ON:        /* enable ints for RTC updates. */
 302                return gen_set_rtc_irq_bit(RTC_UIE);
 303
 304        case RTC_RD_TIME:       /* Read the time/date from RTC  */
 305                /* this doesn't get week-day, who cares */
 306                memset(&wtime, 0, sizeof(wtime));
 307                get_rtc_time(&wtime);
 308
 309                return copy_to_user((void *)arg, &wtime, sizeof(wtime)) ? -EFAULT : 0;
 310
 311        case RTC_SET_TIME:      /* Set the RTC */
 312            {
 313                int year;
 314                unsigned char leap_yr;
 315
 316                if (!capable(CAP_SYS_TIME))
 317                        return -EACCES;
 318
 319                if (copy_from_user(&wtime, (struct rtc_time *)arg,
 320                                   sizeof(wtime)))
 321                        return -EFAULT;
 322
 323                year = wtime.tm_year + 1900;
 324                leap_yr = ((!(year % 4) && (year % 100)) ||
 325                           !(year % 400));
 326
 327                if ((wtime.tm_mon < 0 || wtime.tm_mon > 11) || (wtime.tm_mday < 1))
 328                        return -EINVAL;
 329
 330                if (wtime.tm_mday < 0 || wtime.tm_mday >
 331                    (days_in_mo[wtime.tm_mon] + ((wtime.tm_mon == 1) && leap_yr)))
 332                        return -EINVAL;
 333
 334                if (wtime.tm_hour < 0 || wtime.tm_hour >= 24 ||
 335                    wtime.tm_min < 0 || wtime.tm_min >= 60 ||
 336                    wtime.tm_sec < 0 || wtime.tm_sec >= 60)
 337                        return -EINVAL;
 338
 339                return set_rtc_time(&wtime);
 340            }
 341        }
 342
 343        return -EINVAL;
 344}
 345
 346/*
 347 *      We enforce only one user at a time here with the open/close.
 348 *      Also clear the previous interrupt data on an open, and clean
 349 *      up things on a close.
 350 */
 351
 352static int gen_rtc_open(struct inode *inode, struct file *file)
 353{
 354        if (gen_rtc_status & RTC_IS_OPEN)
 355                return -EBUSY;
 356
 357        MOD_INC_USE_COUNT;
 358
 359        gen_rtc_status |= RTC_IS_OPEN;
 360        gen_rtc_irq_data = 0;
 361        irq_active = 0;
 362
 363        return 0;
 364}
 365
 366static int gen_rtc_release(struct inode *inode, struct file *file)
 367{
 368        /*
 369         * Turn off all interrupts once the device is no longer
 370         * in use and clear the data.
 371         */
 372
 373        gen_clear_rtc_irq_bit(RTC_PIE|RTC_AIE|RTC_UIE);
 374
 375        gen_rtc_status &= ~RTC_IS_OPEN;
 376        MOD_DEC_USE_COUNT;
 377
 378        return 0;
 379}
 380
 381
 382#ifdef CONFIG_PROC_FS
 383
 384/*
 385 *      Info exported via "/proc/rtc".
 386 */
 387
 388static int gen_rtc_proc_output(char *buf)
 389{
 390        char *p;
 391        struct rtc_time tm;
 392        unsigned int flags;
 393        struct rtc_pll_info pll;
 394
 395        p = buf;
 396
 397        flags = get_rtc_time(&tm);
 398
 399        p += sprintf(p,
 400                     "rtc_time\t: %02d:%02d:%02d\n"
 401                     "rtc_date\t: %04d-%02d-%02d\n"
 402                     "rtc_epoch\t: %04u\n",
 403                     tm.tm_hour, tm.tm_min, tm.tm_sec,
 404                     tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, 1900);
 405
 406        tm.tm_hour = tm.tm_min = tm.tm_sec = 0;
 407
 408        p += sprintf(p, "alarm\t\t: ");
 409        if (tm.tm_hour <= 24)
 410                p += sprintf(p, "%02d:", tm.tm_hour);
 411        else
 412                p += sprintf(p, "**:");
 413
 414        if (tm.tm_min <= 59)
 415                p += sprintf(p, "%02d:", tm.tm_min);
 416        else
 417                p += sprintf(p, "**:");
 418
 419        if (tm.tm_sec <= 59)
 420                p += sprintf(p, "%02d\n", tm.tm_sec);
 421        else
 422                p += sprintf(p, "**\n");
 423
 424        p += sprintf(p,
 425                     "DST_enable\t: %s\n"
 426                     "BCD\t\t: %s\n"
 427                     "24hr\t\t: %s\n"
 428                     "square_wave\t: %s\n"
 429                     "alarm_IRQ\t: %s\n"
 430                     "update_IRQ\t: %s\n"
 431                     "periodic_IRQ\t: %s\n"
 432                     "periodic_freq\t: %ld\n"
 433                     "batt_status\t: %s\n",
 434                     (flags & RTC_DST_EN) ? "yes" : "no",
 435                     (flags & RTC_DM_BINARY) ? "no" : "yes",
 436                     (flags & RTC_24H) ? "yes" : "no",
 437                     (flags & RTC_SQWE) ? "yes" : "no",
 438                     (flags & RTC_AIE) ? "yes" : "no",
 439                     irq_active ? "yes" : "no",
 440                     (flags & RTC_PIE) ? "yes" : "no",
 441                     0L /* freq */,
 442                     (flags & RTC_BATT_BAD) ? "bad" : "okay");
 443        if (!get_rtc_pll(&pll))
 444            p += sprintf(p,
 445                         "PLL adjustment\t: %d\n"
 446                         "PLL max +ve adjustment\t: %d\n"
 447                         "PLL max -ve adjustment\t: %d\n"
 448                         "PLL +ve adjustment factor\t: %d\n"
 449                         "PLL -ve adjustment factor\t: %d\n"
 450                         "PLL frequency\t: %ld\n",
 451                         pll.pll_value,
 452                         pll.pll_max,
 453                         pll.pll_min,
 454                         pll.pll_posmult,
 455                         pll.pll_negmult,
 456                         pll.pll_clock);
 457        return p - buf;
 458}
 459
 460static int gen_rtc_read_proc(char *page, char **start, off_t off,
 461                             int count, int *eof, void *data)
 462{
 463        int len = gen_rtc_proc_output (page);
 464        if (len <= off+count) *eof = 1;
 465        *start = page + off;
 466        len -= off;
 467        if (len>count) len = count;
 468        if (len<0) len = 0;
 469        return len;
 470}
 471
 472#endif /* CONFIG_PROC_FS */
 473
 474
 475/*
 476 *      The various file operations we support.
 477 */
 478
 479static struct file_operations gen_rtc_fops = {
 480        .owner          = THIS_MODULE,
 481#ifdef CONFIG_GEN_RTC_X
 482        .read           = gen_rtc_read,
 483        .poll           = gen_rtc_poll,
 484#endif
 485        .ioctl          = gen_rtc_ioctl,
 486        .open           = gen_rtc_open,
 487        .release        = gen_rtc_release,
 488};
 489
 490static struct miscdevice rtc_gen_dev =
 491{
 492        .minor          = RTC_MINOR,
 493        .name           = "rtc",
 494        .fops           = &gen_rtc_fops,
 495};
 496
 497static int __init rtc_generic_init(void)
 498{
 499        int retval;
 500
 501        printk(KERN_INFO "Generic RTC Driver v%s\n", RTC_VERSION);
 502
 503        retval = misc_register(&rtc_gen_dev);
 504        if(retval < 0)
 505                return retval;
 506
 507#ifdef CONFIG_PROC_FS
 508        if((create_proc_read_entry ("driver/rtc", 0, 0, gen_rtc_read_proc, NULL)) == NULL){
 509                misc_deregister(&rtc_gen_dev);
 510                return -ENOMEM;
 511        }
 512#endif
 513
 514        return 0;
 515}
 516
 517static void __exit rtc_generic_exit(void)
 518{
 519        remove_proc_entry ("driver/rtc", NULL);
 520        misc_deregister(&rtc_gen_dev);
 521}
 522
 523
 524module_init(rtc_generic_init);
 525module_exit(rtc_generic_exit);
 526EXPORT_NO_SYMBOLS;
 527
 528MODULE_AUTHOR("Richard Zidlicky");
 529MODULE_LICENSE("GPL");
 530
 531
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.