Blame view

drivers/input/mousedev.c 26.1 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
2
3
4
5
6
7
8
9
10
  /*
   * Input driver to ExplorerPS/2 device driver module.
   *
   * Copyright (c) 1999-2002 Vojtech Pavlik
   * Copyright (c) 2004      Dmitry Torokhov
   *
   * This program is free software; you can redistribute it and/or modify
   * it under the terms of the GNU General Public License version 2 as published by
   * the Free Software Foundation.
   */
da0c49011   Joe Perches   Input: use pr_fmt...
11
  #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
4f00469c1   Dmitry Torokhov   [PATCH] Input: ki...
12
  #define MOUSEDEV_MINOR_BASE	32
c91cb7a75   Dmitry Torokhov   Input: mousedev -...
13
14
  #define MOUSEDEV_MINORS		31
  #define MOUSEDEV_MIX		63
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
15

a99bbaf5e   Alexey Dobriyan   headers: remove s...
16
  #include <linux/sched.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
17
18
19
  #include <linux/slab.h>
  #include <linux/poll.h>
  #include <linux/module.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
20
21
  #include <linux/init.h>
  #include <linux/input.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
22
23
24
  #include <linux/random.h>
  #include <linux/major.h>
  #include <linux/device.h>
7f8d4cad1   Dmitry Torokhov   Input: extend the...
25
  #include <linux/cdev.h>
987a6c029   Daniel Mack   Input: switch to ...
26
  #include <linux/kernel.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
27
28
29
30
31
32
33
34
35
36
37
38
39
  
  MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
  MODULE_DESCRIPTION("Mouse (ExplorerPS/2) device interfaces");
  MODULE_LICENSE("GPL");
  
  #ifndef CONFIG_INPUT_MOUSEDEV_SCREEN_X
  #define CONFIG_INPUT_MOUSEDEV_SCREEN_X	1024
  #endif
  #ifndef CONFIG_INPUT_MOUSEDEV_SCREEN_Y
  #define CONFIG_INPUT_MOUSEDEV_SCREEN_Y	768
  #endif
  
  static int xres = CONFIG_INPUT_MOUSEDEV_SCREEN_X;
84c12b241   Dmitry Torokhov   Input: mousedev -...
40
  module_param(xres, uint, 0644);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
41
42
43
  MODULE_PARM_DESC(xres, "Horizontal screen resolution");
  
  static int yres = CONFIG_INPUT_MOUSEDEV_SCREEN_Y;
84c12b241   Dmitry Torokhov   Input: mousedev -...
44
  module_param(yres, uint, 0644);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
45
46
47
  MODULE_PARM_DESC(yres, "Vertical screen resolution");
  
  static unsigned tap_time = 200;
84c12b241   Dmitry Torokhov   Input: mousedev -...
48
  module_param(tap_time, uint, 0644);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
49
50
51
52
53
54
55
56
57
58
  MODULE_PARM_DESC(tap_time, "Tap time for touchpads in absolute mode (msecs)");
  
  struct mousedev_hw_data {
  	int dx, dy, dz;
  	int x, y;
  	int abs_event;
  	unsigned long buttons;
  };
  
  struct mousedev {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
59
  	int open;
464b24157   Dmitry Torokhov   Input: mousedev -...
60
  	struct input_handle handle;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
61
  	wait_queue_head_t wait;
d0ffb9be8   Dmitry Torokhov   Input: handlers -...
62
  	struct list_head client_list;
464b24157   Dmitry Torokhov   Input: mousedev -...
63
64
  	spinlock_t client_lock; /* protects client_list */
  	struct mutex mutex;
9657d75c5   Dmitry Torokhov   Input: convert fr...
65
  	struct device dev;
7f8d4cad1   Dmitry Torokhov   Input: extend the...
66
  	struct cdev cdev;
20da92de8   Dmitry Torokhov   Input: change inp...
67
  	bool exist;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
68

d542ed82f   Dmitry Torokhov   Input: handlers -...
69
  	struct list_head mixdev_node;
3376b8b75   Dmitry Torokhov   Input: mousedev -...
70
  	bool opened_by_mixdev;
d542ed82f   Dmitry Torokhov   Input: handlers -...
71

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
72
73
74
75
76
  	struct mousedev_hw_data packet;
  	unsigned int pkt_count;
  	int old_x[4], old_y[4];
  	int frac_dx, frac_dy;
  	unsigned long touch;
e4dbedc7e   Dmitry Torokhov   Input: mousedev -...
77
78
79
  
  	int (*open_device)(struct mousedev *mousedev);
  	void (*close_device)(struct mousedev *mousedev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
80
81
82
83
84
85
86
87
88
89
90
91
92
93
  };
  
  enum mousedev_emul {
  	MOUSEDEV_EMUL_PS2,
  	MOUSEDEV_EMUL_IMPS,
  	MOUSEDEV_EMUL_EXPS
  };
  
  struct mousedev_motion {
  	int dx, dy, dz;
  	unsigned long buttons;
  };
  
  #define PACKET_QUEUE_LEN	16
d0ffb9be8   Dmitry Torokhov   Input: handlers -...
94
  struct mousedev_client {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
95
96
97
98
99
100
101
102
103
104
105
106
107
  	struct fasync_struct *fasync;
  	struct mousedev *mousedev;
  	struct list_head node;
  
  	struct mousedev_motion packets[PACKET_QUEUE_LEN];
  	unsigned int head, tail;
  	spinlock_t packet_lock;
  	int pos_x, pos_y;
  
  	signed char ps2[6];
  	unsigned char ready, buffer, bufsiz;
  	unsigned char imexseq, impsseq;
  	enum mousedev_emul mode;
c1e4c8d3e   Pavel Machek   [PATCH] fix jumpy...
108
  	unsigned long last_buttons;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
109
110
111
112
113
114
  };
  
  #define MOUSEDEV_SEQ_LEN	6
  
  static unsigned char mousedev_imps_seq[] = { 0xf3, 200, 0xf3, 100, 0xf3, 80 };
  static unsigned char mousedev_imex_seq[] = { 0xf3, 200, 0xf3, 200, 0xf3, 80 };
9657d75c5   Dmitry Torokhov   Input: convert fr...
115
  static struct mousedev *mousedev_mix;
d542ed82f   Dmitry Torokhov   Input: handlers -...
116
  static LIST_HEAD(mousedev_mix_list);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
117
118
119
  
  #define fx(i)  (mousedev->old_x[(mousedev->pkt_count - (i)) & 03])
  #define fy(i)  (mousedev->old_y[(mousedev->pkt_count - (i)) & 03])
464b24157   Dmitry Torokhov   Input: mousedev -...
120
121
122
  static void mousedev_touchpad_event(struct input_dev *dev,
  				    struct mousedev *mousedev,
  				    unsigned int code, int value)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
123
124
125
  {
  	int size, tmp;
  	enum { FRACTION_DENOM = 128 };
0d9d93c41   Dmitry Torokhov   Input: mousedev -...
126
  	switch (code) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
127

464b24157   Dmitry Torokhov   Input: mousedev -...
128
  	case ABS_X:
987a6c029   Daniel Mack   Input: switch to ...
129

464b24157   Dmitry Torokhov   Input: mousedev -...
130
131
  		fx(0) = value;
  		if (mousedev->touch && mousedev->pkt_count >= 2) {
268ba5c05   Christoph Fritz   Input: mousedev -...
132
133
  			size = input_abs_get_max(dev, ABS_X) -
  					input_abs_get_min(dev, ABS_X);
464b24157   Dmitry Torokhov   Input: mousedev -...
134
135
  			if (size == 0)
  				size = 256 * 2;
987a6c029   Daniel Mack   Input: switch to ...
136

464b24157   Dmitry Torokhov   Input: mousedev -...
137
138
139
140
141
142
143
144
145
146
147
  			tmp = ((value - fx(2)) * 256 * FRACTION_DENOM) / size;
  			tmp += mousedev->frac_dx;
  			mousedev->packet.dx = tmp / FRACTION_DENOM;
  			mousedev->frac_dx =
  				tmp - mousedev->packet.dx * FRACTION_DENOM;
  		}
  		break;
  
  	case ABS_Y:
  		fy(0) = value;
  		if (mousedev->touch && mousedev->pkt_count >= 2) {
987a6c029   Daniel Mack   Input: switch to ...
148
  			/* use X size for ABS_Y to keep the same scale */
268ba5c05   Christoph Fritz   Input: mousedev -...
149
150
  			size = input_abs_get_max(dev, ABS_X) -
  					input_abs_get_min(dev, ABS_X);
464b24157   Dmitry Torokhov   Input: mousedev -...
151
152
  			if (size == 0)
  				size = 256 * 2;
987a6c029   Daniel Mack   Input: switch to ...
153

464b24157   Dmitry Torokhov   Input: mousedev -...
154
155
156
157
158
159
160
  			tmp = -((value - fy(2)) * 256 * FRACTION_DENOM) / size;
  			tmp += mousedev->frac_dy;
  			mousedev->packet.dy = tmp / FRACTION_DENOM;
  			mousedev->frac_dy = tmp -
  				mousedev->packet.dy * FRACTION_DENOM;
  		}
  		break;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
161
162
  	}
  }
