Root/drivers/char/genrtc.c

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

Archive Download this file



interactive