diff options
author | Jonathan Herman <hermanjl@cs.unc.edu> | 2013-01-22 10:38:37 -0500 |
---|---|---|
committer | Jonathan Herman <hermanjl@cs.unc.edu> | 2013-01-22 10:38:37 -0500 |
commit | fcc9d2e5a6c89d22b8b773a64fb4ad21ac318446 (patch) | |
tree | a57612d1888735a2ec7972891b68c1ac5ec8faea /drivers/rtc/alarm-dev.c | |
parent | 8dea78da5cee153b8af9c07a2745f6c55057fe12 (diff) |
Diffstat (limited to 'drivers/rtc/alarm-dev.c')
-rw-r--r-- | drivers/rtc/alarm-dev.c | 286 |
1 files changed, 286 insertions, 0 deletions
diff --git a/drivers/rtc/alarm-dev.c b/drivers/rtc/alarm-dev.c new file mode 100644 index 00000000000..686e6f7ed48 --- /dev/null +++ b/drivers/rtc/alarm-dev.c | |||
@@ -0,0 +1,286 @@ | |||
1 | /* drivers/rtc/alarm-dev.c | ||
2 | * | ||
3 | * Copyright (C) 2007-2009 Google, Inc. | ||
4 | * | ||
5 | * This software is licensed under the terms of the GNU General Public | ||
6 | * License version 2, as published by the Free Software Foundation, and | ||
7 | * may be copied, distributed, and modified under those terms. | ||
8 | * | ||
9 | * This program is distributed in the hope that it will be useful, | ||
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
12 | * GNU General Public License for more details. | ||
13 | * | ||
14 | */ | ||
15 | |||
16 | #include <asm/mach/time.h> | ||
17 | #include <linux/android_alarm.h> | ||
18 | #include <linux/device.h> | ||
19 | #include <linux/miscdevice.h> | ||
20 | #include <linux/fs.h> | ||
21 | #include <linux/platform_device.h> | ||
22 | #include <linux/sched.h> | ||
23 | #include <linux/spinlock.h> | ||
24 | #include <linux/sysdev.h> | ||
25 | #include <linux/uaccess.h> | ||
26 | #include <linux/wakelock.h> | ||
27 | |||
28 | #define ANDROID_ALARM_PRINT_INFO (1U << 0) | ||
29 | #define ANDROID_ALARM_PRINT_IO (1U << 1) | ||
30 | #define ANDROID_ALARM_PRINT_INT (1U << 2) | ||
31 | |||
32 | static int debug_mask = ANDROID_ALARM_PRINT_INFO; | ||
33 | module_param_named(debug_mask, debug_mask, int, S_IRUGO | S_IWUSR | S_IWGRP); | ||
34 | |||
35 | #define pr_alarm(debug_level_mask, args...) \ | ||
36 | do { \ | ||
37 | if (debug_mask & ANDROID_ALARM_PRINT_##debug_level_mask) { \ | ||
38 | pr_info(args); \ | ||
39 | } \ | ||
40 | } while (0) | ||
41 | |||
42 | #define ANDROID_ALARM_WAKEUP_MASK ( \ | ||
43 | ANDROID_ALARM_RTC_WAKEUP_MASK | \ | ||
44 | ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP_MASK) | ||
45 | |||
46 | /* support old usespace code */ | ||
47 | #define ANDROID_ALARM_SET_OLD _IOW('a', 2, time_t) /* set alarm */ | ||
48 | #define ANDROID_ALARM_SET_AND_WAIT_OLD _IOW('a', 3, time_t) | ||
49 | |||
50 | static int alarm_opened; | ||
51 | static DEFINE_SPINLOCK(alarm_slock); | ||
52 | static struct wake_lock alarm_wake_lock; | ||
53 | static DECLARE_WAIT_QUEUE_HEAD(alarm_wait_queue); | ||
54 | static uint32_t alarm_pending; | ||
55 | static uint32_t alarm_enabled; | ||
56 | static uint32_t wait_pending; | ||
57 | |||
58 | static struct alarm alarms[ANDROID_ALARM_TYPE_COUNT]; | ||
59 | |||
60 | static long alarm_ioctl(struct file *file, unsigned int cmd, unsigned long arg) | ||
61 | { | ||
62 | int rv = 0; | ||
63 | unsigned long flags; | ||
64 | struct timespec new_alarm_time; | ||
65 | struct timespec new_rtc_time; | ||
66 | struct timespec tmp_time; | ||
67 | enum android_alarm_type alarm_type = ANDROID_ALARM_IOCTL_TO_TYPE(cmd); | ||
68 | uint32_t alarm_type_mask = 1U << alarm_type; | ||
69 | |||
70 | if (alarm_type >= ANDROID_ALARM_TYPE_COUNT) | ||
71 | return -EINVAL; | ||
72 | |||
73 | if (ANDROID_ALARM_BASE_CMD(cmd) != ANDROID_ALARM_GET_TIME(0)) { | ||
74 | if ((file->f_flags & O_ACCMODE) == O_RDONLY) | ||
75 | return -EPERM; | ||
76 | if (file->private_data == NULL && | ||
77 | cmd != ANDROID_ALARM_SET_RTC) { | ||
78 | spin_lock_irqsave(&alarm_slock, flags); | ||
79 | if (alarm_opened) { | ||
80 | spin_unlock_irqrestore(&alarm_slock, flags); | ||
81 | return -EBUSY; | ||
82 | } | ||
83 | alarm_opened = 1; | ||
84 | file->private_data = (void *)1; | ||
85 | spin_unlock_irqrestore(&alarm_slock, flags); | ||
86 | } | ||
87 | } | ||
88 | |||
89 | switch (ANDROID_ALARM_BASE_CMD(cmd)) { | ||
90 | case ANDROID_ALARM_CLEAR(0): | ||
91 | spin_lock_irqsave(&alarm_slock, flags); | ||
92 | pr_alarm(IO, "alarm %d clear\n", alarm_type); | ||
93 | alarm_try_to_cancel(&alarms[alarm_type]); | ||
94 | if (alarm_pending) { | ||
95 | alarm_pending &= ~alarm_type_mask; | ||
96 | if (!alarm_pending && !wait_pending) | ||
97 | wake_unlock(&alarm_wake_lock); | ||
98 | } | ||
99 | alarm_enabled &= ~alarm_type_mask; | ||
100 | spin_unlock_irqrestore(&alarm_slock, flags); | ||
101 | break; | ||
102 | |||
103 | case ANDROID_ALARM_SET_OLD: | ||
104 | case ANDROID_ALARM_SET_AND_WAIT_OLD: | ||
105 | if (get_user(new_alarm_time.tv_sec, (int __user *)arg)) { | ||
106 | rv = -EFAULT; | ||
107 | goto err1; | ||
108 | } | ||
109 | new_alarm_time.tv_nsec = 0; | ||
110 | goto from_old_alarm_set; | ||
111 | |||
112 | case ANDROID_ALARM_SET_AND_WAIT(0): | ||
113 | case ANDROID_ALARM_SET(0): | ||
114 | if (copy_from_user(&new_alarm_time, (void __user *)arg, | ||
115 | sizeof(new_alarm_time))) { | ||
116 | rv = -EFAULT; | ||
117 | goto err1; | ||
118 | } | ||
119 | from_old_alarm_set: | ||
120 | spin_lock_irqsave(&alarm_slock, flags); | ||
121 | pr_alarm(IO, "alarm %d set %ld.%09ld\n", alarm_type, | ||
122 | new_alarm_time.tv_sec, new_alarm_time.tv_nsec); | ||
123 | alarm_enabled |= alarm_type_mask; | ||
124 | alarm_start_range(&alarms[alarm_type], | ||
125 | timespec_to_ktime(new_alarm_time), | ||
126 | timespec_to_ktime(new_alarm_time)); | ||
127 | spin_unlock_irqrestore(&alarm_slock, flags); | ||
128 | if (ANDROID_ALARM_BASE_CMD(cmd) != ANDROID_ALARM_SET_AND_WAIT(0) | ||
129 | && cmd != ANDROID_ALARM_SET_AND_WAIT_OLD) | ||
130 | break; | ||
131 | /* fall though */ | ||
132 | case ANDROID_ALARM_WAIT: | ||
133 | spin_lock_irqsave(&alarm_slock, flags); | ||
134 | pr_alarm(IO, "alarm wait\n"); | ||
135 | if (!alarm_pending && wait_pending) { | ||
136 | wake_unlock(&alarm_wake_lock); | ||
137 | wait_pending = 0; | ||
138 | } | ||
139 | spin_unlock_irqrestore(&alarm_slock, flags); | ||
140 | rv = wait_event_interruptible(alarm_wait_queue, alarm_pending); | ||
141 | if (rv) | ||
142 | goto err1; | ||
143 | spin_lock_irqsave(&alarm_slock, flags); | ||
144 | rv = alarm_pending; | ||
145 | wait_pending = 1; | ||
146 | alarm_pending = 0; | ||
147 | spin_unlock_irqrestore(&alarm_slock, flags); | ||
148 | break; | ||
149 | case ANDROID_ALARM_SET_RTC: | ||
150 | if (copy_from_user(&new_rtc_time, (void __user *)arg, | ||
151 | sizeof(new_rtc_time))) { | ||
152 | rv = -EFAULT; | ||
153 | goto err1; | ||
154 | } | ||
155 | rv = alarm_set_rtc(new_rtc_time); | ||
156 | spin_lock_irqsave(&alarm_slock, flags); | ||
157 | alarm_pending |= ANDROID_ALARM_TIME_CHANGE_MASK; | ||
158 | wake_up(&alarm_wait_queue); | ||
159 | spin_unlock_irqrestore(&alarm_slock, flags); | ||
160 | if (rv < 0) | ||
161 | goto err1; | ||
162 | break; | ||
163 | case ANDROID_ALARM_GET_TIME(0): | ||
164 | switch (alarm_type) { | ||
165 | case ANDROID_ALARM_RTC_WAKEUP: | ||
166 | case ANDROID_ALARM_RTC: | ||
167 | getnstimeofday(&tmp_time); | ||
168 | break; | ||
169 | case ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP: | ||
170 | case ANDROID_ALARM_ELAPSED_REALTIME: | ||
171 | tmp_time = | ||
172 | ktime_to_timespec(alarm_get_elapsed_realtime()); | ||
173 | break; | ||
174 | case ANDROID_ALARM_TYPE_COUNT: | ||
175 | case ANDROID_ALARM_SYSTEMTIME: | ||
176 | ktime_get_ts(&tmp_time); | ||
177 | break; | ||
178 | } | ||
179 | if (copy_to_user((void __user *)arg, &tmp_time, | ||
180 | sizeof(tmp_time))) { | ||
181 | rv = -EFAULT; | ||
182 | goto err1; | ||
183 | } | ||
184 | break; | ||
185 | |||
186 | default: | ||
187 | rv = -EINVAL; | ||
188 | goto err1; | ||
189 | } | ||
190 | err1: | ||
191 | return rv; | ||
192 | } | ||
193 | |||
194 | static int alarm_open(struct inode *inode, struct file *file) | ||
195 | { | ||
196 | file->private_data = NULL; | ||
197 | return 0; | ||
198 | } | ||
199 | |||
200 | static int alarm_release(struct inode *inode, struct file *file) | ||
201 | { | ||
202 | int i; | ||
203 | unsigned long flags; | ||
204 | |||
205 | spin_lock_irqsave(&alarm_slock, flags); | ||
206 | if (file->private_data != 0) { | ||
207 | for (i = 0; i < ANDROID_ALARM_TYPE_COUNT; i++) { | ||
208 | uint32_t alarm_type_mask = 1U << i; | ||
209 | if (alarm_enabled & alarm_type_mask) { | ||
210 | pr_alarm(INFO, "alarm_release: clear alarm, " | ||
211 | "pending %d\n", | ||
212 | !!(alarm_pending & alarm_type_mask)); | ||
213 | alarm_enabled &= ~alarm_type_mask; | ||
214 | } | ||
215 | spin_unlock_irqrestore(&alarm_slock, flags); | ||
216 | alarm_cancel(&alarms[i]); | ||
217 | spin_lock_irqsave(&alarm_slock, flags); | ||
218 | } | ||
219 | if (alarm_pending | wait_pending) { | ||
220 | if (alarm_pending) | ||
221 | pr_alarm(INFO, "alarm_release: clear " | ||
222 | "pending alarms %x\n", alarm_pending); | ||
223 | wake_unlock(&alarm_wake_lock); | ||
224 | wait_pending = 0; | ||
225 | alarm_pending = 0; | ||
226 | } | ||
227 | alarm_opened = 0; | ||
228 | } | ||
229 | spin_unlock_irqrestore(&alarm_slock, flags); | ||
230 | return 0; | ||
231 | } | ||
232 | |||
233 | static void alarm_triggered(struct alarm *alarm) | ||
234 | { | ||
235 | unsigned long flags; | ||
236 | uint32_t alarm_type_mask = 1U << alarm->type; | ||
237 | |||
238 | pr_alarm(INT, "alarm_triggered type %d\n", alarm->type); | ||
239 | spin_lock_irqsave(&alarm_slock, flags); | ||
240 | if (alarm_enabled & alarm_type_mask) { | ||
241 | wake_lock_timeout(&alarm_wake_lock, 5 * HZ); | ||
242 | alarm_enabled &= ~alarm_type_mask; | ||
243 | alarm_pending |= alarm_type_mask; | ||
244 | wake_up(&alarm_wait_queue); | ||
245 | } | ||
246 | spin_unlock_irqrestore(&alarm_slock, flags); | ||
247 | } | ||
248 | |||
249 | static const struct file_operations alarm_fops = { | ||
250 | .owner = THIS_MODULE, | ||
251 | .unlocked_ioctl = alarm_ioctl, | ||
252 | .open = alarm_open, | ||
253 | .release = alarm_release, | ||
254 | }; | ||
255 | |||
256 | static struct miscdevice alarm_device = { | ||
257 | .minor = MISC_DYNAMIC_MINOR, | ||
258 | .name = "alarm", | ||
259 | .fops = &alarm_fops, | ||
260 | }; | ||
261 | |||
262 | static int __init alarm_dev_init(void) | ||
263 | { | ||
264 | int err; | ||
265 | int i; | ||
266 | |||
267 | err = misc_register(&alarm_device); | ||
268 | if (err) | ||
269 | return err; | ||
270 | |||
271 | for (i = 0; i < ANDROID_ALARM_TYPE_COUNT; i++) | ||
272 | alarm_init(&alarms[i], i, alarm_triggered); | ||
273 | wake_lock_init(&alarm_wake_lock, WAKE_LOCK_SUSPEND, "alarm"); | ||
274 | |||
275 | return 0; | ||
276 | } | ||
277 | |||
278 | static void __exit alarm_dev_exit(void) | ||
279 | { | ||
280 | misc_deregister(&alarm_device); | ||
281 | wake_lock_destroy(&alarm_wake_lock); | ||
282 | } | ||
283 | |||
284 | module_init(alarm_dev_init); | ||
285 | module_exit(alarm_dev_exit); | ||
286 | |||