aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/misc/mpu3050/timerirq.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/misc/mpu3050/timerirq.c')
-rw-r--r--drivers/misc/mpu3050/timerirq.c299
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 */
46struct 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
59static struct miscdevice *timerirq_dev_data;
60
61static 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
88static 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
111static 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 */
127static 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
146static 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 */
157static 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
188static 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 */
201static 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 */
241static 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
256static 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}
280module_init(timerirq_init);
281
282static 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}
294module_exit(timerirq_exit);
295
296MODULE_AUTHOR("Invensense Corporation");
297MODULE_DESCRIPTION("Timer IRQ device driver.");
298MODULE_LICENSE("GPL");
299MODULE_ALIAS("timerirq");