aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/misc/inv_mpu/timerirq.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/misc/inv_mpu/timerirq.c')
-rw-r--r--drivers/misc/inv_mpu/timerirq.c296
1 files changed, 296 insertions, 0 deletions
diff --git a/drivers/misc/inv_mpu/timerirq.c b/drivers/misc/inv_mpu/timerirq.c
new file mode 100644
index 00000000000..601858f9c4d
--- /dev/null
+++ b/drivers/misc/inv_mpu/timerirq.c
@@ -0,0 +1,296 @@
1/*
2 $License:
3 Copyright (C) 2011 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 <linux/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 && data->timeout &&
164 !(file->f_flags & O_NONBLOCK)) {
165 wait_event_interruptible_timeout(data->timerirq_wait,
166 data->data_ready,
167 data->timeout);
168 }
169
170 if (data->data_ready && NULL != buf && count >= sizeof(data->data)) {
171 err = copy_to_user(buf, &data->data, sizeof(data->data));
172 data->data.data_type = 0;
173 } else {
174 return 0;
175 }
176 if (err != 0) {
177 dev_err(data->dev->this_device,
178 "Copy to user returned %d\n", err);
179 return -EFAULT;
180 }
181 data->data_ready = 0;
182 len = sizeof(data->data);
183 return len;
184}
185
186static unsigned int timerirq_poll(struct file *file,
187 struct poll_table_struct *poll)
188{
189 int mask = 0;
190 struct timerirq_data *data = file->private_data;
191
192 poll_wait(file, &data->timerirq_wait, poll);
193 if (data->data_ready)
194 mask |= POLLIN | POLLRDNORM;
195 return mask;
196}
197
198/* ioctl - I/O control */
199static long timerirq_ioctl(struct file *file,
200 unsigned int cmd, unsigned long arg)
201{
202 int retval = 0;
203 int tmp;
204 struct timerirq_data *data = file->private_data;
205
206 dev_dbg(data->dev->this_device,
207 "%s current->pid %d, %d, %ld\n",
208 __func__, current->pid, cmd, arg);
209
210 if (!data)
211 return -EFAULT;
212
213 switch (cmd) {
214 case TIMERIRQ_SET_TIMEOUT:
215 data->timeout = arg;
216 break;
217 case TIMERIRQ_GET_INTERRUPT_CNT:
218 tmp = data->data.interruptcount - 1;
219 if (data->data.interruptcount > 1)
220 data->data.interruptcount = 1;
221
222 if (copy_to_user((int *)arg, &tmp, sizeof(int)))
223 return -EFAULT;
224 break;
225 case TIMERIRQ_START:
226 data->period = arg;
227 retval = start_timerirq(data);
228 break;
229 case TIMERIRQ_STOP:
230 retval = stop_timerirq(data);
231 break;
232 default:
233 retval = -EINVAL;
234 }
235 return retval;
236}
237
238/* define which file operations are supported */
239static const struct file_operations timerirq_fops = {
240 .owner = THIS_MODULE,
241 .read = timerirq_read,
242 .poll = timerirq_poll,
243
244#if HAVE_COMPAT_IOCTL
245 .compat_ioctl = timerirq_ioctl,
246#endif
247#if HAVE_UNLOCKED_IOCTL
248 .unlocked_ioctl = timerirq_ioctl,
249#endif
250 .open = timerirq_open,
251 .release = timerirq_release,
252};
253
254static int __init timerirq_init(void)
255{
256
257 int res;
258 static struct miscdevice *data;
259
260 data = kzalloc(sizeof(*data), GFP_KERNEL);
261 if (!data)
262 return -ENOMEM;
263 timerirq_dev_data = data;
264 data->minor = MISC_DYNAMIC_MINOR;
265 data->name = "timerirq";
266 data->fops = &timerirq_fops;
267
268 res = misc_register(data);
269 if (res < 0) {
270 dev_err(data->this_device, "misc_register returned %d\n", res);
271 return res;
272 }
273
274 return res;
275}
276
277module_init(timerirq_init);
278
279static void __exit timerirq_exit(void)
280{
281 struct miscdevice *data = timerirq_dev_data;
282
283 dev_info(data->this_device, "Unregistering %s\n", data->name);
284
285 misc_deregister(data);
286 kfree(data);
287
288 timerirq_dev_data = NULL;
289}
290
291module_exit(timerirq_exit);
292
293MODULE_AUTHOR("Invensense Corporation");
294MODULE_DESCRIPTION("Timer IRQ device driver.");
295MODULE_LICENSE("GPL");
296MODULE_ALIAS("timerirq");