--- 2.6.8/drivers/char/genrtc.c.orig 2004-10-18 19:20:58.000000000 +0100 +++ 2.6.8/drivers/char/genrtc.c 2004-10-20 03:01:12.000000000 +0100 @@ -38,9 +38,10 @@ * 1.05 portable RTC_UIE emulation rz@linux-m68k.org * 1.06 set_rtc_time can return an error trini@kernel.crashing.org * 1.07 ported to HP PARISC (hppa) Helge Deller + * 1.08 support RTC_PIE and RTC_CALLBK Martin Habets */ -#define RTC_VERSION "1.07" +#define RTC_VERSION "1.08" #include #include @@ -72,9 +73,20 @@ */ #define RTC_IS_OPEN 0x01 /* means /dev/rtc is in use */ +#define RTC_TIMER_ON 0x02 /* missed irq timer active */ static unsigned char gen_rtc_status; /* bitmapped status byte. */ static unsigned long gen_rtc_irq_data; /* our output to the world */ +static spinlock_t gen_rtc_lock = SPIN_LOCK_UNLOCKED; + +static unsigned long gen_rtc_freq = 0; /* Current periodic IRQ rate */ + /* > this, need CAP_SYS_RESOURCE */ +static unsigned long gen_rtc_max_user_freq = 64; +static struct timer_list gen_rtc_irq_timer; + +/* RTC_CALLBK support */ +static spinlock_t gen_rtc_task_lock = SPIN_LOCK_UNLOCKED; +static rtc_task_t gen_rtc_callback = { NULL, NULL }; /* months start at 0 now */ static unsigned char days_in_mo[] = @@ -95,7 +107,6 @@ static volatile int stask_active; /* schedule_work */ static volatile int ttask_active; /* timer_task */ static int stop_rtc_timers; /* don't requeue tasks */ -static spinlock_t gen_rtc_lock = SPIN_LOCK_UNLOCKED; static void gen_rtc_interrupt(unsigned long arg); @@ -122,7 +133,7 @@ stask_active=0; add_timer(&timer_task); - gen_rtc_interrupt(0); + gen_rtc_interrupt(RTC_UIE); } else if (schedule_work(&genrtc_task) == 0) stask_active = 0; } @@ -140,10 +151,11 @@ if ((schedule_work(&genrtc_task) == 0)) stask_active = 0; } +#endif /* - * call gen_rtc_interrupt function to signal an RTC_UIE, - * arg is unused. + * call gen_rtc_interrupt function to signal an RTC_UIE or an RTC_PIE, + * as indicated by arg. * Could be invoked either from a real interrupt handler or * from some routine that periodically (eg 100HZ) monitors * whether RTC_SECS changed @@ -154,20 +166,34 @@ * interrupts received since the last read in the remainder * of rtc_irq_data. */ + spin_lock (&gen_rtc_lock); gen_rtc_irq_data += 0x100; gen_rtc_irq_data &= ~0xff; - gen_rtc_irq_data |= RTC_UIE; + gen_rtc_irq_data |= arg; + + if (gen_rtc_status & RTC_TIMER_ON) + mod_timer(&gen_rtc_irq_timer, jiffies + HZ/gen_rtc_freq + 2*HZ/100); + spin_unlock (&gen_rtc_lock); + +#ifdef CONFIG_GEN_RTC_X if (lostint){ printk("genrtc: system delaying clock ticks?\n"); /* increment count so that userspace knows something is wrong */ gen_rtc_irq_data += ((lostint-1)<<8); lostint = 0; } +#endif + + //spin_lock (&gen_rtc_task_lock); + if (gen_rtc_callback.func) + gen_rtc_callback.func(gen_rtc_callback.private_data); + //spin_unlock (&gen_rtc_task_lock); wake_up_interruptible(&gen_rtc_wait); } +#ifdef CONFIG_GEN_RTC_X /* * Now all the various file operations that we export. */ @@ -225,6 +251,9 @@ #endif + + + /* * Used to disable/enable interrupts, only RTC_UIE supported * We also clear out any old irq data after an ioctl() that @@ -273,8 +302,8 @@ #endif } -static int gen_rtc_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) + +static int gen_rtc_do_ioctl(unsigned int cmd, unsigned long arg, int kernel) { struct rtc_time wtime; struct rtc_pll_info pll; @@ -338,11 +367,151 @@ return set_rtc_time(&wtime); } + case RTC_IRQP_READ: /* Read the periodic IRQ rate. */ + { + return put_user(gen_rtc_freq, (unsigned long __user *)arg); + } + case RTC_IRQP_SET: /* Set the periodic IRQ rate. */ + { + int tmp = 0; + + /* + * The max we can do is 8192Hz. + */ + if ((arg < 2) || (arg > 8192)) + return -EINVAL; + /* + * We don't really want Joe User generating more + * than 64Hz of interrupts on a multi-user machine. + */ + if ((arg > gen_rtc_max_user_freq) && + (!capable(CAP_SYS_RESOURCE))) + return -EACCES; + + while (arg > (1< gen_rtc_max_user_freq) && + (!capable(CAP_SYS_RESOURCE))) + return -EACCES; + if (gen_rtc_freq < 2) + return -EINVAL; + + if (!(gen_rtc_status & RTC_TIMER_ON)) { + spin_lock_irq (&gen_rtc_lock); + init_timer(&gen_rtc_irq_timer); + gen_rtc_irq_timer.expires = jiffies + HZ/gen_rtc_freq + 2*HZ/100; + gen_rtc_irq_timer.function = gen_rtc_interrupt; + gen_rtc_irq_timer.data = RTC_PIE; + add_timer(&gen_rtc_irq_timer); + gen_rtc_status |= RTC_TIMER_ON; + gen_rtc_irq_data = 0; + spin_unlock_irq (&gen_rtc_lock); + } + //gen_set_rtc_irq_bit(RTC_PIE); + return 0; + } + case RTC_PIE_OFF: /* Mask periodic int. enab. bit */ + { + //gen_clear_rtc_irq_bit(RTC_PIE); + if (gen_rtc_status & RTC_TIMER_ON) { + spin_lock_irq (&gen_rtc_lock); + gen_rtc_status &= ~RTC_TIMER_ON; + del_timer(&gen_rtc_irq_timer); + spin_unlock_irq (&gen_rtc_lock); + } + return 0; + } } return -EINVAL; } +static int gen_rtc_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + return gen_rtc_do_ioctl(cmd, arg, 0); +} + +/* + * exported stuffs + */ + +EXPORT_SYMBOL(rtc_register); +EXPORT_SYMBOL(rtc_unregister); +EXPORT_SYMBOL(rtc_control); + +int rtc_register(rtc_task_t *task) +{ + if (task == NULL || task->func == NULL) + return -EINVAL; + spin_lock_irq(&gen_rtc_lock); + if (gen_rtc_status & RTC_IS_OPEN) { + spin_unlock_irq(&gen_rtc_lock); + return -EBUSY; + } + spin_lock(&gen_rtc_task_lock); + if (gen_rtc_callback.func) { + spin_unlock(&gen_rtc_task_lock); + spin_unlock_irq(&gen_rtc_lock); + return -EBUSY; + } + gen_rtc_status |= RTC_IS_OPEN; + gen_rtc_callback = *task; + spin_unlock(&gen_rtc_task_lock); + spin_unlock_irq(&gen_rtc_lock); + return 0; +} + +int rtc_unregister(rtc_task_t *task) +{ + spin_lock_irq(&gen_rtc_lock); + spin_lock(&gen_rtc_task_lock); + if (gen_rtc_callback.func != task->func) { + spin_unlock(&gen_rtc_task_lock); + spin_unlock_irq(&gen_rtc_lock); + return -ENXIO; + } + gen_rtc_callback.func = NULL; + gen_rtc_callback.private_data = NULL; + + if (gen_rtc_status & RTC_TIMER_ON) { + gen_rtc_status &= ~RTC_TIMER_ON; + del_timer(&gen_rtc_irq_timer); + } + gen_rtc_status &= ~RTC_IS_OPEN; + spin_unlock(&gen_rtc_task_lock); + spin_unlock_irq(&gen_rtc_lock); + return 0; +} + +int rtc_control(rtc_task_t *task, unsigned int cmd, unsigned long arg) +{ + spin_lock_irq(&gen_rtc_task_lock); + if (gen_rtc_callback.func != task->func) { + spin_unlock_irq(&gen_rtc_task_lock); + return -ENXIO; + } + spin_unlock_irq(&gen_rtc_task_lock); + return gen_rtc_do_ioctl(cmd, arg, 1); +} + + /* * We enforce only one user at a time here with the open/close. * Also clear the previous interrupt data on an open, and clean @@ -370,7 +539,9 @@ gen_clear_rtc_irq_bit(RTC_PIE|RTC_AIE|RTC_UIE); - gen_rtc_status &= ~RTC_IS_OPEN; + gen_rtc_status = 0; + gen_rtc_callback.func = NULL; + gen_rtc_callback.private_data = NULL; return 0; } @@ -378,7 +549,7 @@ #ifdef CONFIG_PROC_FS /* - * Info exported via "/proc/rtc". + * Info exported via "/proc/driver/rtc". */ static int gen_rtc_proc_output(char *buf) @@ -391,6 +562,7 @@ p = buf; flags = get_rtc_time(&tm); + flags |= RTC_PIE; p += sprintf(p, "rtc_time\t: %02d:%02d:%02d\n" @@ -434,7 +606,7 @@ (flags & RTC_AIE) ? "yes" : "no", irq_active ? "yes" : "no", (flags & RTC_PIE) ? "yes" : "no", - 0L /* freq */, + gen_rtc_freq, (flags & RTC_BATT_BAD) ? "bad" : "okay"); if (!get_rtc_pll(&pll)) p += sprintf(p,