464b24157   Dmitry Torokhov   Input: mousedev -...
163
164
  static void mousedev_abs_event(struct input_dev *dev, struct mousedev *mousedev,
  				unsigned int code, int value)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
165
  {
987a6c029   Daniel Mack   Input: switch to ...
166
  	int min, max, size;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
167
168
  
  	switch (code) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
169

464b24157   Dmitry Torokhov   Input: mousedev -...
170
  	case ABS_X:
987a6c029   Daniel Mack   Input: switch to ...
171
172
173
174
  		min = input_abs_get_min(dev, ABS_X);
  		max = input_abs_get_max(dev, ABS_X);
  
  		size = max - min;
464b24157   Dmitry Torokhov   Input: mousedev -...
175
176
  		if (size == 0)
  			size = xres ? : 1;
987a6c029   Daniel Mack   Input: switch to ...
177

8c127f071   Hans Petter Selasky   Input: properly a...
178
  		value = clamp(value, min, max);
987a6c029   Daniel Mack   Input: switch to ...
179
180
  
  		mousedev->packet.x = ((value - min) * xres) / size;
464b24157   Dmitry Torokhov   Input: mousedev -...
181
182
183
184
  		mousedev->packet.abs_event = 1;
  		break;
  
  	case ABS_Y:
987a6c029   Daniel Mack   Input: switch to ...
185
186
187
188
  		min = input_abs_get_min(dev, ABS_Y);
  		max = input_abs_get_max(dev, ABS_Y);
  
  		size = max - min;
464b24157   Dmitry Torokhov   Input: mousedev -...
189
190
  		if (size == 0)
  			size = yres ? : 1;
987a6c029   Daniel Mack   Input: switch to ...
191

8c127f071   Hans Petter Selasky   Input: properly a...
192
  		value = clamp(value, min, max);
987a6c029   Daniel Mack   Input: switch to ...
193
194
  
  		mousedev->packet.y = yres - ((value - min) * yres) / size;
464b24157   Dmitry Torokhov   Input: mousedev -...
195
196
  		mousedev->packet.abs_event = 1;
  		break;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
197
198
  	}
  }
464b24157   Dmitry Torokhov   Input: mousedev -...
199
200
  static void mousedev_rel_event(struct mousedev *mousedev,
  				unsigned int code, int value)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
201
202
  {
  	switch (code) {
464b24157   Dmitry Torokhov   Input: mousedev -...
203
204
205
206
207
208
209
210
211
212
213
  	case REL_X:
  		mousedev->packet.dx += value;
  		break;
  
  	case REL_Y:
  		mousedev->packet.dy -= value;
  		break;
  
  	case REL_WHEEL:
  		mousedev->packet.dz -= value;
  		break;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
214
215
  	}
  }
464b24157   Dmitry Torokhov   Input: mousedev -...
216
217
  static void mousedev_key_event(struct mousedev *mousedev,
  				unsigned int code, int value)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
218
219
220
221
  {
  	int index;
  
  	switch (code) {
464b24157   Dmitry Torokhov   Input: mousedev -...
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
  
  	case BTN_TOUCH:
  	case BTN_0:
  	case BTN_LEFT:		index = 0; break;
  
  	case BTN_STYLUS:
  	case BTN_1:
  	case BTN_RIGHT:		index = 1; break;
  
  	case BTN_2:
  	case BTN_FORWARD:
  	case BTN_STYLUS2:
  	case BTN_MIDDLE:	index = 2; break;
  
  	case BTN_3:
  	case BTN_BACK:
  	case BTN_SIDE:		index = 3; break;
  
  	case BTN_4:
  	case BTN_EXTRA:		index = 4; break;
  
  	default:		return;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
244
245
246
247
  	}
  
  	if (value) {
  		set_bit(index, &mousedev->packet.buttons);
9657d75c5   Dmitry Torokhov   Input: convert fr...
248
  		set_bit(index, &mousedev_mix->packet.buttons);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
249
250
  	} else {
  		clear_bit(index, &mousedev->packet.buttons);
9657d75c5   Dmitry Torokhov   Input: convert fr...
251
  		clear_bit(index, &mousedev_mix->packet.buttons);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
252
253
  	}
  }
464b24157   Dmitry Torokhov   Input: mousedev -...
254
255
  static void mousedev_notify_readers(struct mousedev *mousedev,
  				    struct mousedev_hw_data *packet)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
