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