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/misc/mpu3050/timerirq.c | |
parent | 8dea78da5cee153b8af9c07a2745f6c55057fe12 (diff) |
Diffstat (limited to 'drivers/misc/mpu3050/timerirq.c')
-rw-r--r-- | drivers/misc/mpu3050/timerirq.c | 299 |
1 files changed, 299 insertions, 0 deletions
diff --git a/drivers/misc/mpu3050/timerirq.c b/drivers/misc/mpu3050/timerirq.c new file mode 100644 index 00000000000..41c3ac98101 --- /dev/null +++ b/drivers/misc/mpu3050/timerirq.c | |||
@@ -0,0 +1,299 @@ | |||
1 | /* | ||
2 | $License: | ||
3 | Copyright (C) 2010 InvenSense Corporation, All Rights Reserved. | ||
4 | |||
5 | This program is free software; you can redistribute it and/or modify | ||
6 | it under the terms of the GNU General Public License as published by | ||
7 | the Free Software Foundation; either version 2 of the License, or | ||
8 | (at your option) any later version. | ||
9 | |||
10 | This program is distributed in the hope that it will be useful, | ||
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | GNU General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU General Public License | ||
16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
17 | $ | ||
18 | */ | ||
19 | #include <linux/interrupt.h> | ||
20 | #include <linux/module.h> | ||
21 | #include <linux/moduleparam.h> | ||
22 | #include <linux/kernel.h> | ||
23 | #include <linux/init.h> | ||
24 | #include <linux/stat.h> | ||
25 | #include <linux/signal.h> | ||
26 | #include <linux/miscdevice.h> | ||
27 | #include <linux/i2c.h> | ||
28 | #include <linux/i2c-dev.h> | ||
29 | #include <linux/poll.h> | ||
30 | |||
31 | #include <linux/errno.h> | ||
32 | #include <linux/fs.h> | ||
33 | #include <linux/mm.h> | ||
34 | #include <linux/sched.h> | ||
35 | #include <linux/wait.h> | ||
36 | #include <linux/uaccess.h> | ||
37 | #include <linux/io.h> | ||
38 | #include <linux/timer.h> | ||
39 | #include <linux/slab.h> | ||
40 | |||
41 | #include "mpu.h" | ||
42 | #include "mltypes.h" | ||
43 | #include "timerirq.h" | ||
44 | |||
45 | /* function which gets timer data and sends it to TIMER */ | ||
46 | struct timerirq_data { | ||
47 | int pid; | ||
48 | int data_ready; | ||
49 | int run; | ||
50 | int timeout; | ||
51 | unsigned long period; | ||
52 | struct mpuirq_data data; | ||
53 | struct completion timer_done; | ||
54 | wait_queue_head_t timerirq_wait; | ||
55 | struct timer_list timer; | ||
56 | struct miscdevice *dev; | ||
57 | }; | ||
58 | |||
59 | static struct miscdevice *timerirq_dev_data; | ||
60 | |||
61 | static void timerirq_handler(unsigned long arg) | ||
62 | { | ||
63 | struct timerirq_data *data = (struct timerirq_data *)arg; | ||
64 | struct timeval irqtime; | ||
65 | |||
66 | data->data.interruptcount++; | ||
67 | |||
68 | data->data_ready = 1; | ||
69 | |||
70 | do_gettimeofday(&irqtime); | ||
71 | data->data.irqtime = (((long long) irqtime.tv_sec) << 32); | ||
72 | data->data.irqtime += irqtime.tv_usec; | ||
73 | data->data.data_type |= 1; | ||
74 | |||
75 | dev_dbg(data->dev->this_device, | ||
76 | "%s, %lld, %ld\n", __func__, data->data.irqtime, | ||
77 | (unsigned long)data); | ||
78 | |||
79 | wake_up_interruptible(&data->timerirq_wait); | ||
80 | |||
81 | if (data->run) | ||
82 | mod_timer(&data->timer, | ||
83 | jiffies + msecs_to_jiffies(data->period)); | ||
84 | else | ||
85 | complete(&data->timer_done); | ||
86 | } | ||
87 | |||
88 | static int start_timerirq(struct timerirq_data *data) | ||
89 | { | ||
90 | dev_dbg(data->dev->this_device, | ||
91 | "%s current->pid %d\n", __func__, current->pid); | ||
92 | |||
93 | /* Timer already running... success */ | ||
94 | if (data->run) | ||
95 | return 0; | ||
96 | |||
97 | /* Don't allow a period of 0 since this would fire constantly */ | ||
98 | if (!data->period) | ||
99 | return -EINVAL; | ||
100 | |||
101 | data->run = TRUE; | ||
102 | data->data_ready = FALSE; | ||
103 | |||
104 | init_completion(&data->timer_done); | ||
105 | setup_timer(&data->timer, timerirq_handler, (unsigned long)data); | ||
106 | |||
107 | return mod_timer(&data->timer, | ||
108 | jiffies + msecs_to_jiffies(data->period)); | ||
109 | } | ||
110 | |||
111 | static int stop_timerirq(struct timerirq_data *data) | ||
112 | { | ||
113 | dev_dbg(data->dev->this_device, | ||
114 | "%s current->pid %lx\n", __func__, (unsigned long)data); | ||
115 | |||
116 | if (data->run) { | ||
117 | data->run = FALSE; | ||
118 | mod_timer(&data->timer, jiffies + 1); | ||
119 | wait_for_completion(&data->timer_done); | ||
120 | } | ||
121 | return 0; | ||
122 | } | ||
123 | |||
124 | /* The following depends on patch fa1f68db6ca7ebb6fc4487ac215bffba06c01c28 | ||
125 | * drivers: misc: pass miscdevice pointer via file private data | ||
126 | */ | ||
127 | static int timerirq_open(struct inode *inode, struct file *file) | ||
128 | { | ||
129 | /* Device node is availabe in the file->private_data, this is | ||
130 | * exactly what we want so we leave it there */ | ||
131 | struct miscdevice *dev_data = file->private_data; | ||
132 | struct timerirq_data *data = kzalloc(sizeof(*data), GFP_KERNEL); | ||
133 | if (!data) | ||
134 | return -ENOMEM; | ||
135 | |||
136 | data->dev = dev_data; | ||
137 | file->private_data = data; | ||
138 | data->pid = current->pid; | ||
139 | init_waitqueue_head(&data->timerirq_wait); | ||
140 | |||
141 | dev_dbg(data->dev->this_device, | ||
142 | "%s current->pid %d\n", __func__, current->pid); | ||
143 | return 0; | ||
144 | } | ||
145 | |||
146 | static int timerirq_release(struct inode *inode, struct file *file) | ||
147 | { | ||
148 | struct timerirq_data *data = file->private_data; | ||
149 | dev_dbg(data->dev->this_device, "timerirq_release\n"); | ||
150 | if (data->run) | ||
151 | stop_timerirq(data); | ||
152 | kfree(data); | ||
153 | return 0; | ||
154 | } | ||
155 | |||
156 | /* read function called when from /dev/timerirq is read */ | ||
157 | static ssize_t timerirq_read(struct file *file, | ||
158 | char *buf, size_t count, loff_t *ppos) | ||
159 | { | ||
160 | int len, err; | ||
161 | struct timerirq_data *data = file->private_data; | ||
162 | |||
163 | if (!data->data_ready && | ||
164 | data->timeout && | ||
165 | !(file->f_flags & O_NONBLOCK)) { | ||
166 | wait_event_interruptible_timeout(data->timerirq_wait, | ||
167 | data->data_ready, | ||
168 | data->timeout); | ||
169 | } | ||
170 | |||
171 | if (data->data_ready && NULL != buf | ||
172 | && count >= sizeof(data->data)) { | ||
173 | err = copy_to_user(buf, &data->data, sizeof(data->data)); | ||
174 | data->data.data_type = 0; | ||
175 | } else { | ||
176 | return 0; | ||
177 | } | ||
178 | if (err != 0) { | ||
179 | dev_err(data->dev->this_device, | ||
180 | "Copy to user returned %d\n", err); | ||
181 | return -EFAULT; | ||
182 | } | ||
183 | data->data_ready = 0; | ||
184 | len = sizeof(data->data); | ||
185 | return len; | ||
186 | } | ||
187 | |||
188 | static unsigned int timerirq_poll(struct file *file, | ||
189 | struct poll_table_struct *poll) | ||
190 | { | ||
191 | int mask = 0; | ||
192 | struct timerirq_data *data = file->private_data; | ||
193 | |||
194 | poll_wait(file, &data->timerirq_wait, poll); | ||
195 | if (data->data_ready) | ||
196 | mask |= POLLIN | POLLRDNORM; | ||
197 | return mask; | ||
198 | } | ||
199 | |||
200 | /* ioctl - I/O control */ | ||
201 | static long timerirq_ioctl(struct file *file, | ||
202 | unsigned int cmd, unsigned long arg) | ||
203 | { | ||
204 | int retval = 0; | ||
205 | int tmp; | ||
206 | struct timerirq_data *data = file->private_data; | ||
207 | |||
208 | dev_dbg(data->dev->this_device, | ||
209 | "%s current->pid %d, %d, %ld\n", | ||
210 | __func__, current->pid, cmd, arg); | ||
211 | |||
212 | if (!data) | ||
213 | return -EFAULT; | ||
214 | |||
215 | switch (cmd) { | ||
216 | case TIMERIRQ_SET_TIMEOUT: | ||
217 | data->timeout = arg; | ||
218 | break; | ||
219 | case TIMERIRQ_GET_INTERRUPT_CNT: | ||
220 | tmp = data->data.interruptcount - 1; | ||
221 | if (data->data.interruptcount > 1) | ||
222 | data->data.interruptcount = 1; | ||
223 | |||
224 | if (copy_to_user((int *) arg, &tmp, sizeof(int))) | ||
225 | return -EFAULT; | ||
226 | break; | ||
227 | case TIMERIRQ_START: | ||
228 | data->period = arg; | ||
229 | retval = start_timerirq(data); | ||
230 | break; | ||
231 | case TIMERIRQ_STOP: | ||
232 | retval = stop_timerirq(data); | ||
233 | break; | ||
234 | default: | ||
235 | retval = -EINVAL; | ||
236 | } | ||
237 | return retval; | ||
238 | } | ||
239 | |||
240 | /* define which file operations are supported */ | ||
241 | static const struct file_operations timerirq_fops = { | ||
242 | .owner = THIS_MODULE, | ||
243 | .read = timerirq_read, | ||
244 | .poll = timerirq_poll, | ||
245 | |||
246 | #if HAVE_COMPAT_IOCTL | ||
247 | .compat_ioctl = timerirq_ioctl, | ||
248 | #endif | ||
249 | #if HAVE_UNLOCKED_IOCTL | ||
250 | .unlocked_ioctl = timerirq_ioctl, | ||
251 | #endif | ||
252 | .open = timerirq_open, | ||
253 | .release = timerirq_release, | ||
254 | }; | ||
255 | |||
256 | static int __init timerirq_init(void) | ||
257 | { | ||
258 | |||
259 | int res; | ||
260 | static struct miscdevice *data; | ||
261 | |||
262 | data = kzalloc(sizeof(*data), GFP_KERNEL); | ||
263 | if (!data) | ||
264 | return -ENOMEM; | ||
265 | timerirq_dev_data = data; | ||
266 | data->minor = MISC_DYNAMIC_MINOR; | ||
267 | data->name = "timerirq"; | ||
268 | data->fops = &timerirq_fops; | ||
269 | |||
270 | res = misc_register(data); | ||
271 | if (res < 0) { | ||
272 | dev_err(data->this_device, | ||
273 | "misc_register returned %d\n", | ||
274 | res); | ||
275 | return res; | ||
276 | } | ||
277 | |||
278 | return res; | ||
279 | } | ||
280 | module_init(timerirq_init); | ||
281 | |||
282 | static void __exit timerirq_exit(void) | ||
283 | { | ||
284 | struct miscdevice *data = timerirq_dev_data; | ||
285 | |||
286 | dev_info(data->this_device, "Unregistering %s\n", | ||
287 | data->name); | ||
288 | |||
289 | misc_deregister(data); | ||
290 | kfree(data); | ||
291 | |||
292 | timerirq_dev_data = NULL; | ||
293 | } | ||
294 | module_exit(timerirq_exit); | ||
295 | |||
296 | MODULE_AUTHOR("Invensense Corporation"); | ||
297 | MODULE_DESCRIPTION("Timer IRQ device driver."); | ||
298 | MODULE_LICENSE("GPL"); | ||
299 | MODULE_ALIAS("timerirq"); | ||