256
  {
d0ffb9be8   Dmitry Torokhov   Input: handlers -...
257
  	struct mousedev_client *client;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
258
  	struct mousedev_motion *p;
464b24157   Dmitry Torokhov   Input: mousedev -...
259
  	unsigned int new_head;
8121152c1   Dmitry Torokhov   Input: mousedev -...
260
  	int wake_readers = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
261

82ba56c27   Dmitry Torokhov   Input: use full R...
262
  	rcu_read_lock();
464b24157   Dmitry Torokhov   Input: mousedev -...
263
264
265
266
  	list_for_each_entry_rcu(client, &mousedev->client_list, node) {
  
  		/* Just acquire the lock, interrupts already disabled */
  		spin_lock(&client->packet_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
267

d0ffb9be8   Dmitry Torokhov   Input: handlers -...
268
269
  		p = &client->packets[client->head];
  		if (client->ready && p->buttons != mousedev->packet.buttons) {
464b24157   Dmitry Torokhov   Input: mousedev -...
270
  			new_head = (client->head + 1) % PACKET_QUEUE_LEN;
d0ffb9be8   Dmitry Torokhov   Input: handlers -...
271
272
  			if (new_head != client->tail) {
  				p = &client->packets[client->head = new_head];
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
273
274
275
276
277
  				memset(p, 0, sizeof(struct mousedev_motion));
  			}
  		}
  
  		if (packet->abs_event) {
d0ffb9be8   Dmitry Torokhov   Input: handlers -...
278
279
280
281
  			p->dx += packet->x - client->pos_x;
  			p->dy += packet->y - client->pos_y;
  			client->pos_x = packet->x;
  			client->pos_y = packet->y;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
282
  		}
d0ffb9be8   Dmitry Torokhov   Input: handlers -...
283
  		client->pos_x += packet->dx;
464b24157   Dmitry Torokhov   Input: mousedev -...
284
285
  		client->pos_x = client->pos_x < 0 ?
  			0 : (client->pos_x >= xres ? xres : client->pos_x);
d0ffb9be8   Dmitry Torokhov   Input: handlers -...
286
  		client->pos_y += packet->dy;
464b24157   Dmitry Torokhov   Input: mousedev -...
287
288
  		client->pos_y = client->pos_y < 0 ?
  			0 : (client->pos_y >= yres ? yres : client->pos_y);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
289
290
291
292
293
  
  		p->dx += packet->dx;
  		p->dy += packet->dy;
  		p->dz += packet->dz;
  		p->buttons = mousedev->packet.buttons;
464b24157   Dmitry Torokhov   Input: mousedev -...
294
295
  		if (p->dx || p->dy || p->dz ||
  		    p->buttons != client->last_buttons)
d0ffb9be8   Dmitry Torokhov   Input: handlers -...
296
  			client->ready = 1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
297

464b24157   Dmitry Torokhov   Input: mousedev -...
298
  		spin_unlock(&client->packet_lock);
c1e4c8d3e   Pavel Machek   [PATCH] fix jumpy...
299

d0ffb9be8   Dmitry Torokhov   Input: handlers -...
300
301
  		if (client->ready) {
  			kill_fasync(&client->fasync, SIGIO, POLL_IN);
8121152c1   Dmitry Torokhov   Input: mousedev -...
302
303
  			wake_readers = 1;
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
304
  	}
82ba56c27   Dmitry Torokhov   Input: use full R...
305
  	rcu_read_unlock();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
306

8121152c1   Dmitry Torokhov   Input: mousedev -...
307
308
  	if (wake_readers)
  		wake_up_interruptible(&mousedev->wait);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
309
310
311
312
313
314
  }
  
  static void mousedev_touchpad_touch(struct mousedev *mousedev, int value)
  {
  	if (!value) {
  		if (mousedev->touch &&
464b24157   Dmitry Torokhov   Input: mousedev -...
315
316
  		    time_before(jiffies,
  				mousedev->touch + msecs_to_jiffies(tap_time))) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
317
318
319
320
321
322
  			/*
  			 * Toggle left button to emulate tap.
  			 * We rely on the fact that mousedev_mix always has 0
  			 * motion packet so we won't mess current position.
  			 */
  			set_bit(0, &mousedev->packet.buttons);
9657d75c5   Dmitry Torokhov   Input: convert fr...
323
324
  			set_bit(0, &mousedev_mix->packet.buttons);
  			mousedev_notify_readers(mousedev, &mousedev_mix->packet);
464b24157   Dmitry Torokhov   Input: mousedev -...
325
326
  			mousedev_notify_readers(mousedev_mix,
  						&mousedev_mix->packet);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
327
  			clear_bit(0, &mousedev->packet.buttons);
9657d75c5   Dmitry Torokhov   Input: convert fr...
328
  			clear_bit(0, &mousedev_mix->packet.buttons);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
329
330
331
332
  		}
  		mousedev->touch = mousedev->pkt_count = 0;
  		mousedev->frac_dx = 0;
  		mousedev->frac_dy = 0;
1e0afb288   Dmitry Torokhov   Input: fix format...
333
334
335
  
  	} else if (!mousedev->touch)
  		mousedev->touch = jiffies;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
336
  }
464b24157   Dmitry Torokhov   Input: mousedev -...
337
338
  static void mousedev_event(struct input_handle *handle,
  			   unsigned int type, unsigned int code, int value)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
339
340
341
342
  {
  	struct mousedev *mousedev = handle->private;
  
  	switch (type) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
343

464b24157   Dmitry Torokhov   Input: mousedev -...
344
345
346
347
  	case EV_ABS:
  		/* Ignore joysticks */
  		if (test_bit(BTN_TRIGGER, handle->dev->keybit))
  			return;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
348

464b24157   Dmitry Torokhov   Input: mousedev -...
349
350
351
352
353
  		if (test_bit(BTN_TOOL_FINGER, handle->dev->keybit))
  			mousedev_touchpad_event(handle->dev,
  						mousedev, code, value);
  		else
  			mousedev_abs_event(handle->dev, mousedev, code, value);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
354

464b24157   Dmitry Torokhov   Input: mousedev -...
355
  		break;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
356

464b24157   Dmitry Torokhov   Input: mousedev -...
357
358
359
  	case EV_REL:
  		mousedev_rel_event(mousedev, code, value);
  		break;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
360

464b24157   Dmitry Torokhov   Input: mousedev -...
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
  	case EV_KEY:
  		if (value != 2) {
  			if (code == BTN_TOUCH &&
  			    test_bit(BTN_TOOL_FINGER, handle->dev->keybit))
  				mousedev_touchpad_touch(mousedev, value);
  			else
  				mousedev_key_event(mousedev, code, value);
  		}
  		break;
  
  	case EV_SYN:
  		if (code == SYN_REPORT) {
  			if (mousedev->touch) {
  				mousedev->pkt_count++;
  				/*
  				 * Input system eats duplicate events,
  				 * but we need all of them to do correct
  				 * averaging so apply present one forward
  				 */
  				fx(0) = fx(1);
  				fy(0) = fy(1);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
382
  			}
464b24157   Dmitry Torokhov   Input: mousedev -...
383
384
385
386
387
388
389
390
391
  
  			mousedev_notify_readers(mousedev, &mousedev->packet);
  			mousedev_notify_readers(mousedev_mix, &mousedev->packet);
  
  			mousedev->packet.dx = mousedev->packet.dy =
  				mousedev->packet.dz = 0;
  			mousedev->packet.abs_event = 0;
  		}
  		break;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
392
393
394
395
396
  	}
  }
  
  static int mousedev_fasync(int fd, struct file *file, int on)
  {
d0ffb9be8   Dmitry Torokhov   Input: handlers -...
397
  	struct mousedev_client *client = file->private_data;
1e0afb288   Dmitry Torokhov   Input: fix format...
398

60aa49243   Jonathan Corbet   Rationalize fasyn...
399
  	return fasync_helper(fd, file, on, &client->fasync);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
400
  }
9657d75c5   Dmitry Torokhov   Input: convert fr...
401
  static void mousedev_free(struct device *dev)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
