Blame view

drivers/input/mousedev.c 25.8 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
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
13
14
  #define MOUSEDEV_MINORS		32
  #define MOUSEDEV_MIX		31
a99bbaf5e   Alexey Dobriyan   headers: remove s...
15
  #include <linux/sched.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
16
17
18
  #include <linux/slab.h>
  #include <linux/poll.h>
  #include <linux/module.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
19
20
  #include <linux/init.h>
  #include <linux/input.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
21
22
23
  #include <linux/random.h>
  #include <linux/major.h>
  #include <linux/device.h>
987a6c029   Daniel Mack   Input: switch to ...
24
  #include <linux/kernel.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
  #ifdef CONFIG_INPUT_MOUSEDEV_PSAUX
  #include <linux/miscdevice.h>
  #endif
  
  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 -...
41
  module_param(xres, uint, 0644);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
42
43
44
  MODULE_PARM_DESC(xres, "Horizontal screen resolution");
  
  static int yres = CONFIG_INPUT_MOUSEDEV_SCREEN_Y;
84c12b241   Dmitry Torokhov   Input: mousedev -...
45
  module_param(yres, uint, 0644);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
46
47
48
  MODULE_PARM_DESC(yres, "Vertical screen resolution");
  
  static unsigned tap_time = 200;
84c12b241   Dmitry Torokhov   Input: mousedev -...
49
  module_param(tap_time, uint, 0644);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
50
51
52
53
54
55
56
57
58
59
  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
60
61
  	int open;
  	int minor;
464b24157   Dmitry Torokhov   Input: mousedev -...
62
  	struct input_handle handle;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
63
  	wait_queue_head_t wait;
d0ffb9be8   Dmitry Torokhov   Input: handlers -...
64
  	struct list_head client_list;
464b24157   Dmitry Torokhov   Input: mousedev -...
65
66
  	spinlock_t client_lock; /* protects client_list */
  	struct mutex mutex;
9657d75c5   Dmitry Torokhov   Input: convert fr...
67
  	struct device dev;
20da92de8   Dmitry Torokhov   Input: change inp...
68
  	bool exist;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
69

d542ed82f   Dmitry Torokhov   Input: handlers -...
70
71
  	struct list_head mixdev_node;
  	int mixdev_open;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
  	struct mousedev_hw_data packet;
  	unsigned int pkt_count;
  	int old_x[4], old_y[4];
  	int frac_dx, frac_dy;
  	unsigned long touch;
  };
  
  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 -...
91
  struct mousedev_client {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
92
93
94
95
96
97
98
99
100
101
102
103
104
  	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...
105
  	unsigned long last_buttons;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
106
107
108
109
110
111
112
113
114
115
  };
  
  #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 };
  
  static struct input_handler mousedev_handler;
  
  static struct mousedev *mousedev_table[MOUSEDEV_MINORS];
464b24157   Dmitry Torokhov   Input: mousedev -...
116
  static DEFINE_MUTEX(mousedev_table_mutex);
9657d75c5   Dmitry Torokhov   Input: convert fr...
117
  static struct mousedev *mousedev_mix;
d542ed82f   Dmitry Torokhov   Input: handlers -...
118
  static LIST_HEAD(mousedev_mix_list);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
119

464b24157   Dmitry Torokhov   Input: mousedev -...
120
121
  static void mixdev_open_devices(void);
  static void mixdev_close_devices(void);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
122
123
  #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 -...
124
125
126
  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
127
128
129
  {
  	int size, tmp;
  	enum { FRACTION_DENOM = 128 };
0d9d93c41   Dmitry Torokhov   Input: mousedev -...
130
  	switch (code) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
131

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

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

464b24157   Dmitry Torokhov   Input: mousedev -...
141
142
143
144
145
146
147
148
149
150
151
  			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 ...
152
  			/* use X size for ABS_Y to keep the same scale */
268ba5c05   Christoph Fritz   Input: mousedev -...
153
154
  			size = input_abs_get_max(dev, ABS_X) -
  					input_abs_get_min(dev, ABS_X);
464b24157   Dmitry Torokhov   Input: mousedev -...
155
156
  			if (size == 0)
  				size = 256 * 2;
987a6c029   Daniel Mack   Input: switch to ...
157

464b24157   Dmitry Torokhov   Input: mousedev -...
158
159
160
161
162
163
164
  			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
165
166
  	}
  }
464b24157   Dmitry Torokhov   Input: mousedev -...
167
168
  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
169
  {
987a6c029   Daniel Mack   Input: switch to ...
170
  	int min, max, size;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
171
172
  
  	switch (code) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
173

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

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

8c127f071   Hans Petter Selasky   Input: properly a...
196
  		value = clamp(value, min, max);
987a6c029   Daniel Mack   Input: switch to ...
197
198
  
  		mousedev->packet.y = yres - ((value - min) * yres) / size;
464b24157   Dmitry Torokhov   Input: mousedev -...
199
200
  		mousedev->packet.abs_event = 1;
  		break;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
201
202
  	}
  }
