From: Alessandro Zummo Date: Tue, 16 Oct 2007 08:28:15 +0000 (-0700) Subject: RTC: periodic irq fix X-Git-Tag: v2.6.24-rc1~971 X-Git-Url: https://err.no/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=d691eb901e044065de10756ea78a5758d457c7fd;p=linux-2.6 RTC: periodic irq fix Add kernel/kernel and kernel/user locking for the periodic irq feature of the rtc class. PIE ioctls are also supported. Signed-off-by: Alessandro Zummo Cc: David Brownell Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- diff --git a/drivers/rtc/class.c b/drivers/rtc/class.c index 10ab3b71ff..4dfdf019fc 100644 --- a/drivers/rtc/class.c +++ b/drivers/rtc/class.c @@ -153,6 +153,7 @@ struct rtc_device *rtc_device_register(const char *name, struct device *dev, mutex_init(&rtc->ops_lock); spin_lock_init(&rtc->irq_lock); spin_lock_init(&rtc->irq_task_lock); + init_waitqueue_head(&rtc->irq_queue); strlcpy(rtc->name, name, RTC_DEVICE_NAME_SIZE); snprintf(rtc->dev.bus_id, BUS_ID_SIZE, "rtc%d", id); diff --git a/drivers/rtc/interface.c b/drivers/rtc/interface.c index ad66c6ecf3..a43afa54f0 100644 --- a/drivers/rtc/interface.c +++ b/drivers/rtc/interface.c @@ -210,6 +210,10 @@ int rtc_irq_register(struct rtc_device *rtc, struct rtc_task *task) if (task == NULL || task->func == NULL) return -EINVAL; + /* Cannot register while the char dev is in use */ + if (!(mutex_trylock(&rtc->char_lock))) + return -EBUSY; + spin_lock_irq(&rtc->irq_task_lock); if (rtc->irq_task == NULL) { rtc->irq_task = task; @@ -217,13 +221,14 @@ int rtc_irq_register(struct rtc_device *rtc, struct rtc_task *task) } spin_unlock_irq(&rtc->irq_task_lock); + mutex_unlock(&rtc->char_lock); + return retval; } EXPORT_SYMBOL_GPL(rtc_irq_register); void rtc_irq_unregister(struct rtc_device *rtc, struct rtc_task *task) { - spin_lock_irq(&rtc->irq_task_lock); if (rtc->irq_task == task) rtc->irq_task = NULL; @@ -240,8 +245,10 @@ int rtc_irq_set_state(struct rtc_device *rtc, struct rtc_task *task, int enabled return -ENXIO; spin_lock_irqsave(&rtc->irq_task_lock, flags); + if (rtc->irq_task != NULL && task == NULL) + err = -EBUSY; if (rtc->irq_task != task) - err = -ENXIO; + err = -EACCES; spin_unlock_irqrestore(&rtc->irq_task_lock, flags); if (err == 0) @@ -260,8 +267,10 @@ int rtc_irq_set_freq(struct rtc_device *rtc, struct rtc_task *task, int freq) return -ENXIO; spin_lock_irqsave(&rtc->irq_task_lock, flags); + if (rtc->irq_task != NULL && task == NULL) + err = -EBUSY; if (rtc->irq_task != task) - err = -ENXIO; + err = -EACCES; spin_unlock_irqrestore(&rtc->irq_task_lock, flags); if (err == 0) { diff --git a/drivers/rtc/rtc-dev.c b/drivers/rtc/rtc-dev.c index 005fff3a35..362400db2e 100644 --- a/drivers/rtc/rtc-dev.c +++ b/drivers/rtc/rtc-dev.c @@ -238,17 +238,6 @@ static int rtc_dev_ioctl(struct inode *inode, struct file *file, break; } - /* avoid conflicting IRQ users */ - if (cmd == RTC_PIE_ON || cmd == RTC_PIE_OFF || cmd == RTC_IRQP_SET) { - spin_lock_irq(&rtc->irq_task_lock); - if (rtc->irq_task) - err = -EBUSY; - spin_unlock_irq(&rtc->irq_task_lock); - - if (err < 0) - return err; - } - /* try the driver's ioctl interface */ if (ops->ioctl) { err = ops->ioctl(rtc->dev.parent, cmd, arg); @@ -338,18 +327,20 @@ static int rtc_dev_ioctl(struct inode *inode, struct file *file, err = rtc_set_time(rtc, &tm); break; - case RTC_IRQP_READ: - if (ops->irq_set_freq) - err = put_user(rtc->irq_freq, (unsigned long __user *)uarg); - else - err = -ENOTTY; + case RTC_PIE_ON: + err = rtc_irq_set_state(rtc, NULL, 1); + break; + + case RTC_PIE_OFF: + err = rtc_irq_set_state(rtc, NULL, 0); break; case RTC_IRQP_SET: - if (ops->irq_set_freq) - err = rtc_irq_set_freq(rtc, rtc->irq_task, arg); - else - err = -ENOTTY; + err = rtc_irq_set_freq(rtc, NULL, arg); + break; + + case RTC_IRQP_READ: + err = put_user(rtc->irq_freq, (unsigned long __user *)uarg); break; #if 0 @@ -449,8 +440,6 @@ void rtc_dev_prepare(struct rtc_device *rtc) rtc->dev.devt = MKDEV(MAJOR(rtc_devt), rtc->id); mutex_init(&rtc->char_lock); - spin_lock_init(&rtc->irq_lock); - init_waitqueue_head(&rtc->irq_queue); #ifdef CONFIG_RTC_INTF_DEV_UIE_EMUL INIT_WORK(&rtc->uie_task, rtc_uie_task); setup_timer(&rtc->uie_timer, rtc_uie_timer, (unsigned long)rtc);