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/slaveirq.c | |
parent | 8dea78da5cee153b8af9c07a2745f6c55057fe12 (diff) |
Diffstat (limited to 'drivers/misc/mpu3050/slaveirq.c')
-rw-r--r-- | drivers/misc/mpu3050/slaveirq.c | 273 |
1 files changed, 273 insertions, 0 deletions
diff --git a/drivers/misc/mpu3050/slaveirq.c b/drivers/misc/mpu3050/slaveirq.c new file mode 100644 index 00000000000..a3c7bfec4b4 --- /dev/null +++ b/drivers/misc/mpu3050/slaveirq.c | |||
@@ -0,0 +1,273 @@ | |||
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/irq.h> | ||
26 | #include <linux/signal.h> | ||
27 | #include <linux/miscdevice.h> | ||
28 | #include <linux/i2c.h> | ||
29 | #include <linux/i2c-dev.h> | ||
30 | #include <linux/poll.h> | ||
31 | |||
32 | #include <linux/errno.h> | ||
33 | #include <linux/fs.h> | ||
34 | #include <linux/mm.h> | ||
35 | #include <linux/sched.h> | ||
36 | #include <linux/wait.h> | ||
37 | #include <linux/uaccess.h> | ||
38 | #include <linux/io.h> | ||
39 | #include <linux/wait.h> | ||
40 | #include <linux/slab.h> | ||
41 | |||
42 | #include "mpu.h" | ||
43 | #include "slaveirq.h" | ||
44 | #include "mldl_cfg.h" | ||
45 | #include "mpu-i2c.h" | ||
46 | |||
47 | /* function which gets slave data and sends it to SLAVE */ | ||
48 | |||
49 | struct slaveirq_dev_data { | ||
50 | struct miscdevice dev; | ||
51 | struct i2c_client *slave_client; | ||
52 | struct mpuirq_data data; | ||
53 | wait_queue_head_t slaveirq_wait; | ||
54 | int irq; | ||
55 | int pid; | ||
56 | int data_ready; | ||
57 | int timeout; | ||
58 | }; | ||
59 | |||
60 | /* The following depends on patch fa1f68db6ca7ebb6fc4487ac215bffba06c01c28 | ||
61 | * drivers: misc: pass miscdevice pointer via file private data | ||
62 | */ | ||
63 | static int slaveirq_open(struct inode *inode, struct file *file) | ||
64 | { | ||
65 | /* Device node is availabe in the file->private_data, this is | ||
66 | * exactly what we want so we leave it there */ | ||
67 | struct slaveirq_dev_data *data = | ||
68 | container_of(file->private_data, struct slaveirq_dev_data, dev); | ||
69 | |||
70 | dev_dbg(data->dev.this_device, | ||
71 | "%s current->pid %d\n", __func__, current->pid); | ||
72 | data->pid = current->pid; | ||
73 | return 0; | ||
74 | } | ||
75 | |||
76 | static int slaveirq_release(struct inode *inode, struct file *file) | ||
77 | { | ||
78 | struct slaveirq_dev_data *data = | ||
79 | container_of(file->private_data, struct slaveirq_dev_data, dev); | ||
80 | dev_dbg(data->dev.this_device, "slaveirq_release\n"); | ||
81 | return 0; | ||
82 | } | ||
83 | |||
84 | /* read function called when from /dev/slaveirq is read */ | ||
85 | static ssize_t slaveirq_read(struct file *file, | ||
86 | char *buf, size_t count, loff_t *ppos) | ||
87 | { | ||
88 | int len, err; | ||
89 | struct slaveirq_dev_data *data = | ||
90 | container_of(file->private_data, struct slaveirq_dev_data, dev); | ||
91 | |||
92 | if (!data->data_ready && | ||
93 | data->timeout && | ||
94 | !(file->f_flags & O_NONBLOCK)) { | ||
95 | wait_event_interruptible_timeout(data->slaveirq_wait, | ||
96 | data->data_ready, | ||
97 | data->timeout); | ||
98 | } | ||
99 | |||
100 | if (data->data_ready && NULL != buf | ||
101 | && count >= sizeof(data->data)) { | ||
102 | err = copy_to_user(buf, &data->data, sizeof(data->data)); | ||
103 | data->data.data_type = 0; | ||
104 | } else { | ||
105 | return 0; | ||
106 | } | ||
107 | if (err != 0) { | ||
108 | dev_err(data->dev.this_device, | ||
109 | "Copy to user returned %d\n", err); | ||
110 | return -EFAULT; | ||
111 | } | ||
112 | data->data_ready = 0; | ||
113 | len = sizeof(data->data); | ||
114 | return len; | ||
115 | } | ||
116 | |||
117 | static unsigned int slaveirq_poll(struct file *file, | ||
118 | struct poll_table_struct *poll) | ||
119 | { | ||
120 | int mask = 0; | ||
121 | struct slaveirq_dev_data *data = | ||
122 | container_of(file->private_data, struct slaveirq_dev_data, dev); | ||
123 | |||
124 | poll_wait(file, &data->slaveirq_wait, poll); | ||
125 | if (data->data_ready) | ||
126 | mask |= POLLIN | POLLRDNORM; | ||
127 | return mask; | ||
128 | } | ||
129 | |||
130 | /* ioctl - I/O control */ | ||
131 | static long slaveirq_ioctl(struct file *file, | ||
132 | unsigned int cmd, unsigned long arg) | ||
133 | { | ||
134 | int retval = 0; | ||
135 | int tmp; | ||
136 | struct slaveirq_dev_data *data = | ||
137 | container_of(file->private_data, struct slaveirq_dev_data, dev); | ||
138 | |||
139 | switch (cmd) { | ||
140 | case SLAVEIRQ_SET_TIMEOUT: | ||
141 | data->timeout = arg; | ||
142 | break; | ||
143 | |||
144 | case SLAVEIRQ_GET_INTERRUPT_CNT: | ||
145 | tmp = data->data.interruptcount - 1; | ||
146 | if (data->data.interruptcount > 1) | ||
147 | data->data.interruptcount = 1; | ||
148 | |||
149 | if (copy_to_user((int *) arg, &tmp, sizeof(int))) | ||
150 | return -EFAULT; | ||
151 | break; | ||
152 | case SLAVEIRQ_GET_IRQ_TIME: | ||
153 | if (copy_to_user((int *) arg, &data->data.irqtime, | ||
154 | sizeof(data->data.irqtime))) | ||
155 | return -EFAULT; | ||
156 | data->data.irqtime = 0; | ||
157 | break; | ||
158 | default: | ||
159 | retval = -EINVAL; | ||
160 | } | ||
161 | return retval; | ||
162 | } | ||
163 | |||
164 | static irqreturn_t slaveirq_handler(int irq, void *dev_id) | ||
165 | { | ||
166 | struct slaveirq_dev_data *data = (struct slaveirq_dev_data *)dev_id; | ||
167 | static int mycount; | ||
168 | struct timeval irqtime; | ||
169 | mycount++; | ||
170 | |||
171 | data->data.interruptcount++; | ||
172 | |||
173 | /* wake up (unblock) for reading data from userspace */ | ||
174 | data->data_ready = 1; | ||
175 | |||
176 | do_gettimeofday(&irqtime); | ||
177 | data->data.irqtime = (((long long) irqtime.tv_sec) << 32); | ||
178 | data->data.irqtime += irqtime.tv_usec; | ||
179 | data->data.data_type |= 1; | ||
180 | |||
181 | wake_up_interruptible(&data->slaveirq_wait); | ||
182 | |||
183 | return IRQ_HANDLED; | ||
184 | |||
185 | } | ||
186 | |||
187 | /* define which file operations are supported */ | ||
188 | static const struct file_operations slaveirq_fops = { | ||
189 | .owner = THIS_MODULE, | ||
190 | .read = slaveirq_read, | ||
191 | .poll = slaveirq_poll, | ||
192 | |||
193 | #if HAVE_COMPAT_IOCTL | ||
194 | .compat_ioctl = slaveirq_ioctl, | ||
195 | #endif | ||
196 | #if HAVE_UNLOCKED_IOCTL | ||
197 | .unlocked_ioctl = slaveirq_ioctl, | ||
198 | #endif | ||
199 | .open = slaveirq_open, | ||
200 | .release = slaveirq_release, | ||
201 | }; | ||
202 | |||
203 | int slaveirq_init(struct i2c_adapter *slave_adapter, | ||
204 | struct ext_slave_platform_data *pdata, | ||
205 | char *name) | ||
206 | { | ||
207 | |||
208 | int res; | ||
209 | struct slaveirq_dev_data *data; | ||
210 | |||
211 | if (!pdata->irq) | ||
212 | return -EINVAL; | ||
213 | |||
214 | pdata->irq_data = kzalloc(sizeof(*data), | ||
215 | GFP_KERNEL); | ||
216 | data = (struct slaveirq_dev_data *) pdata->irq_data; | ||
217 | if (!data) | ||
218 | return -ENOMEM; | ||
219 | |||
220 | data->dev.minor = MISC_DYNAMIC_MINOR; | ||
221 | data->dev.name = name; | ||
222 | data->dev.fops = &slaveirq_fops; | ||
223 | data->irq = pdata->irq; | ||
224 | data->pid = 0; | ||
225 | data->data_ready = 0; | ||
226 | data->timeout = 0; | ||
227 | |||
228 | init_waitqueue_head(&data->slaveirq_wait); | ||
229 | |||
230 | res = request_irq(data->irq, slaveirq_handler, IRQF_TRIGGER_RISING, | ||
231 | data->dev.name, data); | ||
232 | |||
233 | if (res) { | ||
234 | dev_err(&slave_adapter->dev, | ||
235 | "myirqtest: cannot register IRQ %d\n", | ||
236 | data->irq); | ||
237 | goto out_request_irq; | ||
238 | } | ||
239 | |||
240 | res = misc_register(&data->dev); | ||
241 | if (res < 0) { | ||
242 | dev_err(&slave_adapter->dev, | ||
243 | "misc_register returned %d\n", | ||
244 | res); | ||
245 | goto out_misc_register; | ||
246 | } | ||
247 | |||
248 | return res; | ||
249 | |||
250 | out_misc_register: | ||
251 | free_irq(data->irq, data); | ||
252 | out_request_irq: | ||
253 | kfree(pdata->irq_data); | ||
254 | pdata->irq_data = NULL; | ||
255 | |||
256 | return res; | ||
257 | } | ||
258 | |||
259 | void slaveirq_exit(struct ext_slave_platform_data *pdata) | ||
260 | { | ||
261 | struct slaveirq_dev_data *data = pdata->irq_data; | ||
262 | |||
263 | if (!pdata->irq_data || data->irq <= 0) | ||
264 | return; | ||
265 | |||
266 | dev_info(data->dev.this_device, "Unregistering %s\n", | ||
267 | data->dev.name); | ||
268 | |||
269 | free_irq(data->irq, data); | ||
270 | misc_deregister(&data->dev); | ||
271 | kfree(pdata->irq_data); | ||
272 | pdata->irq_data = NULL; | ||
273 | } | ||