vsense: add new modes of operation
[pandora-kernel.git] / drivers / input / misc / vsense.c
1 /*
2         vsense.c
3
4         Written by GraÅžvydas Ignotas <notasas@gmail.com>
5
6         This program is free software; you can redistribute it and/or modify
7         it under the terms of the GNU General Public License as published by
8         the Free Software Foundation; version 2 of the License.
9 */
10
11 #include <linux/kernel.h>
12 #include <linux/init.h>
13 #include <linux/module.h>
14 #include <linux/i2c.h>
15 #include <linux/input.h>
16 #include <linux/interrupt.h>
17 #include <linux/timer.h>
18 #include <linux/uaccess.h>
19 #include <linux/ctype.h>
20 #include <linux/proc_fs.h>
21 #include <linux/i2c/vsense.h>
22 #include <linux/gpio.h>
23
24 #define VSENSE_MODE_ABS         0
25 #define VSENSE_MODE_MOUSE       1
26 #define VSENSE_MODE_SCROLL      2
27
28 /* hack for Pandora: keep track of usage to prevent reset
29  * while other nub is in use
30  */
31 static int reference_count;
32
33 struct vsense_drvdata {
34         struct input_dev *input;
35         struct i2c_client *client;
36         struct delayed_work work;
37         int reset_gpio;
38         int irq_gpio;
39         int mode;
40         int scroll_counter;
41         char dev_name[12];
42 };
43
44 static void vsense_work(struct work_struct *work)
45 {
46         struct vsense_drvdata *ddata;
47         int ax = 0, ay = 0, rx = 0, ry = 0;
48         signed char buff[8];
49         int ret;
50
51         ddata = container_of(work, struct vsense_drvdata, work.work);
52
53         if (unlikely(gpio_get_value(ddata->irq_gpio)))
54                 goto dosync;
55
56         ret = i2c_master_recv(ddata->client, buff, 8);
57         if (unlikely(ret != 8)) {
58                 dev_err(&ddata->client->dev, "read failed with %i\n", ret);
59                 goto dosync;
60         }
61
62         rx = (signed int)buff[0];
63         ry = (signed int)buff[1];
64         ax = (signed int)buff[2];
65         ay = (signed int)buff[3];
66
67         schedule_delayed_work(&ddata->work, msecs_to_jiffies(25));
68
69 dosync:
70         switch (ddata->mode) {
71         case VSENSE_MODE_MOUSE:
72                 input_report_rel(ddata->input, REL_X, rx);
73                 input_report_rel(ddata->input, REL_Y, -ry);
74                 break;
75         case VSENSE_MODE_SCROLL:
76                 if (ddata->scroll_counter++ % 16 != 0)
77                         return;
78                 if (ax < 0)
79                         ax = ax < -8 ? ax / 8 : -1;
80                 else if (ax > 0)
81                         ax = ax >  8 ? ax / 8 :  1;
82                 if (ay < 0)
83                         ay = ay < -8 ? ay / 8 : -1;
84                 else if (ay > 0)
85                         ay = ay >  8 ? ay / 8 :  1;
86                 input_report_rel(ddata->input, REL_HWHEEL, ax);
87                 input_report_rel(ddata->input, REL_WHEEL, -ay);
88                 break;
89         default:
90                 input_report_abs(ddata->input, ABS_X, ax * 8);
91                 input_report_abs(ddata->input, ABS_Y, ay * 8);
92                 break;
93         }
94         input_sync(ddata->input);
95 }
96
97 static irqreturn_t vsense_isr(int irq, void *dev_id)
98 {
99         struct vsense_drvdata *ddata = dev_id;
100
101         schedule_delayed_work(&ddata->work, 0);
102
103         return IRQ_HANDLED;
104 }
105
106 static int vsense_reset(struct input_dev *dev, int val)
107 {
108         struct vsense_drvdata *ddata;
109         int ret;
110
111         ddata = input_get_drvdata(dev);
112
113         dev_dbg(&dev->dev, "vsense_reset: %i\n", val);
114
115         ret = gpio_request(ddata->reset_gpio, "vsense reset");
116         if (ret < 0) {
117                 dev_err(&dev->dev, "failed to request GPIO %d,"
118                                 " error %d\n", ddata->reset_gpio, ret);
119                 return ret;
120         }
121
122         ret = gpio_direction_output(ddata->reset_gpio, val);
123         if (ret < 0) {
124                 dev_err(&dev->dev, "failed to configure input direction "
125                         "for GPIO %d, error %d\n", ddata->reset_gpio, ret);
126         }
127
128         gpio_free(ddata->reset_gpio);
129         return ret;
130 }
131
132 static int vsense_open(struct input_dev *dev)
133 {
134         dev_dbg(&dev->dev, "vsense_open\n");
135
136         if (reference_count++ == 0)
137                 vsense_reset(dev, 0);
138
139         return 0;
140 }
141
142 static void vsense_close(struct input_dev *dev)
143 {
144         dev_dbg(&dev->dev, "vsense_close\n");
145
146         if (--reference_count == 0)
147                 vsense_reset(dev, 1);
148         BUG_ON(reference_count < 0);
149 }
150
151 static int vsense_input_register(struct vsense_drvdata *ddata, int mode)
152 {
153         struct input_dev *input;
154         int ret;
155
156         input = input_allocate_device();
157         if (input == NULL)
158                 return -ENOMEM;
159
160         if (mode != VSENSE_MODE_ABS) {
161                 /* pretend to be a mouse */
162                 input_set_capability(input, EV_REL, REL_X);
163                 input_set_capability(input, EV_REL, REL_Y);
164                 input_set_capability(input, EV_REL, REL_WHEEL);
165                 input_set_capability(input, EV_REL, REL_HWHEEL);
166                 /* add fake buttons to fool X that this is a mouse */
167                 input_set_capability(input, EV_KEY, BTN_LEFT);
168                 input_set_capability(input, EV_KEY, BTN_RIGHT);
169         } else {
170                 input->evbit[BIT_WORD(EV_ABS)] = BIT_MASK(EV_ABS);
171                 input_set_abs_params(input, ABS_X, -256, 256, 0, 0);
172                 input_set_abs_params(input, ABS_Y, -256, 256, 0, 0);
173         }
174
175         input->name = ddata->dev_name;
176         input->dev.parent = &ddata->client->dev;
177
178         input->id.bustype = BUS_I2C;
179         input->id.version = 0x0091;
180
181         input->open = vsense_open;
182         input->close = vsense_close;
183
184         ddata->input = input;
185         input_set_drvdata(input, ddata);
186
187         ret = input_register_device(input);
188         if (ret) {
189                 dev_err(&ddata->client->dev, "failed to register input device,"
190                         " error %d\n", ret);
191                 input_free_device(input);
192                 return ret;
193         }
194
195         ddata->mode = mode;
196         return 0;
197 }
198
199 static void vsense_input_unregister(struct vsense_drvdata *ddata)
200 {
201         cancel_delayed_work_sync(&ddata->work);
202         input_unregister_device(ddata->input);
203 }
204
205 static int vsense_proc_read(char *page, char **start, off_t off, int count,
206                 int *eof, void *data)
207 {
208         struct vsense_drvdata *ddata = data;
209         char *p = page;
210         int len;
211
212         switch (ddata->mode) {
213         case VSENSE_MODE_MOUSE:
214                 len = sprintf(p, "mouse\n");
215                 break;
216         case VSENSE_MODE_SCROLL:
217                 len = sprintf(p, "scroll\n");
218                 break;
219         default:
220                 len = sprintf(p, "absolute\n");
221                 break;
222         }
223
224         *eof = 1;
225         return len + 1;
226 }
227
228 static int vsense_proc_write(struct file *file, const char __user *buffer,
229                 unsigned long count, void *data)
230 {
231         struct vsense_drvdata *ddata = data;
232         int mode = ddata->mode;
233         char buff[32], *p;
234         int ret;
235
236         count = strncpy_from_user(buff, buffer,
237                         count < sizeof(buff) ? count : sizeof(buff) - 1);
238         buff[count] = 0;
239
240         p = buff + strlen(buff) - 1;
241         while (p > buff && isspace(*p))
242                 p--;
243         p[1] = 0;
244
245         if (strcasecmp(buff, "mouse") == 0)
246                 mode = VSENSE_MODE_MOUSE;
247         else if (strcasecmp(buff, "scroll") == 0)
248                 mode = VSENSE_MODE_SCROLL;
249         else if (strcasecmp(buff, "absolute") == 0)
250                 mode = VSENSE_MODE_ABS;
251         else
252                 dev_err(&ddata->client->dev, "unknown mode: %s\n", buff);
253
254         if (mode != ddata->mode) {
255                 disable_irq(ddata->client->irq);
256                 vsense_input_unregister(ddata);
257                 ret = vsense_input_register(ddata, mode);
258                 if (ret)
259                         dev_err(&ddata->client->dev, "failed to re-register "
260                                 "input as %d, code %d\n", mode, ret);
261                 else
262                         enable_irq(ddata->client->irq);
263         }
264
265         return count;
266 }
267
268 static int vsense_probe(struct i2c_client *client,
269                          const struct i2c_device_id *id)
270 {
271         struct vsense_platform_data *pdata = client->dev.platform_data;
272         struct vsense_drvdata *ddata;
273         struct proc_dir_entry *pret;
274         char buff[32];
275         int ret;
276
277         if (pdata == NULL) {
278                 dev_err(&client->dev, "no platform data?\n");
279                 return -EINVAL;
280         }
281
282         if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C) == 0) {
283                 dev_err(&client->dev, "can't talk I2C?\n");
284                 return -EIO;
285         }
286
287         ddata = kzalloc(sizeof(struct vsense_drvdata), GFP_KERNEL);
288         if (ddata == NULL)
289                 return -ENOMEM;
290
291         snprintf(ddata->dev_name, sizeof(ddata->dev_name),
292                 "vsense%02x", client->addr);
293
294         ret = gpio_request(pdata->gpio_irq, client->name);
295         if (ret < 0) {
296                 dev_err(&client->dev, "failed to request GPIO %d,"
297                         " error %d\n", pdata->gpio_irq, ret);
298                 goto fail0;
299         }
300
301         ret = gpio_direction_input(pdata->gpio_irq);
302         if (ret < 0) {
303                 dev_err(&client->dev, "failed to configure input direction "
304                         "for GPIO %d, error %d\n", pdata->gpio_irq, ret);
305                 goto fail1;
306         }
307
308         ret = gpio_to_irq(pdata->gpio_irq);
309         if (ret < 0) {
310                 dev_err(&client->dev, "unable to get irq number for GPIO %d, "
311                         "error %d\n", pdata->gpio_irq, ret);
312                 goto fail1;
313         }
314         client->irq = ret;
315
316         INIT_DELAYED_WORK(&ddata->work, vsense_work);
317         ddata->mode = VSENSE_MODE_ABS;
318         ddata->client = client;
319         ddata->reset_gpio = pdata->gpio_reset;
320         ddata->irq_gpio = pdata->gpio_irq;
321         i2c_set_clientdata(client, ddata);
322
323         ret = vsense_input_register(ddata, ddata->mode);
324         if (ret) {
325                 dev_err(&client->dev, "failed to register input device, "
326                         "error %d\n", ret);
327                 goto fail1;
328         }
329
330         ret = request_irq(client->irq, vsense_isr,
331                         IRQF_SAMPLE_RANDOM | IRQF_TRIGGER_FALLING,
332                         client->name, ddata);
333         if (ret) {
334                 dev_err(&client->dev, "unable to claim irq %d, error %d\n",
335                         client->irq, ret);
336                 goto fail1;
337         }
338
339         dev_dbg(&client->dev, "probe %02x, gpio %i, irq %i, \"%s\"\n",
340                 client->addr, pdata->gpio_irq, client->irq, client->name);
341
342         snprintf(buff, sizeof(buff), "pandora/vsense%02x", client->addr);
343         pret = create_proc_entry(buff, S_IWUGO | S_IRUGO, NULL);
344         if (pret == NULL) {
345                 proc_mkdir("pandora", NULL);
346                 pret = create_proc_entry(buff, S_IWUSR | S_IRUGO, NULL);
347                 if (pret == NULL)
348                         dev_err(&client->dev, "can't create proc file");
349         }
350
351         pret->data = ddata;
352         pret->read_proc = vsense_proc_read;
353         pret->write_proc = vsense_proc_write;
354
355         return 0;
356
357 fail1:
358         gpio_free(pdata->gpio_irq);
359 fail0:
360         kfree(ddata);
361         return ret;
362 }
363
364 static int __devexit vsense_remove(struct i2c_client *client)
365 {
366         struct vsense_drvdata *ddata;
367         char buff[32];
368
369         dev_dbg(&client->dev, "remove\n");
370
371         ddata = i2c_get_clientdata(client);
372
373         snprintf(buff, sizeof(buff), "pandora/vsense%02x", client->addr);
374         remove_proc_entry(buff, NULL);
375         free_irq(client->irq, ddata);
376         vsense_input_unregister(ddata);
377         gpio_free(ddata->irq_gpio);
378         kfree(ddata);
379
380         return 0;
381 }
382
383 static const struct i2c_device_id vsense_id[] = {
384         { "vsense", 0 },
385         { }
386 };
387
388 static struct i2c_driver vsense_driver = {
389         .driver = {
390                 .name   = "vsense",
391         },
392         .probe          = vsense_probe,
393         .remove         = __devexit_p(vsense_remove),
394         .id_table       = vsense_id,
395 };
396
397 static int __init vsense_init(void)
398 {
399         return i2c_add_driver(&vsense_driver);
400 }
401
402 static void __exit vsense_exit(void)
403 {
404         i2c_del_driver(&vsense_driver);
405 }
406
407
408 MODULE_AUTHOR("Grazvydas Ignotas");
409 MODULE_DESCRIPTION("VSense navigation device driver");
410 MODULE_LICENSE("GPL");
411
412 module_init(vsense_init);
413 module_exit(vsense_exit);