diff options
Diffstat (limited to 'drivers/rtc/rtc-dev.c')
| -rw-r--r-- | drivers/rtc/rtc-dev.c | 131 |
1 files changed, 124 insertions, 7 deletions
diff --git a/drivers/rtc/rtc-dev.c b/drivers/rtc/rtc-dev.c index 2011567005f9..61a58259c93f 100644 --- a/drivers/rtc/rtc-dev.c +++ b/drivers/rtc/rtc-dev.c | |||
| @@ -48,6 +48,93 @@ static int rtc_dev_open(struct inode *inode, struct file *file) | |||
| 48 | return err; | 48 | return err; |
| 49 | } | 49 | } |
| 50 | 50 | ||
| 51 | #ifdef CONFIG_RTC_INTF_DEV_UIE_EMUL | ||
| 52 | /* | ||
| 53 | * Routine to poll RTC seconds field for change as often as possible, | ||
| 54 | * after first RTC_UIE use timer to reduce polling | ||
| 55 | */ | ||
| 56 | static void rtc_uie_task(void *data) | ||
| 57 | { | ||
| 58 | struct rtc_device *rtc = data; | ||
| 59 | struct rtc_time tm; | ||
| 60 | int num = 0; | ||
| 61 | int err; | ||
| 62 | |||
| 63 | err = rtc_read_time(&rtc->class_dev, &tm); | ||
| 64 | spin_lock_irq(&rtc->irq_lock); | ||
| 65 | if (rtc->stop_uie_polling || err) { | ||
| 66 | rtc->uie_task_active = 0; | ||
| 67 | } else if (rtc->oldsecs != tm.tm_sec) { | ||
| 68 | num = (tm.tm_sec + 60 - rtc->oldsecs) % 60; | ||
| 69 | rtc->oldsecs = tm.tm_sec; | ||
| 70 | rtc->uie_timer.expires = jiffies + HZ - (HZ/10); | ||
| 71 | rtc->uie_timer_active = 1; | ||
| 72 | rtc->uie_task_active = 0; | ||
| 73 | add_timer(&rtc->uie_timer); | ||
| 74 | } else if (schedule_work(&rtc->uie_task) == 0) { | ||
| 75 | rtc->uie_task_active = 0; | ||
| 76 | } | ||
| 77 | spin_unlock_irq(&rtc->irq_lock); | ||
| 78 | if (num) | ||
| 79 | rtc_update_irq(&rtc->class_dev, num, RTC_UF | RTC_IRQF); | ||
| 80 | } | ||
| 81 | |||
| 82 | static void rtc_uie_timer(unsigned long data) | ||
| 83 | { | ||
| 84 | struct rtc_device *rtc = (struct rtc_device *)data; | ||
| 85 | unsigned long flags; | ||
| 86 | |||
| 87 | spin_lock_irqsave(&rtc->irq_lock, flags); | ||
| 88 | rtc->uie_timer_active = 0; | ||
| 89 | rtc->uie_task_active = 1; | ||
| 90 | if ((schedule_work(&rtc->uie_task) == 0)) | ||
| 91 | rtc->uie_task_active = 0; | ||
| 92 | spin_unlock_irqrestore(&rtc->irq_lock, flags); | ||
| 93 | } | ||
| 94 | |||
| 95 | static void clear_uie(struct rtc_device *rtc) | ||
| 96 | { | ||
| 97 | spin_lock_irq(&rtc->irq_lock); | ||
| 98 | if (rtc->irq_active) { | ||
| 99 | rtc->stop_uie_polling = 1; | ||
| 100 | if (rtc->uie_timer_active) { | ||
| 101 | spin_unlock_irq(&rtc->irq_lock); | ||
| 102 | del_timer_sync(&rtc->uie_timer); | ||
| 103 | spin_lock_irq(&rtc->irq_lock); | ||
| 104 | rtc->uie_timer_active = 0; | ||
| 105 | } | ||
| 106 | if (rtc->uie_task_active) { | ||
| 107 | spin_unlock_irq(&rtc->irq_lock); | ||
| 108 | flush_scheduled_work(); | ||
| 109 | spin_lock_irq(&rtc->irq_lock); | ||
| 110 | } | ||
| 111 | rtc->irq_active = 0; | ||
| 112 | } | ||
| 113 | spin_unlock_irq(&rtc->irq_lock); | ||
| 114 | } | ||
| 115 | |||
| 116 | static int set_uie(struct rtc_device *rtc) | ||
| 117 | { | ||
| 118 | struct rtc_time tm; | ||
| 119 | int err; | ||
| 120 | |||
| 121 | err = rtc_read_time(&rtc->class_dev, &tm); | ||
| 122 | if (err) | ||
| 123 | return err; | ||
| 124 | spin_lock_irq(&rtc->irq_lock); | ||
| 125 | if (!rtc->irq_active) { | ||
| 126 | rtc->irq_active = 1; | ||
| 127 | rtc->stop_uie_polling = 0; | ||
| 128 | rtc->oldsecs = tm.tm_sec; | ||
| 129 | rtc->uie_task_active = 1; | ||
| 130 | if (schedule_work(&rtc->uie_task) == 0) | ||
| 131 | rtc->uie_task_active = 0; | ||
| 132 | } | ||
| 133 | rtc->irq_data = 0; | ||
| 134 | spin_unlock_irq(&rtc->irq_lock); | ||
| 135 | return 0; | ||
| 136 | } | ||
| 137 | #endif /* CONFIG_RTC_INTF_DEV_UIE_EMUL */ | ||
| 51 | 138 | ||
| 52 | static ssize_t | 139 | static ssize_t |
| 53 | rtc_dev_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) | 140 | rtc_dev_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) |
| @@ -127,6 +214,28 @@ static int rtc_dev_ioctl(struct inode *inode, struct file *file, | |||
| 127 | struct rtc_wkalrm alarm; | 214 | struct rtc_wkalrm alarm; |
| 128 | void __user *uarg = (void __user *) arg; | 215 | void __user *uarg = (void __user *) arg; |
| 129 | 216 | ||
| 217 | /* check that the calles has appropriate permissions | ||
| 218 | * for certain ioctls. doing this check here is useful | ||
| 219 | * to avoid duplicate code in each driver. | ||
| 220 | */ | ||
| 221 | switch (cmd) { | ||
| 222 | case RTC_EPOCH_SET: | ||
| 223 | case RTC_SET_TIME: | ||
| 224 | if (!capable(CAP_SYS_TIME)) | ||
| 225 | return -EACCES; | ||
| 226 | break; | ||
| 227 | |||
| 228 | case RTC_IRQP_SET: | ||
| 229 | if (arg > rtc->max_user_freq && !capable(CAP_SYS_RESOURCE)) | ||
| 230 | return -EACCES; | ||
| 231 | break; | ||
| 232 | |||
| 233 | case RTC_PIE_ON: | ||
| 234 | if (!capable(CAP_SYS_RESOURCE)) | ||
| 235 | return -EACCES; | ||
| 236 | break; | ||
| 237 | } | ||
| 238 | |||
| 130 | /* avoid conflicting IRQ users */ | 239 | /* avoid conflicting IRQ users */ |
| 131 | if (cmd == RTC_PIE_ON || cmd == RTC_PIE_OFF || cmd == RTC_IRQP_SET) { | 240 | if (cmd == RTC_PIE_ON || cmd == RTC_PIE_OFF || cmd == RTC_IRQP_SET) { |
| 132 | spin_lock(&rtc->irq_task_lock); | 241 | spin_lock(&rtc->irq_task_lock); |
| @@ -185,9 +294,6 @@ static int rtc_dev_ioctl(struct inode *inode, struct file *file, | |||
| 185 | break; | 294 | break; |
| 186 | 295 | ||
| 187 | case RTC_SET_TIME: | 296 | case RTC_SET_TIME: |
| 188 | if (!capable(CAP_SYS_TIME)) | ||
| 189 | return -EACCES; | ||
| 190 | |||
| 191 | if (copy_from_user(&tm, uarg, sizeof(tm))) | 297 | if (copy_from_user(&tm, uarg, sizeof(tm))) |
| 192 | return -EFAULT; | 298 | return -EFAULT; |
| 193 | 299 | ||
| @@ -203,10 +309,6 @@ static int rtc_dev_ioctl(struct inode *inode, struct file *file, | |||
| 203 | err = -EINVAL; | 309 | err = -EINVAL; |
| 204 | break; | 310 | break; |
| 205 | } | 311 | } |
| 206 | if (!capable(CAP_SYS_TIME)) { | ||
| 207 | err = -EACCES; | ||
| 208 | break; | ||
| 209 | } | ||
| 210 | rtc_epoch = arg; | 312 | rtc_epoch = arg; |
| 211 | err = 0; | 313 | err = 0; |
| 212 | #endif | 314 | #endif |
| @@ -232,6 +334,14 @@ static int rtc_dev_ioctl(struct inode *inode, struct file *file, | |||
| 232 | return -EFAULT; | 334 | return -EFAULT; |
| 233 | break; | 335 | break; |
| 234 | 336 | ||
| 337 | #ifdef CONFIG_RTC_INTF_DEV_UIE_EMUL | ||
| 338 | case RTC_UIE_OFF: | ||
| 339 | clear_uie(rtc); | ||
| 340 | return 0; | ||
| 341 | |||
| 342 | case RTC_UIE_ON: | ||
| 343 | return set_uie(rtc); | ||
| 344 | #endif | ||
| 235 | default: | 345 | default: |
| 236 | err = -ENOTTY; | 346 | err = -ENOTTY; |
| 237 | break; | 347 | break; |
| @@ -244,6 +354,9 @@ static int rtc_dev_release(struct inode *inode, struct file *file) | |||
| 244 | { | 354 | { |
| 245 | struct rtc_device *rtc = to_rtc_device(file->private_data); | 355 | struct rtc_device *rtc = to_rtc_device(file->private_data); |
| 246 | 356 | ||
| 357 | #ifdef CONFIG_RTC_INTF_DEV_UIE_EMUL | ||
| 358 | clear_uie(rtc); | ||
| 359 | #endif | ||
| 247 | if (rtc->ops->release) | 360 | if (rtc->ops->release) |
| 248 | rtc->ops->release(rtc->class_dev.dev); | 361 | rtc->ops->release(rtc->class_dev.dev); |
| 249 | 362 | ||
| @@ -284,6 +397,10 @@ static int rtc_dev_add_device(struct class_device *class_dev, | |||
| 284 | mutex_init(&rtc->char_lock); | 397 | mutex_init(&rtc->char_lock); |
| 285 | spin_lock_init(&rtc->irq_lock); | 398 | spin_lock_init(&rtc->irq_lock); |
| 286 | init_waitqueue_head(&rtc->irq_queue); | 399 | init_waitqueue_head(&rtc->irq_queue); |
| 400 | #ifdef CONFIG_RTC_INTF_DEV_UIE_EMUL | ||
| 401 | INIT_WORK(&rtc->uie_task, rtc_uie_task, rtc); | ||
| 402 | setup_timer(&rtc->uie_timer, rtc_uie_timer, (unsigned long)rtc); | ||
| 403 | #endif | ||
| 287 | 404 | ||
| 288 | cdev_init(&rtc->char_dev, &rtc_dev_fops); | 405 | cdev_init(&rtc->char_dev, &rtc_dev_fops); |
| 289 | rtc->char_dev.owner = rtc->owner; | 406 | rtc->char_dev.owner = rtc->owner; |