464b24157   Dmitry Torokhov   Input: mousedev -...
203
204
  static void mousedev_rel_event(struct mousedev *mousedev,
  				unsigned int code, int value)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
205
206
  {
  	switch (code) {
464b24157   Dmitry Torokhov   Input: mousedev -...
207
208
209
210
211
212
213
214
215
216
217
  	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
218
219
  	}
  }
464b24157   Dmitry Torokhov   Input: mousedev -...
220
221
  static void mousedev_key_event(struct mousedev *mousedev,
  				unsigned int code, int value)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
222
223
224
225
  {
  	int index;
  
  	switch (code) {
464b24157   Dmitry Torokhov   Input: mousedev -...
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
  
  	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
248
249
250
251
  	}
  
  	if (value) {
  		set_bit(index, &mousedev->packet.buttons);
9657d75c5   Dmitry Torokhov   Input: convert fr...
252
  		set_bit(index, &mousedev_mix->packet.buttons);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
253
254
  	} else {
  		clear_bit(index, &mousedev->packet.buttons);
9657d75c5   Dmitry Torokhov   Input: convert fr...
255
  		clear_bit(index, &mousedev_mix->packet.buttons);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
256
257
  	}
  }
464b24157   Dmitry Torokhov   Input: mousedev -...
258
259
  static void mousedev_notify_readers(struct mousedev *mousedev,
  				    struct mousedev_hw_data *packet)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
