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 2011567005f..61a58259c93 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; |