1/*
2 * $Id: mousedev.c,v 1.1.1.1 2008/10/15 03:26:33 james26_jang Exp $
3 *
4 *  Copyright (c) 1999-2000 Vojtech Pavlik
5 *
6 *  Input driver to ImExPS/2 device driver module.
7 *
8 *  Sponsored by SuSE
9 */
10
11/*
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
16 *
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20 * GNU General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
25 *
26 * Should you need to contact me, the author, you can do so either by
27 * e-mail - mail your message to <vojtech@suse.cz>, or by paper mail:
28 * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic
29 */
30
31#define MOUSEDEV_MINOR_BASE 	32
32#define MOUSEDEV_MINORS		32
33#define MOUSEDEV_MIX		31
34
35#include <linux/slab.h>
36#include <linux/poll.h>
37#include <linux/module.h>
38#include <linux/init.h>
39#include <linux/input.h>
40#include <linux/config.h>
41#include <linux/smp_lock.h>
42#include <linux/random.h>
43
44#ifndef CONFIG_INPUT_MOUSEDEV_SCREEN_X
45#define CONFIG_INPUT_MOUSEDEV_SCREEN_X	1024
46#endif
47#ifndef CONFIG_INPUT_MOUSEDEV_SCREEN_Y
48#define CONFIG_INPUT_MOUSEDEV_SCREEN_Y	768
49#endif
50
51struct mousedev {
52	int exist;
53	int open;
54	int minor;
55	wait_queue_head_t wait;
56	struct mousedev_list *list;
57	struct input_handle handle;
58	devfs_handle_t devfs;
59};
60
61struct mousedev_list {
62	struct fasync_struct *fasync;
63	struct mousedev *mousedev;
64	struct mousedev_list *next;
65	int dx, dy, dz, oldx, oldy;
66	signed char ps2[6];
67	unsigned long buttons;
68	unsigned char ready, buffer, bufsiz;
69	unsigned char mode, imexseq, impsseq;
70};
71
72#define MOUSEDEV_SEQ_LEN	6
73
74static unsigned char mousedev_imps_seq[] = { 0xf3, 200, 0xf3, 100, 0xf3, 80 };
75static unsigned char mousedev_imex_seq[] = { 0xf3, 200, 0xf3, 200, 0xf3, 80 };
76
77static struct input_handler mousedev_handler;
78
79static struct mousedev *mousedev_table[MOUSEDEV_MINORS];
80static struct mousedev mousedev_mix;
81
82static int xres = CONFIG_INPUT_MOUSEDEV_SCREEN_X;
83static int yres = CONFIG_INPUT_MOUSEDEV_SCREEN_Y;
84
85static void mousedev_event(struct input_handle *handle, unsigned int type, unsigned int code, int value)
86{
87	struct mousedev *mousedevs[3] = { handle->private, &mousedev_mix, NULL };
88	struct mousedev **mousedev = mousedevs;
89	struct mousedev_list *list;
90	int index, size;
91
92	add_mouse_randomness((type << 4) ^ code ^ (code >> 4) ^ value);
93
94	while (*mousedev) {
95		list = (*mousedev)->list;
96		while (list) {
97			switch (type) {
98				case EV_ABS:
99					if (test_bit(BTN_TRIGGER, handle->dev->keybit))
100						break;
101					switch (code) {
102						case ABS_X:
103							size = handle->dev->absmax[ABS_X] - handle->dev->absmin[ABS_X];
104							list->dx += (value * xres - list->oldx) / size;
105							list->oldx += list->dx * size;
106							break;
107						case ABS_Y:
108							size = handle->dev->absmax[ABS_Y] - handle->dev->absmin[ABS_Y];
109							list->dy -= (value * yres - list->oldy) / size;
110							list->oldy -= list->dy * size;
111							break;
112					}
113					break;
114
115				case EV_REL:
116					switch (code) {
117						case REL_X:	list->dx += value; break;
118						case REL_Y:	list->dy -= value; break;
119						case REL_WHEEL:	if (list->mode) list->dz -= value; break;
120					}
121					break;
122
123				case EV_KEY:
124					switch (code) {
125						case BTN_0:
126						case BTN_TOUCH:
127						case BTN_LEFT:   index = 0; break;
128						case BTN_4:
129						case BTN_EXTRA:  if (list->mode == 2) { index = 4; break; }
130						case BTN_STYLUS:
131						case BTN_1:
132						case BTN_RIGHT:  index = 1; break;
133						case BTN_3:
134						case BTN_SIDE:   if (list->mode == 2) { index = 3; break; }
135						case BTN_2:
136						case BTN_STYLUS2:
137						case BTN_MIDDLE: index = 2; break;
138						default: return;
139					}
140					switch (value) {
141						case 0: clear_bit(index, &list->buttons); break;
142						case 1: set_bit(index, &list->buttons); break;
143						case 2: return;
144					}
145					break;
146			}
147
148			list->ready = 1;
149
150			kill_fasync(&list->fasync, SIGIO, POLL_IN);
151
152			list = list->next;
153		}
154
155		wake_up_interruptible(&((*mousedev)->wait));
156		mousedev++;
157	}
158}
159
160static int mousedev_fasync(int fd, struct file *file, int on)
161{
162	int retval;
163	struct mousedev_list *list = file->private_data;
164	retval = fasync_helper(fd, file, on, &list->fasync);
165	return retval < 0 ? retval : 0;
166}
167
168static int mousedev_release(struct inode * inode, struct file * file)
169{
170	struct mousedev_list *list = file->private_data;
171	struct mousedev_list **listptr;
172
173	lock_kernel();
174	listptr = &list->mousedev->list;
175	mousedev_fasync(-1, file, 0);
176
177	while (*listptr && (*listptr != list))
178		listptr = &((*listptr)->next);
179	*listptr = (*listptr)->next;
180
181	if (!--list->mousedev->open) {
182		if (list->mousedev->minor == MOUSEDEV_MIX) {
183			struct input_handle *handle = mousedev_handler.handle;
184			while (handle) {
185				struct mousedev *mousedev = handle->private;
186				if (!mousedev->open) {
187					if (mousedev->exist) {
188						input_close_device(&mousedev->handle);
189					} else {
190						input_unregister_minor(mousedev->devfs);
191						mousedev_table[mousedev->minor] = NULL;
192						kfree(mousedev);
193					}
194				}
195				handle = handle->hnext;
196			}
197		} else {
198			if (!mousedev_mix.open) {
199				if (list->mousedev->exist) {
200					input_close_device(&list->mousedev->handle);
201				} else {
202					input_unregister_minor(list->mousedev->devfs);
203					mousedev_table[list->mousedev->minor] = NULL;
204					kfree(list->mousedev);
205				}
206			}
207		}
208	}
209
210	kfree(list);
211	unlock_kernel();
212
213	return 0;
214}
215
216static int mousedev_open(struct inode * inode, struct file * file)
217{
218	struct mousedev_list *list;
219	int i = MINOR(inode->i_rdev) - MOUSEDEV_MINOR_BASE;
220
221	if (i >= MOUSEDEV_MINORS || !mousedev_table[i])
222		return -ENODEV;
223
224	if (!(list = kmalloc(sizeof(struct mousedev_list), GFP_KERNEL)))
225		return -ENOMEM;
226	memset(list, 0, sizeof(struct mousedev_list));
227
228	list->mousedev = mousedev_table[i];
229	list->next = mousedev_table[i]->list;
230	mousedev_table[i]->list = list;
231	file->private_data = list;
232
233	if (!list->mousedev->open++) {
234		if (list->mousedev->minor == MOUSEDEV_MIX) {
235			struct input_handle *handle = mousedev_handler.handle;
236			while (handle) {
237				struct mousedev *mousedev = handle->private;
238				if (!mousedev->open)
239					if (mousedev->exist)
240						input_open_device(handle);
241				handle = handle->hnext;
242			}
243		} else {
244			if (!mousedev_mix.open)
245				if (list->mousedev->exist)
246					input_open_device(&list->mousedev->handle);
247		}
248	}
249
250	return 0;
251}
252
253static void mousedev_packet(struct mousedev_list *list, unsigned char off)
254{
255	list->ps2[off] = 0x08 | ((list->dx < 0) << 4) | ((list->dy < 0) << 5) | (list->buttons & 0x07);
256	list->ps2[off + 1] = (list->dx > 127 ? 127 : (list->dx < -127 ? -127 : list->dx));
257	list->ps2[off + 2] = (list->dy > 127 ? 127 : (list->dy < -127 ? -127 : list->dy));
258	list->dx -= list->ps2[off + 1];
259	list->dy -= list->ps2[off + 2];
260	list->bufsiz = off + 3;
261
262	if (list->mode == 2) {
263		list->ps2[off + 3] = (list->dz > 7 ? 7 : (list->dz < -7 ? -7 : list->dz));
264		list->dz -= list->ps2[off + 3];
265		list->ps2[off + 3] = (list->ps2[off + 3] & 0x0f) | ((list->buttons & 0x18) << 1);
266		list->bufsiz++;
267	}
268
269	if (list->mode == 1) {
270		list->ps2[off + 3] = (list->dz > 127 ? 127 : (list->dz < -127 ? -127 : list->dz));
271		list->dz -= list->ps2[off + 3];
272		list->bufsiz++;
273	}
274
275	if (!list->dx && !list->dy && (!list->mode || !list->dz)) list->ready = 0;
276	list->buffer = list->bufsiz;
277}
278
279
280static ssize_t mousedev_write(struct file * file, const char * buffer, size_t count, loff_t *ppos)
281{
282	struct mousedev_list *list = file->private_data;
283	unsigned char c;
284	int i;
285
286	for (i = 0; i < count; i++) {
287
288		if (get_user(c, buffer + i))
289			return -EFAULT;
290
291		if (c == mousedev_imex_seq[list->imexseq]) {
292			if (++list->imexseq == MOUSEDEV_SEQ_LEN) {
293				list->imexseq = 0;
294				list->mode = 2;
295			}
296		} else list->imexseq = 0;
297
298		if (c == mousedev_imps_seq[list->impsseq]) {
299			if (++list->impsseq == MOUSEDEV_SEQ_LEN) {
300				list->impsseq = 0;
301				list->mode = 1;
302			}
303		} else list->impsseq = 0;
304
305		list->ps2[0] = 0xfa;
306		list->bufsiz = 1;
307		list->ready = 1;
308
309		switch (c) {
310
311			case 0xeb: /* Poll */
312				mousedev_packet(list, 1);
313				break;
314
315			case 0xf2: /* Get ID */
316				switch (list->mode) {
317					case 0: list->ps2[1] = 0; break;
318					case 1: list->ps2[1] = 3; break;
319					case 2: list->ps2[1] = 4; break;
320				}
321				list->bufsiz = 2;
322				break;
323
324			case 0xe9: /* Get info */
325				list->ps2[1] = 0x60; list->ps2[2] = 3; list->ps2[3] = 200;
326				list->bufsiz = 4;
327				break;
328		}
329
330		list->buffer = list->bufsiz;
331	}
332
333	kill_fasync(&list->fasync, SIGIO, POLL_IN);
334
335	wake_up_interruptible(&list->mousedev->wait);
336
337	return count;
338}
339
340static ssize_t mousedev_read(struct file * file, char * buffer, size_t count, loff_t *ppos)
341{
342	DECLARE_WAITQUEUE(wait, current);
343	struct mousedev_list *list = file->private_data;
344	int retval = 0;
345
346	if (!list->ready && !list->buffer) {
347
348		add_wait_queue(&list->mousedev->wait, &wait);
349		current->state = TASK_INTERRUPTIBLE;
350
351		while (!list->ready) {
352
353			if (file->f_flags & O_NONBLOCK) {
354				retval = -EAGAIN;
355				break;
356			}
357			if (signal_pending(current)) {
358				retval = -ERESTARTSYS;
359				break;
360			}
361
362			schedule();
363		}
364
365		current->state = TASK_RUNNING;
366		remove_wait_queue(&list->mousedev->wait, &wait);
367	}
368
369	if (retval)
370		return retval;
371
372	if (!list->buffer)
373		mousedev_packet(list, 0);
374
375	if (count > list->buffer)
376		count = list->buffer;
377
378	if (copy_to_user(buffer, list->ps2 + list->bufsiz - list->buffer, count))
379		return -EFAULT;
380
381	list->buffer -= count;
382
383	return count;
384}
385
386/* No kernel lock - fine */
387static unsigned int mousedev_poll(struct file *file, poll_table *wait)
388{
389	struct mousedev_list *list = file->private_data;
390	poll_wait(file, &list->mousedev->wait, wait);
391	if (list->ready || list->buffer)
392		return POLLIN | POLLRDNORM;
393	return 0;
394}
395
396struct file_operations mousedev_fops = {
397	owner:		THIS_MODULE,
398	read:		mousedev_read,
399	write:		mousedev_write,
400	poll:		mousedev_poll,
401	open:		mousedev_open,
402	release:	mousedev_release,
403	fasync:		mousedev_fasync,
404};
405
406static struct input_handle *mousedev_connect(struct input_handler *handler, struct input_dev *dev)
407{
408	struct mousedev *mousedev;
409	int minor = 0;
410
411	if (!test_bit(EV_KEY, dev->evbit) ||
412	   (!test_bit(BTN_LEFT, dev->keybit) && !test_bit(BTN_TOUCH, dev->keybit)))
413		return NULL;
414
415	if ((!test_bit(EV_REL, dev->evbit) || !test_bit(REL_X, dev->relbit)) &&
416	    (!test_bit(EV_ABS, dev->evbit) || !test_bit(ABS_X, dev->absbit)))
417		return NULL;
418
419	for (minor = 0; minor < MOUSEDEV_MINORS && mousedev_table[minor]; minor++);
420	if (minor == MOUSEDEV_MINORS) {
421		printk(KERN_ERR "mousedev: no more free mousedev devices\n");
422		return NULL;
423	}
424
425	if (!(mousedev = kmalloc(sizeof(struct mousedev), GFP_KERNEL)))
426		return NULL;
427	memset(mousedev, 0, sizeof(struct mousedev));
428	init_waitqueue_head(&mousedev->wait);
429
430	mousedev->exist = 1;
431	mousedev->minor = minor;
432	mousedev_table[minor] = mousedev;
433
434	mousedev->handle.dev = dev;
435	mousedev->handle.handler = handler;
436	mousedev->handle.private = mousedev;
437
438	mousedev->devfs = input_register_minor("mouse%d", minor, MOUSEDEV_MINOR_BASE);
439
440	if (mousedev_mix.open)
441		input_open_device(&mousedev->handle);
442
443//	printk(KERN_INFO "mouse%d: PS/2 mouse device for input%d\n", minor, dev->number);
444
445	return &mousedev->handle;
446}
447
448static void mousedev_disconnect(struct input_handle *handle)
449{
450	struct mousedev *mousedev = handle->private;
451
452	mousedev->exist = 0;
453
454	if (mousedev->open) {
455		input_close_device(handle);
456	} else {
457		if (mousedev_mix.open)
458			input_close_device(handle);
459		input_unregister_minor(mousedev->devfs);
460		mousedev_table[mousedev->minor] = NULL;
461		kfree(mousedev);
462	}
463}
464
465static struct input_handler mousedev_handler = {
466	event:		mousedev_event,
467	connect:	mousedev_connect,
468	disconnect:	mousedev_disconnect,
469	fops:		&mousedev_fops,
470	minor:		MOUSEDEV_MINOR_BASE,
471};
472
473static int __init mousedev_init(void)
474{
475	input_register_handler(&mousedev_handler);
476
477	memset(&mousedev_mix, 0, sizeof(struct mousedev));
478	init_waitqueue_head(&mousedev_mix.wait);
479	mousedev_table[MOUSEDEV_MIX] = &mousedev_mix;
480	mousedev_mix.exist = 1;
481	mousedev_mix.minor = MOUSEDEV_MIX;
482	mousedev_mix.devfs = input_register_minor("mice", MOUSEDEV_MIX, MOUSEDEV_MINOR_BASE);
483
484	printk(KERN_INFO "mice: PS/2 mouse device common for all mice\n");
485
486	return 0;
487}
488
489static void __exit mousedev_exit(void)
490{
491	input_unregister_minor(mousedev_mix.devfs);
492	input_unregister_handler(&mousedev_handler);
493}
494
495module_init(mousedev_init);
496module_exit(mousedev_exit);
497
498MODULE_AUTHOR("Vojtech Pavlik <vojtech@suse.cz>");
499MODULE_DESCRIPTION("Input driver to PS/2 or ImPS/2 device driver");
500MODULE_LICENSE("GPL");
501
502MODULE_PARM(xres, "i");
503MODULE_PARM_DESC(xres, "Horizontal screen resolution");
504MODULE_PARM(yres, "i");
505MODULE_PARM_DESC(yres, "Vertical screen resolution");
506