260
  {
d0ffb9be8   Dmitry Torokhov   Input: handlers -...
261
  	struct mousedev_client *client;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
262
  	struct mousedev_motion *p;
464b24157   Dmitry Torokhov   Input: mousedev -...
263
  	unsigned int new_head;
8121152c1   Dmitry Torokhov   Input: mousedev -...
264
  	int wake_readers = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
265

82ba56c27   Dmitry Torokhov   Input: use full R...
266
  	rcu_read_lock();
464b24157   Dmitry Torokhov   Input: mousedev -...
267
268
269
270
  	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
271

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

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

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

8121152c1   Dmitry Torokhov   Input: mousedev -...
311
312
  	if (wake_readers)
  		wake_up_interruptible(&mousedev->wait);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
313
314
315
316
317
318
  }
  
  static void mousedev_touchpad_touch(struct mousedev *mousedev, int value)
  {
  	if (!value) {
  		if (mousedev->touch &&
464b24157   Dmitry Torokhov   Input: mousedev -...
319
320
  		    time_before(jiffies,
  				mousedev->touch + msecs_to_jiffies(tap_time))) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
321
322
323
324
325
326
  			/*
  			 * 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...
327
328
  			set_bit(0, &mousedev_mix->packet.buttons);
  			mousedev_notify_readers(mousedev, &mousedev_mix->packet);
464b24157   Dmitry Torokhov   Input: mousedev -...
329
330
  			mousedev_notify_readers(mousedev_mix,
  						&mousedev_mix->packet);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
331
  			clear_bit(0, &mousedev->packet.buttons);
9657d75c5   Dmitry Torokhov   Input: convert fr...
332
  			clear_bit(0, &mousedev_mix->packet.buttons);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
333
334
335
336
  		}
  		mousedev->touch = mousedev->pkt_count = 0;
  		mousedev->frac_dx = 0;
  		mousedev->frac_dy = 0;
1e0afb288   Dmitry Torokhov   Input: fix format...
337
338
339
  
  	} else if (!mousedev->touch)
  		mousedev->touch = jiffies;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
340
  }
464b24157   Dmitry Torokhov   Input: mousedev -...
341
342
  static void mousedev_event(struct input_handle *handle,
  			   unsigned int type, unsigned int code, int value)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
343
344
345
346
  {
  	struct mousedev *mousedev = handle->private;
  
  	switch (type) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
347

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

464b24157   Dmitry Torokhov   Input: mousedev -...
353
354
355
356
357
  		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
358

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

464b24157   Dmitry Torokhov   Input: mousedev -...
361
362
363
  	case EV_REL:
  		mousedev_rel_event(mousedev, code, value);
  		break;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
364

464b24157   Dmitry Torokhov   Input: mousedev -...
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
  	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
386
  			}
464b24157   Dmitry Torokhov   Input: mousedev -...
387
388
389
390
391
392
393
394
395
  
  			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
396
397
398
399
400
  	}
  }
  
  static int mousedev_fasync(int fd, struct file *file, int on)
  {
d0ffb9be8   Dmitry Torokhov   Input: handlers -...
401
  	struct mousedev_client *client = file->private_data;
1e0afb288   Dmitry Torokhov   Input: fix format...
402

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

464b24157   Dmitry Torokhov   Input: mousedev -...
415
416
417
  	retval = mutex_lock_interruptible(&mousedev->mutex);
  	if (retval)
  		return retval;
d542ed82f   Dmitry Torokhov   Input: handlers -...
418

464b24157   Dmitry Torokhov   Input: mousedev -...
419
420
421
422
  	if (mousedev->minor == MOUSEDEV_MIX)
  		mixdev_open_devices();
  	else if (!mousedev->exist)
  		retval = -ENODEV;
064450140   Oliver Neukum   Input: fix open c...
423
  	else if (!mousedev->open++) {
464b24157   Dmitry Torokhov   Input: mousedev -...
424
  		retval = input_open_device(&mousedev->handle);
064450140   Oliver Neukum   Input: fix open c...
425
426
427
  		if (retval)
  			mousedev->open--;
  	}
d542ed82f   Dmitry Torokhov   Input: handlers -...
428

464b24157   Dmitry Torokhov   Input: mousedev -...
429
430
  	mutex_unlock(&mousedev->mutex);
  	return retval;
d542ed82f   Dmitry Torokhov   Input: handlers -...
431
  }
464b24157   Dmitry Torokhov   Input: mousedev -...
432
  static void mousedev_close_device(struct mousedev *mousedev)
d542ed82f   Dmitry Torokhov   Input: handlers -...
433
  {
464b24157   Dmitry Torokhov   Input: mousedev -...
434
  	mutex_lock(&mousedev->mutex);
d542ed82f   Dmitry Torokhov   Input: handlers -...
435

464b24157   Dmitry Torokhov   Input: mousedev -...
436
437
438
439
440
441
  	if (mousedev->minor == MOUSEDEV_MIX)
  		mixdev_close_devices();
  	else if (mousedev->exist && !--mousedev->open)
  		input_close_device(&mousedev->handle);
  
  	mutex_unlock(&mousedev->mutex);
d542ed82f   Dmitry Torokhov   Input: handlers -...
442
  }
464b24157   Dmitry Torokhov   Input: mousedev -...
443
444
445
446
447
  /*
   * Open all available devices so they can all be multiplexed in one.
   * stream. Note that this function is called with mousedev_mix->mutex
   * held.
   */
d542ed82f   Dmitry Torokhov   Input: handlers -...
448
449
450
  static void mixdev_open_devices(void)
  {
  	struct mousedev *mousedev;
9657d75c5   Dmitry Torokhov   Input: convert fr...
451
452
  	if (mousedev_mix->open++)
  		return;
d542ed82f   Dmitry Torokhov   Input: handlers -...
453
  	list_for_each_entry(mousedev, &mousedev_mix_list, mixdev_node) {
9657d75c5   Dmitry Torokhov   Input: convert fr...
454
  		if (!mousedev->mixdev_open) {
464b24157   Dmitry Torokhov   Input: mousedev -...
455
456
  			if (mousedev_open_device(mousedev))
  				continue;
d542ed82f   Dmitry Torokhov   Input: handlers -...
457

9657d75c5   Dmitry Torokhov   Input: convert fr...
458
  			mousedev->mixdev_open = 1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
459
460
  		}
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
461
  }
464b24157   Dmitry Torokhov   Input: mousedev -...
462
463
464
465
466
  /*
   * Close all devices that were opened as part of multiplexed
   * device. Note that this function is called with mousedev_mix->mutex
   * held.
   */
d542ed82f   Dmitry Torokhov   Input: handlers -...
467
  static void mixdev_close_devices(void)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
468
  {
9657d75c5   Dmitry Torokhov   Input: convert fr...
469
  	struct mousedev *mousedev;
d542ed82f   Dmitry Torokhov   Input: handlers -...
470

9657d75c5   Dmitry Torokhov   Input: convert fr...
471
472
473
474
  	if (--mousedev_mix->open)
  		return;
  
  	list_for_each_entry(mousedev, &mousedev_mix_list, mixdev_node) {
d542ed82f   Dmitry Torokhov   Input: handlers -...
475
476
  		if (mousedev->mixdev_open) {
  			mousedev->mixdev_open = 0;
464b24157   Dmitry Torokhov   Input: mousedev -...
477
  			mousedev_close_device(mousedev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
478
479
  		}
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
480
  }
464b24157   Dmitry Torokhov   Input: mousedev -...
481
482
483
484
485
486
487
  
  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 -...
488
489
490
491
492
493
494
495
  }
  
  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...
496
  	synchronize_rcu();
464b24157   Dmitry Torokhov   Input: mousedev -...
497
  }
d0ffb9be8   Dmitry Torokhov   Input: handlers -...
498
  static int mousedev_release(struct inode *inode, struct file *file)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
499
  {
d0ffb9be8   Dmitry Torokhov   Input: handlers -...
500
501
  	struct mousedev_client *client = file->private_data;
  	struct mousedev *mousedev = client->mousedev;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
502

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

464b24157   Dmitry Torokhov   Input: mousedev -...
506
  	mousedev_close_device(mousedev);
9657d75c5   Dmitry Torokhov   Input: convert fr...
507
  	put_device(&mousedev->dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
508

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
509
510
  	return 0;
  }
d0ffb9be8   Dmitry Torokhov   Input: handlers -...
511
  static int mousedev_open(struct inode *inode, struct file *file)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
512
  {
d0ffb9be8   Dmitry Torokhov   Input: handlers -...
513
  	struct mousedev_client *client;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
514
  	struct mousedev *mousedev;
d542ed82f   Dmitry Torokhov   Input: handlers -...
515
  	int error;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
516
517
518
519
520
521
522
523
  	int i;
  
  #ifdef CONFIG_INPUT_MOUSEDEV_PSAUX
  	if (imajor(inode) == MISC_MAJOR)
  		i = MOUSEDEV_MIX;
  	else
  #endif
  		i = iminor(inode) - MOUSEDEV_MINOR_BASE;
d0ffb9be8   Dmitry Torokhov   Input: handlers -...
524
  	if (i >= MOUSEDEV_MINORS)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
525
  		return -ENODEV;
464b24157   Dmitry Torokhov   Input: mousedev -...
526
  	error = mutex_lock_interruptible(&mousedev_table_mutex);
f9c8154f3   Arnd Bergmann   mousedev: BKL pus...
527
  	if (error) {
464b24157   Dmitry Torokhov   Input: mousedev -...
528
  		return error;
f9c8154f3   Arnd Bergmann   mousedev: BKL pus...
529
  	}
d0ffb9be8   Dmitry Torokhov   Input: handlers -...
530
  	mousedev = mousedev_table[i];
464b24157   Dmitry Torokhov   Input: mousedev -...
531
532
533
  	if (mousedev)
  		get_device(&mousedev->dev);
  	mutex_unlock(&mousedev_table_mutex);
f9c8154f3   Arnd Bergmann   mousedev: BKL pus...
534
  	if (!mousedev) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
535
  		return -ENODEV;
f9c8154f3   Arnd Bergmann   mousedev: BKL pus...
536
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
537

d0ffb9be8   Dmitry Torokhov   Input: handlers -...
538
  	client = kzalloc(sizeof(struct mousedev_client), GFP_KERNEL);
9657d75c5   Dmitry Torokhov   Input: convert fr...
539
540
541
542
  	if (!client) {
  		error = -ENOMEM;
  		goto err_put_mousedev;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
543

d0ffb9be8   Dmitry Torokhov   Input: handlers -...
544
545
546
547
  	spin_lock_init(&client->packet_lock);
  	client->pos_x = xres / 2;
  	client->pos_y = yres / 2;
  	client->mousedev = mousedev;
464b24157   Dmitry Torokhov   Input: mousedev -...
548
  	mousedev_attach_client(mousedev, client);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
549

464b24157   Dmitry Torokhov   Input: mousedev -...
550
551
552
  	error = mousedev_open_device(mousedev);
  	if (error)
  		goto err_free_client;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
553

d0ffb9be8   Dmitry Torokhov   Input: handlers -...
554
  	file->private_data = client;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
555
  	return 0;
9657d75c5   Dmitry Torokhov   Input: convert fr...
556
557
  
   err_free_client:
464b24157   Dmitry Torokhov   Input: mousedev -...
558
  	mousedev_detach_client(mousedev, client);
9657d75c5   Dmitry Torokhov   Input: convert fr...
559
560
561
562
  	kfree(client);
   err_put_mousedev:
  	put_device(&mousedev->dev);
  	return error;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
563
564
565
566
567
568
  }
  
  static inline int mousedev_limit_delta(int delta, int limit)
  {
  	return delta > limit ? limit : (delta < -limit ? -limit : delta);
  }
464b24157   Dmitry Torokhov   Input: mousedev -...
569
570
  static void mousedev_packet(struct mousedev_client *client,
  			    signed char *ps2_data)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
571
  {
464b24157   Dmitry Torokhov   Input: mousedev -...
572
  	struct mousedev_motion *p = &client->packets[client->tail];
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
573

464b24157   Dmitry Torokhov   Input: mousedev -...
574
575
  	ps2_data[0] = 0x08 |
  		((p->dx < 0) << 4) | ((p->dy < 0) << 5) | (p->buttons & 0x07);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
576
577
578
579
  	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 -...
580
  	switch (client->mode) {
464b24157   Dmitry Torokhov   Input: mousedev -...
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
  	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
603
604
605
  	}
  
  	if (!p->dx && !p->dy && !p->dz) {
d0ffb9be8   Dmitry Torokhov   Input: handlers -...
606
607
608
  		if (client->tail == client->head) {
  			client->ready = 0;
  			client->last_buttons = p->buttons;
c1e4c8d3e   Pavel Machek   [PATCH] fix jumpy...
609
  		} else
d0ffb9be8   Dmitry Torokhov   Input: handlers -...
610
  			client->tail = (client->tail + 1) % PACKET_QUEUE_LEN;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
611
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
612
  }
464b24157   Dmitry Torokhov   Input: mousedev -...
613
614
615
616
  static void mousedev_generate_response(struct mousedev_client *client,
  					int command)
  {
  	client->ps2[0] = 0xfa; /* ACK */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
617

464b24157   Dmitry Torokhov   Input: mousedev -...
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
649
650
651
652
653
654
655
656
657
658
659
660
  	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
661
  {
d0ffb9be8   Dmitry Torokhov   Input: handlers -...
662
  	struct mousedev_client *client = file->private_data;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
663
664
665
666
667
668
669
  	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 -...
670
  		spin_lock_irq(&client->packet_lock);
d0ffb9be8   Dmitry Torokhov   Input: handlers -...
671
672
673
674
  		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
675
  			}
1e0afb288   Dmitry Torokhov   Input: fix format...
676
  		} else
d0ffb9be8   Dmitry Torokhov   Input: handlers -...
677
  			client->imexseq = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
678

d0ffb9be8   Dmitry Torokhov   Input: handlers -...
679
680
681
682
  		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
683
  			}
1e0afb288   Dmitry Torokhov   Input: fix format...
684
  		} else
d0ffb9be8   Dmitry Torokhov   Input: handlers -...
685
  			client->impsseq = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
686

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

464b24157   Dmitry Torokhov   Input: mousedev -...
689
  		spin_unlock_irq(&client->packet_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
690
  	}
d0ffb9be8   Dmitry Torokhov   Input: handlers -...
691
  	kill_fasync(&client->fasync, SIGIO, POLL_IN);
d0ffb9be8   Dmitry Torokhov   Input: handlers -...
692
  	wake_up_interruptible(&client->mousedev->wait);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
693
694
695
  
  	return count;
  }
464b24157   Dmitry Torokhov   Input: mousedev -...
696
697
  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
698
  {
d0ffb9be8   Dmitry Torokhov   Input: handlers -...
699
  	struct mousedev_client *client = file->private_data;
464b24157   Dmitry Torokhov   Input: mousedev -...
700
701
  	struct mousedev *mousedev = client->mousedev;
  	signed char data[sizeof(client->ps2)];
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
702
  	int retval = 0;
464b24157   Dmitry Torokhov   Input: mousedev -...
703
704
  	if (!client->ready && !client->buffer && mousedev->exist &&
  	    (file->f_flags & O_NONBLOCK))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
705
  		return -EAGAIN;
464b24157   Dmitry Torokhov   Input: mousedev -...
706
707
  	retval = wait_event_interruptible(mousedev->wait,
  			!mousedev->exist || client->ready || client->buffer);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
708
709
  	if (retval)
  		return retval;
464b24157   Dmitry Torokhov   Input: mousedev -...
710
  	if (!mousedev->exist)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
711
  		return -ENODEV;
464b24157   Dmitry Torokhov   Input: mousedev -...
712
  	spin_lock_irq(&client->packet_lock);
d0ffb9be8   Dmitry Torokhov   Input: handlers -...
713
714
715
  	if (!client->buffer && client->ready) {
  		mousedev_packet(client, client->ps2);
  		client->buffer = client->bufsiz;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
716
  	}
d0ffb9be8   Dmitry Torokhov   Input: handlers -...
717
718
  	if (count > client->buffer)
  		count = client->buffer;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
719

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

464b24157   Dmitry Torokhov   Input: mousedev -...
723
724
725
  	spin_unlock_irq(&client->packet_lock);
  
  	if (copy_to_user(buffer, data, count))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
726
727
728
729
730
731
732
733
  		return -EFAULT;
  
  	return count;
  }
  
  /* No kernel lock - fine */
  static unsigned int mousedev_poll(struct file *file, poll_table *wait)
  {
d0ffb9be8   Dmitry Torokhov   Input: handlers -...
734
735
  	struct mousedev_client *client = file->private_data;
  	struct mousedev *mousedev = client->mousedev;
4d4bf995e   Julien Moutinho   Input: mousedev -...
736
  	unsigned int mask;
1e0afb288   Dmitry Torokhov   Input: fix format...
737

d0ffb9be8   Dmitry Torokhov   Input: handlers -...
738
  	poll_wait(file, &mousedev->wait, wait);
4d4bf995e   Julien Moutinho   Input: mousedev -...
739
740
741
742
743
744
  
  	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
745
  }
66e661188   Dmitry Torokhov   Input: constify i...
746
  static const struct file_operations mousedev_fops = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
747
748
749
750
751
752
753
  	.owner =	THIS_MODULE,
  	.read =		mousedev_read,
  	.write =	mousedev_write,
  	.poll =		mousedev_poll,
  	.open =		mousedev_open,
  	.release =	mousedev_release,
  	.fasync =	mousedev_fasync,
6038f373a   Arnd Bergmann   llseek: automatic...
754
  	.llseek = noop_llseek,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
755
  };
464b24157   Dmitry Torokhov   Input: mousedev -...
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
  static int mousedev_install_chrdev(struct mousedev *mousedev)
  {
  	mousedev_table[mousedev->minor] = mousedev;
  	return 0;
  }
  
  static void mousedev_remove_chrdev(struct mousedev *mousedev)
  {
  	mutex_lock(&mousedev_table_mutex);
  	mousedev_table[mousedev->minor] = NULL;
  	mutex_unlock(&mousedev_table_mutex);
  }
  
  /*
   * 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...
777
  	mousedev->exist = false;
464b24157   Dmitry Torokhov   Input: mousedev -...
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
  	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);
  	mousedev_remove_chrdev(mousedev);
  
  	/* mousedev is marked dead so no one else accesses mousedev->open */
  	if (mousedev->open)
  		input_close_device(handle);
  }
9657d75c5   Dmitry Torokhov   Input: convert fr...
809
810
811
  static struct mousedev *mousedev_create(struct input_dev *dev,
  					struct input_handler *handler,
  					int minor)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
812
813
  {
  	struct mousedev *mousedev;
5b2a08262   Dmitry Torokhov   Input: rework han...
814
  	int error;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
815

5b2a08262   Dmitry Torokhov   Input: rework han...
816
  	mousedev = kzalloc(sizeof(struct mousedev), GFP_KERNEL);
9657d75c5   Dmitry Torokhov   Input: convert fr...
817
818
819
820
  	if (!mousedev) {
  		error = -ENOMEM;
  		goto err_out;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
821

d0ffb9be8   Dmitry Torokhov   Input: handlers -...
822
  	INIT_LIST_HEAD(&mousedev->client_list);
5b2a08262   Dmitry Torokhov   Input: rework han...
823
  	INIT_LIST_HEAD(&mousedev->mixdev_node);
464b24157   Dmitry Torokhov   Input: mousedev -...
824
825
826
  	spin_lock_init(&mousedev->client_lock);
  	mutex_init(&mousedev->mutex);
  	lockdep_set_subclass(&mousedev->mutex,
f74eef95e   Hitoshi Mitake   Input: mousedev -...
827
  			     minor == MOUSEDEV_MIX ? SINGLE_DEPTH_NESTING : 0);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
828
  	init_waitqueue_head(&mousedev->wait);
9657d75c5   Dmitry Torokhov   Input: convert fr...
829
  	if (minor == MOUSEDEV_MIX)
3d5cb60ef   Thadeu Lima de Souza Cascardo   Input: simplify n...
830
  		dev_set_name(&mousedev->dev, "mice");
9657d75c5   Dmitry Torokhov   Input: convert fr...
831
  	else
3d5cb60ef   Thadeu Lima de Souza Cascardo   Input: simplify n...
832
  		dev_set_name(&mousedev->dev, "mouse%d", minor);
9657d75c5   Dmitry Torokhov   Input: convert fr...
833

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
834
  	mousedev->minor = minor;
20da92de8   Dmitry Torokhov   Input: change inp...
835
  	mousedev->exist = true;
a7097ff89   Dmitry Torokhov   Input: make sure ...
836
  	mousedev->handle.dev = input_get_device(dev);
3d5cb60ef   Thadeu Lima de Souza Cascardo   Input: simplify n...
837
  	mousedev->handle.name = dev_name(&mousedev->dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
838
839
  	mousedev->handle.handler = handler;
  	mousedev->handle.private = mousedev;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
840

9657d75c5   Dmitry Torokhov   Input: convert fr...
841
842
843
844
845
846
  	mousedev->dev.class = &input_class;
  	if (dev)
  		mousedev->dev.parent = &dev->dev;
  	mousedev->dev.devt = MKDEV(INPUT_MAJOR, MOUSEDEV_MINOR_BASE + minor);
  	mousedev->dev.release = mousedev_free;
  	device_initialize(&mousedev->dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
847

464b24157   Dmitry Torokhov   Input: mousedev -...
848
849
850
851
852
853
854
855
856
  	if (minor != MOUSEDEV_MIX) {
  		error = input_register_handle(&mousedev->handle);
  		if (error)
  			goto err_free_mousedev;
  	}
  
  	error = mousedev_install_chrdev(mousedev);
  	if (error)
  		goto err_unregister_handle;
5b2a08262   Dmitry Torokhov   Input: rework han...
857

9657d75c5   Dmitry Torokhov   Input: convert fr...
858
859
  	error = device_add(&mousedev->dev);
  	if (error)
464b24157   Dmitry Torokhov   Input: mousedev -...
860
  		goto err_cleanup_mousedev;
9657d75c5   Dmitry Torokhov   Input: convert fr...
861
862
  
  	return mousedev;
464b24157   Dmitry Torokhov   Input: mousedev -...
863
864
865
866
867
   err_cleanup_mousedev:
  	mousedev_cleanup(mousedev);
   err_unregister_handle:
  	if (minor != MOUSEDEV_MIX)
  		input_unregister_handle(&mousedev->handle);
9657d75c5   Dmitry Torokhov   Input: convert fr...
868
869
870
871
872
873
874
875
   err_free_mousedev:
  	put_device(&mousedev->dev);
   err_out:
  	return ERR_PTR(error);
  }
  
  static void mousedev_destroy(struct mousedev *mousedev)
  {
9657d75c5   Dmitry Torokhov   Input: convert fr...
876
  	device_del(&mousedev->dev);
464b24157   Dmitry Torokhov   Input: mousedev -...
877
878
879
880
881
  	mousedev_cleanup(mousedev);
  	if (mousedev->minor != MOUSEDEV_MIX)
  		input_unregister_handle(&mousedev->handle);
  	put_device(&mousedev->dev);
  }
9657d75c5   Dmitry Torokhov   Input: convert fr...
882

464b24157   Dmitry Torokhov   Input: mousedev -...
883
884
885
886
887
888
889
890
891
892
893
894
895
896
  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;
  
  		mousedev->mixdev_open = 1;
5b2a08262   Dmitry Torokhov   Input: rework han...
897
  	}
c9bcd582d   Greg Kroah-Hartman   [PATCH] INPUT: Cr...
898

464b24157   Dmitry Torokhov   Input: mousedev -...
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
  	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);
  
  	if (mousedev->mixdev_open) {
  		mousedev->mixdev_open = 0;
  		mousedev_close_device(mousedev);
  	}
  
  	list_del_init(&mousedev->mixdev_node);
  	mutex_unlock(&mousedev_mix->mutex);
9657d75c5   Dmitry Torokhov   Input: convert fr...
918
919
  	put_device(&mousedev->dev);
  }
464b24157   Dmitry Torokhov   Input: mousedev -...
920
921
  static int mousedev_connect(struct input_handler *handler,
  			    struct input_dev *dev,
9657d75c5   Dmitry Torokhov   Input: convert fr...
922
923
924
925
926
  			    const struct input_device_id *id)
  {
  	struct mousedev *mousedev;
  	int minor;
  	int error;
464b24157   Dmitry Torokhov   Input: mousedev -...
927
928
929
  	for (minor = 0; minor < MOUSEDEV_MINORS; minor++)
  		if (!mousedev_table[minor])
  			break;
9657d75c5   Dmitry Torokhov   Input: convert fr...
930
  	if (minor == MOUSEDEV_MINORS) {
da0c49011   Joe Perches   Input: use pr_fmt...
931
932
  		pr_err("no more free mousedev devices
  ");
9657d75c5   Dmitry Torokhov   Input: convert fr...
933
934
935
936
937
938
  		return -ENFILE;
  	}
  
  	mousedev = mousedev_create(dev, handler, minor);
  	if (IS_ERR(mousedev))
  		return PTR_ERR(mousedev);
5b2a08262   Dmitry Torokhov   Input: rework han...
939

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

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

d542ed82f   Dmitry Torokhov   Input: handlers -...
953
  	mixdev_remove_device(mousedev);
9657d75c5   Dmitry Torokhov   Input: convert fr...
954
  	mousedev_destroy(mousedev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
955
  }
66e661188   Dmitry Torokhov   Input: constify i...
956
  static const struct input_device_id mousedev_ids[] = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
957
  	{
464b24157   Dmitry Torokhov   Input: mousedev -...
958
959
960
  		.flags = INPUT_DEVICE_ID_MATCH_EVBIT |
  				INPUT_DEVICE_ID_MATCH_KEYBIT |
  				INPUT_DEVICE_ID_MATCH_RELBIT,
7b19ada2e   Jiri Slaby   get rid of input ...
961
962
963
  		.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 -...
964
965
  	},	/* A mouse like device, at least one button,
  		   two relative axes */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
966
  	{
464b24157   Dmitry Torokhov   Input: mousedev -...
967
968
  		.flags = INPUT_DEVICE_ID_MATCH_EVBIT |
  				INPUT_DEVICE_ID_MATCH_RELBIT,
7b19ada2e   Jiri Slaby   get rid of input ...
969
970
  		.evbit = { BIT_MASK(EV_KEY) | BIT_MASK(EV_REL) },
  		.relbit = { BIT_MASK(REL_WHEEL) },
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
971
972
  	},	/* A separate scrollwheel */
  	{
464b24157   Dmitry Torokhov   Input: mousedev -...
973
974
975
  		.flags = INPUT_DEVICE_ID_MATCH_EVBIT |
  				INPUT_DEVICE_ID_MATCH_KEYBIT |
  				INPUT_DEVICE_ID_MATCH_ABSBIT,
7b19ada2e   Jiri Slaby   get rid of input ...
976
977
978
  		.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 -...
979
980
  	},	/* A tablet like device, at least touch detection,
  		   two absolute axes */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
981
  	{
464b24157   Dmitry Torokhov   Input: mousedev -...
982
983
984
  		.flags = INPUT_DEVICE_ID_MATCH_EVBIT |
  				INPUT_DEVICE_ID_MATCH_KEYBIT |
  				INPUT_DEVICE_ID_MATCH_ABSBIT,
7b19ada2e   Jiri Slaby   get rid of input ...
985
986
987
988
989
990
  		.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
991
  	},	/* A touchpad */
6724f9346   Micah Parrish   Input: mousedev -...
992
993
994
995
  	{
  		.flags = INPUT_DEVICE_ID_MATCH_EVBIT |
  			INPUT_DEVICE_ID_MATCH_KEYBIT |
  			INPUT_DEVICE_ID_MATCH_ABSBIT,
d182c10c8   Dmitry Torokhov   Input: mousedev -...
996
  		.evbit = { BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS) },
6724f9346   Micah Parrish   Input: mousedev -...
997
998
999
1000
  		.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
1001

d0ffb9be8   Dmitry Torokhov   Input: handlers -...
1002
  	{ },	/* Terminating entry */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
  };
  
  MODULE_DEVICE_TABLE(input, mousedev_ids);
  
  static struct input_handler mousedev_handler = {
  	.event =	mousedev_event,
  	.connect =	mousedev_connect,
  	.disconnect =	mousedev_disconnect,
  	.fops =		&mousedev_fops,
  	.minor =	MOUSEDEV_MINOR_BASE,
  	.name =		"mousedev",
  	.id_table =	mousedev_ids,
  };
  
  #ifdef CONFIG_INPUT_MOUSEDEV_PSAUX
  static struct miscdevice psaux_mouse = {
  	PSMOUSE_MINOR, "psaux", &mousedev_fops
  };
  static int psaux_registered;
  #endif
  
  static int __init mousedev_init(void)
  {
4263cf0fa   Dmitry Torokhov   Input: make input...
1026
  	int error;
9657d75c5   Dmitry Torokhov   Input: convert fr...
1027
1028
1029
  	mousedev_mix = mousedev_create(NULL, &mousedev_handler, MOUSEDEV_MIX);
  	if (IS_ERR(mousedev_mix))
  		return PTR_ERR(mousedev_mix);
4263cf0fa   Dmitry Torokhov   Input: make input...
1030
  	error = input_register_handler(&mousedev_handler);
9657d75c5   Dmitry Torokhov   Input: convert fr...
1031
1032
  	if (error) {
  		mousedev_destroy(mousedev_mix);
4263cf0fa   Dmitry Torokhov   Input: make input...
1033
  		return error;
4263cf0fa   Dmitry Torokhov   Input: make input...
1034
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1035
1036
  
  #ifdef CONFIG_INPUT_MOUSEDEV_PSAUX
4263cf0fa   Dmitry Torokhov   Input: make input...
1037
1038
  	error = misc_register(&psaux_mouse);
  	if (error)
da0c49011   Joe Perches   Input: use pr_fmt...
1039
1040
1041
  		pr_warning("could not register psaux device, error: %d
  ",
  			   error);
4263cf0fa   Dmitry Torokhov   Input: make input...
1042
1043
  	else
  		psaux_registered = 1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1044
  #endif
da0c49011   Joe Perches   Input: use pr_fmt...
1045
1046
  	pr_info("PS/2 mouse device common for all mice
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
  
  	return 0;
  }
  
  static void __exit mousedev_exit(void)
  {
  #ifdef CONFIG_INPUT_MOUSEDEV_PSAUX
  	if (psaux_registered)
  		misc_deregister(&psaux_mouse);
  #endif
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1057
  	input_unregister_handler(&mousedev_handler);
9657d75c5   Dmitry Torokhov   Input: convert fr...
1058
  	mousedev_destroy(mousedev_mix);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1059
1060
1061
1062
  }
  
  module_init(mousedev_init);
  module_exit(mousedev_exit);