402
  {
9657d75c5   Dmitry Torokhov   Input: convert fr...
403
  	struct mousedev *mousedev = container_of(dev, struct mousedev, dev);
a7097ff89   Dmitry Torokhov   Input: make sure ...
404
  	input_put_device(mousedev->handle.dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
405
406
  	kfree(mousedev);
  }
464b24157   Dmitry Torokhov   Input: mousedev -...
407
  static int mousedev_open_device(struct mousedev *mousedev)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
408
  {
464b24157   Dmitry Torokhov   Input: mousedev -...
409
  	int retval;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
410

464b24157   Dmitry Torokhov   Input: mousedev -...
411
412
413
  	retval = mutex_lock_interruptible(&mousedev->mutex);
  	if (retval)
  		return retval;
d542ed82f   Dmitry Torokhov   Input: handlers -...
414

e4dbedc7e   Dmitry Torokhov   Input: mousedev -...
415
  	if (!mousedev->exist)
464b24157   Dmitry Torokhov   Input: mousedev -...
416
  		retval = -ENODEV;
064450140   Oliver Neukum   Input: fix open c...
417
  	else if (!mousedev->open++) {
464b24157   Dmitry Torokhov   Input: mousedev -...
418
  		retval = input_open_device(&mousedev->handle);
064450140   Oliver Neukum   Input: fix open c...
419
420
421
  		if (retval)
  			mousedev->open--;
  	}
d542ed82f   Dmitry Torokhov   Input: handlers -...
422

464b24157   Dmitry Torokhov   Input: mousedev -...
423
424
  	mutex_unlock(&mousedev->mutex);
  	return retval;
d542ed82f   Dmitry Torokhov   Input: handlers -...
425
  }
464b24157   Dmitry Torokhov   Input: mousedev -...
426
  static void mousedev_close_device(struct mousedev *mousedev)
d542ed82f   Dmitry Torokhov   Input: handlers -...
427
  {
464b24157   Dmitry Torokhov   Input: mousedev -...
428
  	mutex_lock(&mousedev->mutex);
d542ed82f   Dmitry Torokhov   Input: handlers -...
429

e4dbedc7e   Dmitry Torokhov   Input: mousedev -...
430
  	if (mousedev->exist && !--mousedev->open)
464b24157   Dmitry Torokhov   Input: mousedev -...
431
432
433
  		input_close_device(&mousedev->handle);
  
  	mutex_unlock(&mousedev->mutex);
d542ed82f   Dmitry Torokhov   Input: handlers -...
434
  }
464b24157   Dmitry Torokhov   Input: mousedev -...
435
436
437
438
439
  /*
   * Open all available devices so they can all be multiplexed in one.
   * stream. Note that this function is called with mousedev_mix->mutex
   * held.
   */
e4dbedc7e   Dmitry Torokhov   Input: mousedev -...
440
  static int mixdev_open_devices(struct mousedev *mixdev)
d542ed82f   Dmitry Torokhov   Input: handlers -...
441
  {
e4dbedc7e   Dmitry Torokhov   Input: mousedev -...
442
443
444
445
446
  	int error;
  
  	error = mutex_lock_interruptible(&mixdev->mutex);
  	if (error)
  		return error;
d542ed82f   Dmitry Torokhov   Input: handlers -...
447

e4dbedc7e   Dmitry Torokhov   Input: mousedev -...
448
449
  	if (!mixdev->open++) {
  		struct mousedev *mousedev;
9657d75c5   Dmitry Torokhov   Input: convert fr...
450

e4dbedc7e   Dmitry Torokhov   Input: mousedev -...
451
452
453
454
  		list_for_each_entry(mousedev, &mousedev_mix_list, mixdev_node) {
  			if (!mousedev->opened_by_mixdev) {
  				if (mousedev_open_device(mousedev))
  					continue;
d542ed82f   Dmitry Torokhov   Input: handlers -...
455

e4dbedc7e   Dmitry Torokhov   Input: mousedev -...
456
457
  				mousedev->opened_by_mixdev = true;
  			}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
458
459
  		}
  	}
e4dbedc7e   Dmitry Torokhov   Input: mousedev -...
460
461
462
  
  	mutex_unlock(&mixdev->mutex);
  	return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
463
  }
464b24157   Dmitry Torokhov   Input: mousedev -...
464
465
466
467
468
  /*
   * Close all devices that were opened as part of multiplexed
   * device. Note that this function is called with mousedev_mix->mutex
   * held.
   */
e4dbedc7e   Dmitry Torokhov   Input: mousedev -...
469
  static void mixdev_close_devices(struct mousedev *mixdev)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
470
  {
e4dbedc7e   Dmitry Torokhov   Input: mousedev -...
471
  	mutex_lock(&mixdev->mutex);
d542ed82f   Dmitry Torokhov   Input: handlers -...
472

e4dbedc7e   Dmitry Torokhov   Input: mousedev -...
473
474
  	if (!--mixdev->open) {
  		struct mousedev *mousedev;
9657d75c5   Dmitry Torokhov   Input: convert fr...
475

e4dbedc7e   Dmitry Torokhov   Input: mousedev -...
476
477
478
479
480
  		list_for_each_entry(mousedev, &mousedev_mix_list, mixdev_node) {
  			if (mousedev->opened_by_mixdev) {
  				mousedev->opened_by_mixdev = false;
  				mousedev_close_device(mousedev);
  			}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
481
482
  		}
  	}
e4dbedc7e   Dmitry Torokhov   Input: mousedev -...
483
484
  
  	mutex_unlock(&mixdev->mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
485
  }
464b24157   Dmitry Torokhov   Input: mousedev -...
486
487
488
489
490
491
492
  
  static void mousedev_attach_client(struct mousedev *mousedev,
  				   struct mousedev_client *client)
  {
  	spin_lock(&mousedev->client_lock);
  	list_add_tail_rcu(&client->node, &mousedev->client_list);
  	spin_unlock(&mousedev->client_lock);
464b24157   Dmitry Torokhov   Input: mousedev -...
493
494
495
496
497
498
499
500
  }
  
  static void mousedev_detach_client(struct mousedev *mousedev,
  				   struct mousedev_client *client)
  {
  	spin_lock(&mousedev->client_lock);
  	list_del_rcu(&client->node);
  	spin_unlock(&mousedev->client_lock);
82ba56c27   Dmitry Torokhov   Input: use full R...
501
  	synchronize_rcu();
464b24157   Dmitry Torokhov   Input: mousedev -...
502
  }
d0ffb9be8   Dmitry Torokhov   Input: handlers -...
503
  static int mousedev_release(struct inode *inode, struct file *file)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
504
  {
d0ffb9be8   Dmitry Torokhov   Input: handlers -...
505
506
  	struct mousedev_client *client = file->private_data;
  	struct mousedev *mousedev = client->mousedev;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
507

464b24157   Dmitry Torokhov   Input: mousedev -...
508
  	mousedev_detach_client(mousedev, client);
d0ffb9be8   Dmitry Torokhov   Input: handlers -...
509
  	kfree(client);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
510

e4dbedc7e   Dmitry Torokhov   Input: mousedev -...
511
  	mousedev->close_device(mousedev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
512

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
513
514
  	return 0;
  }
d0ffb9be8   Dmitry Torokhov   Input: handlers -...
515
  static int mousedev_open(struct inode *inode, struct file *file)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
516
  {
d0ffb9be8   Dmitry Torokhov   Input: handlers -...
517
  	struct mousedev_client *client;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
518
  	struct mousedev *mousedev;
d542ed82f   Dmitry Torokhov   Input: handlers -...
519
  	int error;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
520
521
522
  
  #ifdef CONFIG_INPUT_MOUSEDEV_PSAUX
  	if (imajor(inode) == MISC_MAJOR)
7f8d4cad1   Dmitry Torokhov   Input: extend the...
523
  		mousedev = mousedev_mix;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
524
525
  	else
  #endif
7f8d4cad1   Dmitry Torokhov   Input: extend the...
526
  		mousedev = container_of(inode->i_cdev, struct mousedev, cdev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
527

d0ffb9be8   Dmitry Torokhov   Input: handlers -...
528
  	client = kzalloc(sizeof(struct mousedev_client), GFP_KERNEL);
7f8d4cad1   Dmitry Torokhov   Input: extend the...
529
530
  	if (!client)
  		return -ENOMEM;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
531

d0ffb9be8   Dmitry Torokhov   Input: handlers -...
532
533
534
535
  	spin_lock_init(&client->packet_lock);
  	client->pos_x = xres / 2;
  	client->pos_y = yres / 2;
  	client->mousedev = mousedev;
464b24157   Dmitry Torokhov   Input: mousedev -...
536
  	mousedev_attach_client(mousedev, client);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
537

e4dbedc7e   Dmitry Torokhov   Input: mousedev -...
538
  	error = mousedev->open_device(mousedev);
464b24157   Dmitry Torokhov   Input: mousedev -...
539
540
  	if (error)
  		goto err_free_client;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
541

d0ffb9be8   Dmitry Torokhov   Input: handlers -...
542
  	file->private_data = client;
0124be497   Dmitry Torokhov   Input: mousedev -...
543
  	nonseekable_open(inode, file);
7f8d4cad1   Dmitry Torokhov   Input: extend the...
544

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
545
  	return 0;
9657d75c5   Dmitry Torokhov   Input: convert fr...
546
547
  
   err_free_client:
464b24157   Dmitry Torokhov   Input: mousedev -...
548
  	mousedev_detach_client(mousedev, client);
9657d75c5   Dmitry Torokhov   Input: convert fr...
549
  	kfree(client);
9657d75c5   Dmitry Torokhov   Input: convert fr...
550
  	return error;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
551
552
553
554
555
556
  }
  
  static inline int mousedev_limit_delta(int delta, int limit)
  {
  	return delta > limit ? limit : (delta < -limit ? -limit : delta);
  }
464b24157   Dmitry Torokhov   Input: mousedev -...
557
558
  static void mousedev_packet(struct mousedev_client *client,
  			    signed char *ps2_data)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
559
  {
464b24157   Dmitry Torokhov   Input: mousedev -...
560
  	struct mousedev_motion *p = &client->packets[client->tail];
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
561

464b24157   Dmitry Torokhov   Input: mousedev -...
562
563
  	ps2_data[0] = 0x08 |
  		((p->dx < 0) << 4) | ((p->dy < 0) << 5) | (p->buttons & 0x07);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
564
565
566
567
  	ps2_data[1] = mousedev_limit_delta(p->dx, 127);
  	ps2_data[2] = mousedev_limit_delta(p->dy, 127);
  	p->dx -= ps2_data[1];
  	p->dy -= ps2_data[2];
d0ffb9be8   Dmitry Torokhov   Input: handlers -...
568
  	switch (client->mode) {
464b24157   Dmitry Torokhov   Input: mousedev -...
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
  	case MOUSEDEV_EMUL_EXPS:
  		ps2_data[3] = mousedev_limit_delta(p->dz, 7);
  		p->dz -= ps2_data[3];
  		ps2_data[3] = (ps2_data[3] & 0x0f) | ((p->buttons & 0x18) << 1);
  		client->bufsiz = 4;
  		break;
  
  	case MOUSEDEV_EMUL_IMPS:
  		ps2_data[0] |=
  			((p->buttons & 0x10) >> 3) | ((p->buttons & 0x08) >> 1);
  		ps2_data[3] = mousedev_limit_delta(p->dz, 127);
  		p->dz -= ps2_data[3];
  		client->bufsiz = 4;
  		break;
  
  	case MOUSEDEV_EMUL_PS2:
  	default:
  		ps2_data[0] |=
  			((p->buttons & 0x10) >> 3) | ((p->buttons & 0x08) >> 1);
  		p->dz = 0;
  		client->bufsiz = 3;
  		break;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
591
592
593
  	}
  
  	if (!p->dx && !p->dy && !p->dz) {
d0ffb9be8   Dmitry Torokhov   Input: handlers -...
594
595
596
  		if (client->tail == client->head) {
  			client->ready = 0;
  			client->last_buttons = p->buttons;
c1e4c8d3e   Pavel Machek   [PATCH] fix jumpy...
597
  		} else
d0ffb9be8   Dmitry Torokhov   Input: handlers -...
598
  			client->tail = (client->tail + 1) % PACKET_QUEUE_LEN;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
599
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
600
  }
464b24157   Dmitry Torokhov   Input: mousedev -...
601
602
603
604
  static void mousedev_generate_response(struct mousedev_client *client,
  					int command)
  {
  	client->ps2[0] = 0xfa; /* ACK */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
605

464b24157   Dmitry Torokhov   Input: mousedev -...
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
  	switch (command) {
  
  	case 0xeb: /* Poll */
  		mousedev_packet(client, &client->ps2[1]);
  		client->bufsiz++; /* account for leading ACK */
  		break;
  
  	case 0xf2: /* Get ID */
  		switch (client->mode) {
  		case MOUSEDEV_EMUL_PS2:
  			client->ps2[1] = 0;
  			break;
  		case MOUSEDEV_EMUL_IMPS:
  			client->ps2[1] = 3;
  			break;
  		case MOUSEDEV_EMUL_EXPS:
  			client->ps2[1] = 4;
  			break;
  		}
  		client->bufsiz = 2;
  		break;
  
  	case 0xe9: /* Get info */
  		client->ps2[1] = 0x60; client->ps2[2] = 3; client->ps2[3] = 200;
  		client->bufsiz = 4;
  		break;
  
  	case 0xff: /* Reset */
  		client->impsseq = client->imexseq = 0;
  		client->mode = MOUSEDEV_EMUL_PS2;
  		client->ps2[1] = 0xaa; client->ps2[2] = 0x00;
  		client->bufsiz = 3;
  		break;
  
  	default:
  		client->bufsiz = 1;
  		break;
  	}
  	client->buffer = client->bufsiz;
  }
  
  static ssize_t mousedev_write(struct file *file, const char __user *buffer,
  				size_t count, loff_t *ppos)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
649
  {
d0ffb9be8   Dmitry Torokhov   Input: handlers -...
650
  	struct mousedev_client *client = file->private_data;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
651
652
653
654
655
656
657
  	unsigned char c;
  	unsigned int i;
  
  	for (i = 0; i < count; i++) {
  
  		if (get_user(c, buffer + i))
  			return -EFAULT;
464b24157   Dmitry Torokhov   Input: mousedev -...
658
  		spin_lock_irq(&client->packet_lock);
d0ffb9be8   Dmitry Torokhov   Input: handlers -...
659
660
661
662
  		if (c == mousedev_imex_seq[client->imexseq]) {
  			if (++client->imexseq == MOUSEDEV_SEQ_LEN) {
  				client->imexseq = 0;
  				client->mode = MOUSEDEV_EMUL_EXPS;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
663
  			}
1e0afb288   Dmitry Torokhov   Input: fix format...
664
  		} else
d0ffb9be8   Dmitry Torokhov   Input: handlers -...
665
  			client->imexseq = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
666

d0ffb9be8   Dmitry Torokhov   Input: handlers -...
667
668
669
670
  		if (c == mousedev_imps_seq[client->impsseq]) {
  			if (++client->impsseq == MOUSEDEV_SEQ_LEN) {
  				client->impsseq = 0;
  				client->mode = MOUSEDEV_EMUL_IMPS;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
671
  			}
1e0afb288   Dmitry Torokhov   Input: fix format...
672
  		} else
d0ffb9be8   Dmitry Torokhov   Input: handlers -...
673
  			client->impsseq = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
674

464b24157   Dmitry Torokhov   Input: mousedev -...
675
  		mousedev_generate_response(client, c);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
676

464b24157   Dmitry Torokhov   Input: mousedev -...
677
  		spin_unlock_irq(&client->packet_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
678
  	}
d0ffb9be8   Dmitry Torokhov   Input: handlers -...
679
  	kill_fasync(&client->fasync, SIGIO, POLL_IN);
d0ffb9be8   Dmitry Torokhov   Input: handlers -...
680
  	wake_up_interruptible(&client->mousedev->wait);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
681
682
683
  
  	return count;
  }
464b24157   Dmitry Torokhov   Input: mousedev -...
684
685
  static ssize_t mousedev_read(struct file *file, char __user *buffer,
  			     size_t count, loff_t *ppos)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
686
  {
d0ffb9be8   Dmitry Torokhov   Input: handlers -...
687
  	struct mousedev_client *client = file->private_data;
464b24157   Dmitry Torokhov   Input: mousedev -...
688
689
  	struct mousedev *mousedev = client->mousedev;
  	signed char data[sizeof(client->ps2)];
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
690
  	int retval = 0;
464b24157   Dmitry Torokhov   Input: mousedev -...
691
692
  	if (!client->ready && !client->buffer && mousedev->exist &&
  	    (file->f_flags & O_NONBLOCK))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
693
  		return -EAGAIN;
464b24157   Dmitry Torokhov   Input: mousedev -...
694
695
  	retval = wait_event_interruptible(mousedev->wait,
  			!mousedev->exist || client->ready || client->buffer);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
696
697
  	if (retval)
  		return retval;
464b24157   Dmitry Torokhov   Input: mousedev -...
698
  	if (!mousedev->exist)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
699
  		return -ENODEV;
464b24157   Dmitry Torokhov   Input: mousedev -...
700
  	spin_lock_irq(&client->packet_lock);
d0ffb9be8   Dmitry Torokhov   Input: handlers -...
701
702
703
  	if (!client->buffer && client->ready) {
  		mousedev_packet(client, client->ps2);
  		client->buffer = client->bufsiz;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
704
  	}
d0ffb9be8   Dmitry Torokhov   Input: handlers -...
705
706
  	if (count > client->buffer)
  		count = client->buffer;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
707

464b24157   Dmitry Torokhov   Input: mousedev -...
708
  	memcpy(data, client->ps2 + client->bufsiz - client->buffer, count);
d0ffb9be8   Dmitry Torokhov   Input: handlers -...
709
  	client->buffer -= count;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
710

464b24157   Dmitry Torokhov   Input: mousedev -...
711
712
713
  	spin_unlock_irq(&client->packet_lock);
  
  	if (copy_to_user(buffer, data, count))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
714
715
716
717
718
719
720
721
  		return -EFAULT;
  
  	return count;
  }
  
  /* No kernel lock - fine */
  static unsigned int mousedev_poll(struct file *file, poll_table *wait)
  {
d0ffb9be8   Dmitry Torokhov   Input: handlers -...
722
723
  	struct mousedev_client *client = file->private_data;
  	struct mousedev *mousedev = client->mousedev;
4d4bf995e   Julien Moutinho   Input: mousedev -...
724
  	unsigned int mask;
1e0afb288   Dmitry Torokhov   Input: fix format...
725

d0ffb9be8   Dmitry Torokhov   Input: handlers -...
726
  	poll_wait(file, &mousedev->wait, wait);
4d4bf995e   Julien Moutinho   Input: mousedev -...
727
728
729
730
731
732
  
  	mask = mousedev->exist ? POLLOUT | POLLWRNORM : POLLHUP | POLLERR;
  	if (client->ready || client->buffer)
  		mask |= POLLIN | POLLRDNORM;
  
  	return mask;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
733
  }
66e661188   Dmitry Torokhov   Input: constify i...
734
  static const struct file_operations mousedev_fops = {
1c74585ec   Dmitry Torokhov   Input: mousedev -...
735
736
737
738
739
740
741
742
  	.owner		= THIS_MODULE,
  	.read		= mousedev_read,
  	.write		= mousedev_write,
  	.poll		= mousedev_poll,
  	.open		= mousedev_open,
  	.release	= mousedev_release,
  	.fasync		= mousedev_fasync,
  	.llseek		= noop_llseek,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
743
  };
464b24157   Dmitry Torokhov   Input: mousedev -...
744
745
746
747
748
749
750
751
  /*
   * Mark device non-existent. This disables writes, ioctls and
   * prevents new users from opening the device. Already posted
   * blocking reads will stay, however new ones will fail.
   */
  static void mousedev_mark_dead(struct mousedev *mousedev)
  {
  	mutex_lock(&mousedev->mutex);
20da92de8   Dmitry Torokhov   Input: change inp...
752
  	mousedev->exist = false;
464b24157   Dmitry Torokhov   Input: mousedev -...
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
  	mutex_unlock(&mousedev->mutex);
  }
  
  /*
   * Wake up users waiting for IO so they can disconnect from
   * dead device.
   */
  static void mousedev_hangup(struct mousedev *mousedev)
  {
  	struct mousedev_client *client;
  
  	spin_lock(&mousedev->client_lock);
  	list_for_each_entry(client, &mousedev->client_list, node)
  		kill_fasync(&client->fasync, SIGIO, POLL_HUP);
  	spin_unlock(&mousedev->client_lock);
  
  	wake_up_interruptible(&mousedev->wait);
  }
  
  static void mousedev_cleanup(struct mousedev *mousedev)
  {
  	struct input_handle *handle = &mousedev->handle;
  
  	mousedev_mark_dead(mousedev);
  	mousedev_hangup(mousedev);
7f8d4cad1   Dmitry Torokhov   Input: extend the...
778
779
  
  	cdev_del(&mousedev->cdev);
464b24157   Dmitry Torokhov   Input: mousedev -...
780
781
782
783
784
  
  	/* mousedev is marked dead so no one else accesses mousedev->open */
  	if (mousedev->open)
  		input_close_device(handle);
  }
7f8d4cad1   Dmitry Torokhov   Input: extend the...
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
  static int mousedev_reserve_minor(bool mixdev)
  {
  	int minor;
  
  	if (mixdev) {
  		minor = input_get_new_minor(MOUSEDEV_MIX, 1, false);
  		if (minor < 0)
  			pr_err("failed to reserve mixdev minor: %d
  ", minor);
  	} else {
  		minor = input_get_new_minor(MOUSEDEV_MINOR_BASE,
  					    MOUSEDEV_MINORS, true);
  		if (minor < 0)
  			pr_err("failed to reserve new minor: %d
  ", minor);
  	}
  
  	return minor;
  }
9657d75c5   Dmitry Torokhov   Input: convert fr...
804
805
  static struct mousedev *mousedev_create(struct input_dev *dev,
  					struct input_handler *handler,
7f8d4cad1   Dmitry Torokhov   Input: extend the...
806
  					bool mixdev)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
807
808
  {
  	struct mousedev *mousedev;
7f8d4cad1   Dmitry Torokhov   Input: extend the...
809
  	int minor;
5b2a08262   Dmitry Torokhov   Input: rework han...
810
  	int error;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
811

7f8d4cad1   Dmitry Torokhov   Input: extend the...
812
813
814
815
816
  	minor = mousedev_reserve_minor(mixdev);
  	if (minor < 0) {
  		error = minor;
  		goto err_out;
  	}
5b2a08262   Dmitry Torokhov   Input: rework han...
817
  	mousedev = kzalloc(sizeof(struct mousedev), GFP_KERNEL);
9657d75c5   Dmitry Torokhov   Input: convert fr...
818
819
  	if (!mousedev) {
  		error = -ENOMEM;
7f8d4cad1   Dmitry Torokhov   Input: extend the...
820
  		goto err_free_minor;
9657d75c5   Dmitry Torokhov   Input: convert fr...
821
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
822

d0ffb9be8   Dmitry Torokhov   Input: handlers -...
823
  	INIT_LIST_HEAD(&mousedev->client_list);
5b2a08262   Dmitry Torokhov   Input: rework han...
824
  	INIT_LIST_HEAD(&mousedev->mixdev_node);
464b24157   Dmitry Torokhov   Input: mousedev -...
825
826
827
  	spin_lock_init(&mousedev->client_lock);
  	mutex_init(&mousedev->mutex);
  	lockdep_set_subclass(&mousedev->mutex,
7f8d4cad1   Dmitry Torokhov   Input: extend the...
828
  			     mixdev ? SINGLE_DEPTH_NESTING : 0);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
829
  	init_waitqueue_head(&mousedev->wait);
7f8d4cad1   Dmitry Torokhov   Input: extend the...
830
  	if (mixdev) {
3d5cb60ef   Thadeu Lima de Souza Cascardo   Input: simplify n...
831
  		dev_set_name(&mousedev->dev, "mice");
e4dbedc7e   Dmitry Torokhov   Input: mousedev -...
832
833
834
  
  		mousedev->open_device = mixdev_open_devices;
  		mousedev->close_device = mixdev_close_devices;
7f8d4cad1   Dmitry Torokhov   Input: extend the...
835
836
837
838
839
840
  	} else {
  		int dev_no = minor;
  		/* Normalize device number if it falls into legacy range */
  		if (dev_no < MOUSEDEV_MINOR_BASE + MOUSEDEV_MINORS)
  			dev_no -= MOUSEDEV_MINOR_BASE;
  		dev_set_name(&mousedev->dev, "mouse%d", dev_no);
e4dbedc7e   Dmitry Torokhov   Input: mousedev -...
841
842
843
  
  		mousedev->open_device = mousedev_open_device;
  		mousedev->close_device = mousedev_close_device;
7f8d4cad1   Dmitry Torokhov   Input: extend the...
844
  	}
9657d75c5   Dmitry Torokhov   Input: convert fr...
845

20da92de8   Dmitry Torokhov   Input: change inp...
846
  	mousedev->exist = true;
a7097ff89   Dmitry Torokhov   Input: make sure ...
847
  	mousedev->handle.dev = input_get_device(dev);
3d5cb60ef   Thadeu Lima de Souza Cascardo   Input: simplify n...
848
  	mousedev->handle.name = dev_name(&mousedev->dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
849
850
  	mousedev->handle.handler = handler;
  	mousedev->handle.private = mousedev;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
851

9657d75c5   Dmitry Torokhov   Input: convert fr...
852
853
854
  	mousedev->dev.class = &input_class;
  	if (dev)
  		mousedev->dev.parent = &dev->dev;
7f8d4cad1   Dmitry Torokhov   Input: extend the...
855
  	mousedev->dev.devt = MKDEV(INPUT_MAJOR, minor);
9657d75c5   Dmitry Torokhov   Input: convert fr...
856
857
  	mousedev->dev.release = mousedev_free;
  	device_initialize(&mousedev->dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
858

7f8d4cad1   Dmitry Torokhov   Input: extend the...
859
  	if (!mixdev) {
464b24157   Dmitry Torokhov   Input: mousedev -...
860
861
862
863
  		error = input_register_handle(&mousedev->handle);
  		if (error)
  			goto err_free_mousedev;
  	}
7f8d4cad1   Dmitry Torokhov   Input: extend the...
864
  	cdev_init(&mousedev->cdev, &mousedev_fops);
4a215aade   Dmitry Torokhov   Input: fix use-af...
865
  	mousedev->cdev.kobj.parent = &mousedev->dev.kobj;
7f8d4cad1   Dmitry Torokhov   Input: extend the...
866
  	error = cdev_add(&mousedev->cdev, mousedev->dev.devt, 1);
464b24157   Dmitry Torokhov   Input: mousedev -...
867
868
  	if (error)
  		goto err_unregister_handle;
5b2a08262   Dmitry Torokhov   Input: rework han...
869

9657d75c5   Dmitry Torokhov   Input: convert fr...
870
871
  	error = device_add(&mousedev->dev);
  	if (error)
464b24157   Dmitry Torokhov   Input: mousedev -...
872
  		goto err_cleanup_mousedev;
9657d75c5   Dmitry Torokhov   Input: convert fr...
873
874
  
  	return mousedev;
464b24157   Dmitry Torokhov   Input: mousedev -...
875
876
877
   err_cleanup_mousedev:
  	mousedev_cleanup(mousedev);
   err_unregister_handle:
7f8d4cad1   Dmitry Torokhov   Input: extend the...
878
  	if (!mixdev)
464b24157   Dmitry Torokhov   Input: mousedev -...
879
  		input_unregister_handle(&mousedev->handle);
9657d75c5   Dmitry Torokhov   Input: convert fr...
880
881
   err_free_mousedev:
  	put_device(&mousedev->dev);
7f8d4cad1   Dmitry Torokhov   Input: extend the...
882
883
   err_free_minor:
  	input_free_minor(minor);
9657d75c5   Dmitry Torokhov   Input: convert fr...
884
885
886
887
888
889
   err_out:
  	return ERR_PTR(error);
  }
  
  static void mousedev_destroy(struct mousedev *mousedev)
  {
9657d75c5   Dmitry Torokhov   Input: convert fr...
890
  	device_del(&mousedev->dev);
464b24157   Dmitry Torokhov   Input: mousedev -...
891
  	mousedev_cleanup(mousedev);
7f8d4cad1   Dmitry Torokhov   Input: extend the...
892
  	input_free_minor(MINOR(mousedev->dev.devt));
e4dbedc7e   Dmitry Torokhov   Input: mousedev -...
893
  	if (mousedev != mousedev_mix)
464b24157   Dmitry Torokhov   Input: mousedev -...
894
895
896
  		input_unregister_handle(&mousedev->handle);
  	put_device(&mousedev->dev);
  }
9657d75c5   Dmitry Torokhov   Input: convert fr...
897

464b24157   Dmitry Torokhov   Input: mousedev -...
898
899
900
901
902
903
904
905
906
907
908
909
  static int mixdev_add_device(struct mousedev *mousedev)
  {
  	int retval;
  
  	retval = mutex_lock_interruptible(&mousedev_mix->mutex);
  	if (retval)
  		return retval;
  
  	if (mousedev_mix->open) {
  		retval = mousedev_open_device(mousedev);
  		if (retval)
  			goto out;
3376b8b75   Dmitry Torokhov   Input: mousedev -...
910
  		mousedev->opened_by_mixdev = true;
5b2a08262   Dmitry Torokhov   Input: rework han...
911
  	}
c9bcd582d   Greg Kroah-Hartman   [PATCH] INPUT: Cr...
912

464b24157   Dmitry Torokhov   Input: mousedev -...
913
914
915
916
917
918
919
920
921
922
923
  	get_device(&mousedev->dev);
  	list_add_tail(&mousedev->mixdev_node, &mousedev_mix_list);
  
   out:
  	mutex_unlock(&mousedev_mix->mutex);
  	return retval;
  }
  
  static void mixdev_remove_device(struct mousedev *mousedev)
  {
  	mutex_lock(&mousedev_mix->mutex);
3376b8b75   Dmitry Torokhov   Input: mousedev -...
924
925
  	if (mousedev->opened_by_mixdev) {
  		mousedev->opened_by_mixdev = false;
464b24157   Dmitry Torokhov   Input: mousedev -...
926
927
928
929
930
  		mousedev_close_device(mousedev);
  	}
  
  	list_del_init(&mousedev->mixdev_node);
  	mutex_unlock(&mousedev_mix->mutex);
9657d75c5   Dmitry Torokhov   Input: convert fr...
931
932
  	put_device(&mousedev->dev);
  }
464b24157   Dmitry Torokhov   Input: mousedev -...
933
934
  static int mousedev_connect(struct input_handler *handler,
  			    struct input_dev *dev,
9657d75c5   Dmitry Torokhov   Input: convert fr...
935
936
937
  			    const struct input_device_id *id)
  {
  	struct mousedev *mousedev;
9657d75c5   Dmitry Torokhov   Input: convert fr...
938
  	int error;
7f8d4cad1   Dmitry Torokhov   Input: extend the...
939
  	mousedev = mousedev_create(dev, handler, false);
9657d75c5   Dmitry Torokhov   Input: convert fr...
940
941
  	if (IS_ERR(mousedev))
  		return PTR_ERR(mousedev);
5b2a08262   Dmitry Torokhov   Input: rework han...
942

d542ed82f   Dmitry Torokhov   Input: handlers -...
943
  	error = mixdev_add_device(mousedev);
464b24157   Dmitry Torokhov   Input: mousedev -...
944
945
946
947
  	if (error) {
  		mousedev_destroy(mousedev);
  		return error;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
948

5b2a08262   Dmitry Torokhov   Input: rework han...
949
  	return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
950
951
952
953
954
  }
  
  static void mousedev_disconnect(struct input_handle *handle)
  {
  	struct mousedev *mousedev = handle->private;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
955

d542ed82f   Dmitry Torokhov   Input: handlers -...
956
  	mixdev_remove_device(mousedev);
9657d75c5   Dmitry Torokhov   Input: convert fr...
957
  	mousedev_destroy(mousedev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
958
  }
66e661188   Dmitry Torokhov   Input: constify i...
959
  static const struct input_device_id mousedev_ids[] = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
960
  	{
464b24157   Dmitry Torokhov   Input: mousedev -...
961
962
963
  		.flags = INPUT_DEVICE_ID_MATCH_EVBIT |
  				INPUT_DEVICE_ID_MATCH_KEYBIT |
  				INPUT_DEVICE_ID_MATCH_RELBIT,
7b19ada2e   Jiri Slaby   get rid of input ...
964
965
966
  		.evbit = { BIT_MASK(EV_KEY) | BIT_MASK(EV_REL) },
  		.keybit = { [BIT_WORD(BTN_LEFT)] = BIT_MASK(BTN_LEFT) },
  		.relbit = { BIT_MASK(REL_X) | BIT_MASK(REL_Y) },
464b24157   Dmitry Torokhov   Input: mousedev -...
967
968
  	},	/* A mouse like device, at least one button,
  		   two relative axes */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
969
  	{
464b24157   Dmitry Torokhov   Input: mousedev -...
970
971
  		.flags = INPUT_DEVICE_ID_MATCH_EVBIT |
  				INPUT_DEVICE_ID_MATCH_RELBIT,
7b19ada2e   Jiri Slaby   get rid of input ...
972
973
  		.evbit = { BIT_MASK(EV_KEY) | BIT_MASK(EV_REL) },
  		.relbit = { BIT_MASK(REL_WHEEL) },
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
974
975
  	},	/* A separate scrollwheel */
  	{
464b24157   Dmitry Torokhov   Input: mousedev -...
976
977
978
  		.flags = INPUT_DEVICE_ID_MATCH_EVBIT |
  				INPUT_DEVICE_ID_MATCH_KEYBIT |
  				INPUT_DEVICE_ID_MATCH_ABSBIT,
7b19ada2e   Jiri Slaby   get rid of input ...
979
980
981
  		.evbit = { BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS) },
  		.keybit = { [BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH) },
  		.absbit = { BIT_MASK(ABS_X) | BIT_MASK(ABS_Y) },
464b24157   Dmitry Torokhov   Input: mousedev -...
982
983
  	},	/* A tablet like device, at least touch detection,
  		   two absolute axes */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
984
  	{
464b24157   Dmitry Torokhov   Input: mousedev -...
985
986
987
  		.flags = INPUT_DEVICE_ID_MATCH_EVBIT |
  				INPUT_DEVICE_ID_MATCH_KEYBIT |
  				INPUT_DEVICE_ID_MATCH_ABSBIT,
7b19ada2e   Jiri Slaby   get rid of input ...
988
989
990
991
992
993
  		.evbit = { BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS) },
  		.keybit = { [BIT_WORD(BTN_TOOL_FINGER)] =
  				BIT_MASK(BTN_TOOL_FINGER) },
  		.absbit = { BIT_MASK(ABS_X) | BIT_MASK(ABS_Y) |
  				BIT_MASK(ABS_PRESSURE) |
  				BIT_MASK(ABS_TOOL_WIDTH) },
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
994
  	},	/* A touchpad */
6724f9346   Micah Parrish   Input: mousedev -...
995
996
997
998
  	{
  		.flags = INPUT_DEVICE_ID_MATCH_EVBIT |
  			INPUT_DEVICE_ID_MATCH_KEYBIT |
  			INPUT_DEVICE_ID_MATCH_ABSBIT,
d182c10c8   Dmitry Torokhov   Input: mousedev -...
999
  		.evbit = { BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS) },
6724f9346   Micah Parrish   Input: mousedev -...
1000
1001
1002
1003
  		.keybit = { [BIT_WORD(BTN_LEFT)] = BIT_MASK(BTN_LEFT) },
  		.absbit = { BIT_MASK(ABS_X) | BIT_MASK(ABS_Y) },
  	},	/* Mouse-like device with absolute X and Y but ordinary
  		   clicks, like hp ILO2 High Performance mouse */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1004

d0ffb9be8   Dmitry Torokhov   Input: handlers -...
1005
  	{ },	/* Terminating entry */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1006
1007
1008
1009
1010
  };
  
  MODULE_DEVICE_TABLE(input, mousedev_ids);
  
  static struct input_handler mousedev_handler = {
1c74585ec   Dmitry Torokhov   Input: mousedev -...
1011
1012
1013
  	.event		= mousedev_event,
  	.connect	= mousedev_connect,
  	.disconnect	= mousedev_disconnect,
7f8d4cad1   Dmitry Torokhov   Input: extend the...
1014
  	.legacy_minors	= true,
1c74585ec   Dmitry Torokhov   Input: mousedev -...
1015
1016
1017
  	.minor		= MOUSEDEV_MINOR_BASE,
  	.name		= "mousedev",
  	.id_table	= mousedev_ids,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1018
1019
1020
  };
  
  #ifdef CONFIG_INPUT_MOUSEDEV_PSAUX
a2cb1191f   Dmitry Torokhov   Input: mousedev -...
1021
  #include <linux/miscdevice.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1022
  static struct miscdevice psaux_mouse = {
a2cb1191f   Dmitry Torokhov   Input: mousedev -...
1023
1024
1025
  	.minor	= PSMOUSE_MINOR,
  	.name	= "psaux",
  	.fops	= &mousedev_fops,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1026
  };
a2cb1191f   Dmitry Torokhov   Input: mousedev -...
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
  
  static bool psaux_registered;
  
  static void __init mousedev_psaux_register(void)
  {
  	int error;
  
  	error = misc_register(&psaux_mouse);
  	if (error)
  		pr_warn("could not register psaux device, error: %d
  ",
  			   error);
  	else
  		psaux_registered = true;
  }
  
  static void __exit mousedev_psaux_unregister(void)
  {
  	if (psaux_registered)
  		misc_deregister(&psaux_mouse);
  }
  #else
  static inline void mousedev_psaux_register(void) { }
  static inline void mousedev_psaux_unregister(void) { }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1051
1052
1053
1054
  #endif
  
  static int __init mousedev_init(void)
  {
4263cf0fa   Dmitry Torokhov   Input: make input...
1055
  	int error;
7f8d4cad1   Dmitry Torokhov   Input: extend the...
1056
  	mousedev_mix = mousedev_create(NULL, &mousedev_handler, true);
9657d75c5   Dmitry Torokhov   Input: convert fr...
1057
1058
  	if (IS_ERR(mousedev_mix))
  		return PTR_ERR(mousedev_mix);
4263cf0fa   Dmitry Torokhov   Input: make input...
1059
  	error = input_register_handler(&mousedev_handler);
9657d75c5   Dmitry Torokhov   Input: convert fr...
1060
1061
  	if (error) {
  		mousedev_destroy(mousedev_mix);
4263cf0fa   Dmitry Torokhov   Input: make input...
1062
  		return error;
4263cf0fa   Dmitry Torokhov   Input: make input...
1063
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1064

a2cb1191f   Dmitry Torokhov   Input: mousedev -...
1065
  	mousedev_psaux_register();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1066

da0c49011   Joe Perches   Input: use pr_fmt...
1067
1068
  	pr_info("PS/2 mouse device common for all mice
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1069
1070
1071
1072
1073
1074
  
  	return 0;
  }
  
  static void __exit mousedev_exit(void)
  {
a2cb1191f   Dmitry Torokhov   Input: mousedev -...
1075
  	mousedev_psaux_unregister();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1076
  	input_unregister_handler(&mousedev_handler);
9657d75c5   Dmitry Torokhov   Input: convert fr...
1077
  	mousedev_destroy(mousedev_mix);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1078
1079
1080
1081
  }
  
  module_init(mousedev_init);
  module_exit(mousedev_exit);