Blame view

drivers/input/joydev.c 25.9 KB
2874c5fd2   Thomas Gleixner   treewide: Replace...
1
  // SPDX-License-Identifier: GPL-2.0-or-later
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2
3
4
5
6
  /*
   * Joystick device driver for the input driver suite.
   *
   * Copyright (c) 1999-2002 Vojtech Pavlik
   * Copyright (c) 1999 Colin Van Dyke
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
7
   */
da0c49011   Joe Perches   Input: use pr_fmt...
8
  #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
9
  #include <asm/io.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
10
11
12
13
14
15
  #include <linux/delay.h>
  #include <linux/errno.h>
  #include <linux/joystick.h>
  #include <linux/input.h>
  #include <linux/kernel.h>
  #include <linux/major.h>
a99bbaf5e   Alexey Dobriyan   headers: remove s...
16
  #include <linux/sched.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
17
18
  #include <linux/slab.h>
  #include <linux/mm.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
19
20
21
  #include <linux/module.h>
  #include <linux/poll.h>
  #include <linux/init.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
22
  #include <linux/device.h>
7f8d4cad1   Dmitry Torokhov   Input: extend the...
23
  #include <linux/cdev.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
24
25
26
27
28
29
30
31
32
  
  MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
  MODULE_DESCRIPTION("Joystick device interfaces");
  MODULE_SUPPORTED_DEVICE("input/js");
  MODULE_LICENSE("GPL");
  
  #define JOYDEV_MINOR_BASE	0
  #define JOYDEV_MINORS		16
  #define JOYDEV_BUFFER_SIZE	64
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
33
  struct joydev {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
34
  	int open;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
35
36
  	struct input_handle handle;
  	wait_queue_head_t wait;
d0ffb9be8   Dmitry Torokhov   Input: handlers -...
37
  	struct list_head client_list;
b126207cc   Dmitry Torokhov   Input: joydev - i...
38
39
  	spinlock_t client_lock; /* protects client_list */
  	struct mutex mutex;
9657d75c5   Dmitry Torokhov   Input: convert fr...
40
  	struct device dev;
7f8d4cad1   Dmitry Torokhov   Input: extend the...
41
  	struct cdev cdev;
20da92de8   Dmitry Torokhov   Input: change inp...
42
  	bool exist;
9657d75c5   Dmitry Torokhov   Input: convert fr...
43

81c2a3ba4   Daniel Mack   Input: use ABS_CN...
44
  	struct js_corr corr[ABS_CNT];
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
45
46
47
48
49
  	struct JS_DATA_SAVE_TYPE glue;
  	int nabs;
  	int nkey;
  	__u16 keymap[KEY_MAX - BTN_MISC + 1];
  	__u16 keypam[KEY_MAX - BTN_MISC + 1];
81c2a3ba4   Daniel Mack   Input: use ABS_CN...
50
51
52
  	__u8 absmap[ABS_CNT];
  	__u8 abspam[ABS_CNT];
  	__s16 abs[ABS_CNT];
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
53
  };
d0ffb9be8   Dmitry Torokhov   Input: handlers -...
54
  struct joydev_client {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
55
56
57
58
  	struct js_event buffer[JOYDEV_BUFFER_SIZE];
  	int head;
  	int tail;
  	int startup;
b126207cc   Dmitry Torokhov   Input: joydev - i...
59
  	spinlock_t buffer_lock; /* protects access to buffer, head and tail */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
60
61
62
63
  	struct fasync_struct *fasync;
  	struct joydev *joydev;
  	struct list_head node;
  };
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
64
65
66
  static int joydev_correct(int value, struct js_corr *corr)
  {
  	switch (corr->type) {
b126207cc   Dmitry Torokhov   Input: joydev - i...
67
68
69
70
71
72
73
74
75
76
77
78
  
  	case JS_CORR_NONE:
  		break;
  
  	case JS_CORR_BROKEN:
  		value = value > corr->coef[0] ? (value < corr->coef[1] ? 0 :
  			((corr->coef[3] * (value - corr->coef[1])) >> 14)) :
  			((corr->coef[2] * (value - corr->coef[0])) >> 14);
  		break;
  
  	default:
  		return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
79
  	}
5ec69524d   Dmitry Torokhov   Input: joydev - u...
80
  	return clamp(value, -32767, 32767);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
81
  }
b126207cc   Dmitry Torokhov   Input: joydev - i...
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
  static void joydev_pass_event(struct joydev_client *client,
  			      struct js_event *event)
  {
  	struct joydev *joydev = client->joydev;
  
  	/*
  	 * IRQs already disabled, just acquire the lock
  	 */
  	spin_lock(&client->buffer_lock);
  
  	client->buffer[client->head] = *event;
  
  	if (client->startup == joydev->nabs + joydev->nkey) {
  		client->head++;
  		client->head &= JOYDEV_BUFFER_SIZE - 1;
  		if (client->tail == client->head)
  			client->startup = 0;
  	}
  
  	spin_unlock(&client->buffer_lock);
  
  	kill_fasync(&client->fasync, SIGIO, POLL_IN);
  }
  
  static void joydev_event(struct input_handle *handle,
  			 unsigned int type, unsigned int code, int value)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
108
109
  {
  	struct joydev *joydev = handle->private;
d0ffb9be8   Dmitry Torokhov   Input: handlers -...
110
  	struct joydev_client *client;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
111
112
113
  	struct js_event event;
  
  	switch (type) {
b126207cc   Dmitry Torokhov   Input: joydev - i...
114
115
116
117
118
119
120
  	case EV_KEY:
  		if (code < BTN_MISC || value == 2)
  			return;
  		event.type = JS_EVENT_BUTTON;
  		event.number = joydev->keymap[code - BTN_MISC];
  		event.value = value;
  		break;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
121

b126207cc   Dmitry Torokhov   Input: joydev - i...
122
123
124
125
126
127
  	case EV_ABS:
  		event.type = JS_EVENT_AXIS;
  		event.number = joydev->absmap[code];
  		event.value = joydev_correct(value,
  					&joydev->corr[event.number]);
  		if (event.value == joydev->abs[event.number])
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
128
  			return;
b126207cc   Dmitry Torokhov   Input: joydev - i...
129
130
131
132
133
  		joydev->abs[event.number] = event.value;
  		break;
  
  	default:
  		return;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
134
  	}
6f5eacfc1   Tobias Klauser   Input: joydev - r...
135
  	event.time = jiffies_to_msecs(jiffies);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
136

82ba56c27   Dmitry Torokhov   Input: use full R...
137
  	rcu_read_lock();
b126207cc   Dmitry Torokhov   Input: joydev - i...
138
139
  	list_for_each_entry_rcu(client, &joydev->client_list, node)
  		joydev_pass_event(client, &event);
82ba56c27   Dmitry Torokhov   Input: use full R...
140
  	rcu_read_unlock();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
141
142
143
144
145
146
  
  	wake_up_interruptible(&joydev->wait);
  }
  
  static int joydev_fasync(int fd, struct file *file, int on)
  {
d0ffb9be8   Dmitry Torokhov   Input: handlers -...
147
  	struct joydev_client *client = file->private_data;
1e0afb288   Dmitry Torokhov   Input: fix format...
148

60aa49243   Jonathan Corbet   Rationalize fasyn...
149
  	return fasync_helper(fd, file, on, &client->fasync);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
150
  }
9657d75c5   Dmitry Torokhov   Input: convert fr...
151
  static void joydev_free(struct device *dev)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
152
  {
9657d75c5   Dmitry Torokhov   Input: convert fr...
153
  	struct joydev *joydev = container_of(dev, struct joydev, dev);
a7097ff89   Dmitry Torokhov   Input: make sure ...
154
  	input_put_device(joydev->handle.dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
155
156
  	kfree(joydev);
  }
b126207cc   Dmitry Torokhov   Input: joydev - i...
157
158
159
160
161
162
  static void joydev_attach_client(struct joydev *joydev,
  				 struct joydev_client *client)
  {
  	spin_lock(&joydev->client_lock);
  	list_add_tail_rcu(&client->node, &joydev->client_list);
  	spin_unlock(&joydev->client_lock);
b126207cc   Dmitry Torokhov   Input: joydev - i...
163
164
165
166
167
168
169
170
  }
  
  static void joydev_detach_client(struct joydev *joydev,
  				 struct joydev_client *client)
  {
  	spin_lock(&joydev->client_lock);
  	list_del_rcu(&client->node);
  	spin_unlock(&joydev->client_lock);
82ba56c27   Dmitry Torokhov   Input: use full R...
171
  	synchronize_rcu();
b126207cc   Dmitry Torokhov   Input: joydev - i...
172
  }
45536d373   Raphael Assenat   Input: joydev - d...
173
174
175
176
177
178
179
180
181
182
  static void joydev_refresh_state(struct joydev *joydev)
  {
  	struct input_dev *dev = joydev->handle.dev;
  	int i, val;
  
  	for (i = 0; i < joydev->nabs; i++) {
  		val = input_abs_get_val(dev, joydev->abspam[i]);
  		joydev->abs[i] = joydev_correct(val, &joydev->corr[i]);
  	}
  }
b126207cc   Dmitry Torokhov   Input: joydev - i...
183
184
185
186
187
188
189
190
191
192
  static int joydev_open_device(struct joydev *joydev)
  {
  	int retval;
  
  	retval = mutex_lock_interruptible(&joydev->mutex);
  	if (retval)
  		return retval;
  
  	if (!joydev->exist)
  		retval = -ENODEV;
064450140   Oliver Neukum   Input: fix open c...
193
  	else if (!joydev->open++) {
b126207cc   Dmitry Torokhov   Input: joydev - i...
194
  		retval = input_open_device(&joydev->handle);
064450140   Oliver Neukum   Input: fix open c...
195
196
  		if (retval)
  			joydev->open--;
45536d373   Raphael Assenat   Input: joydev - d...
197
198
  		else
  			joydev_refresh_state(joydev);
064450140   Oliver Neukum   Input: fix open c...
199
  	}
b126207cc   Dmitry Torokhov   Input: joydev - i...
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
  
  	mutex_unlock(&joydev->mutex);
  	return retval;
  }
  
  static void joydev_close_device(struct joydev *joydev)
  {
  	mutex_lock(&joydev->mutex);
  
  	if (joydev->exist && !--joydev->open)
  		input_close_device(&joydev->handle);
  
  	mutex_unlock(&joydev->mutex);
  }
  
  /*
   * Wake up users waiting for IO so they can disconnect from
   * dead device.
   */
  static void joydev_hangup(struct joydev *joydev)
  {
  	struct joydev_client *client;
  
  	spin_lock(&joydev->client_lock);
  	list_for_each_entry(client, &joydev->client_list, node)
  		kill_fasync(&client->fasync, SIGIO, POLL_HUP);
  	spin_unlock(&joydev->client_lock);
  
  	wake_up_interruptible(&joydev->wait);
  }
d0ffb9be8   Dmitry Torokhov   Input: handlers -...
230
  static int joydev_release(struct inode *inode, struct file *file)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
231
  {
d0ffb9be8   Dmitry Torokhov   Input: handlers -...
232
233
  	struct joydev_client *client = file->private_data;
  	struct joydev *joydev = client->joydev;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
234

b126207cc   Dmitry Torokhov   Input: joydev - i...
235
  	joydev_detach_client(joydev, client);
d0ffb9be8   Dmitry Torokhov   Input: handlers -...
236
  	kfree(client);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
237

b126207cc   Dmitry Torokhov   Input: joydev - i...
238
  	joydev_close_device(joydev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
239

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
240
241
242
243
244
  	return 0;
  }
  
  static int joydev_open(struct inode *inode, struct file *file)
  {
7f8d4cad1   Dmitry Torokhov   Input: extend the...
245
246
  	struct joydev *joydev =
  			container_of(inode->i_cdev, struct joydev, cdev);
d0ffb9be8   Dmitry Torokhov   Input: handlers -...
247
  	struct joydev_client *client;
d542ed82f   Dmitry Torokhov   Input: handlers -...
248
  	int error;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
249

d0ffb9be8   Dmitry Torokhov   Input: handlers -...
250
  	client = kzalloc(sizeof(struct joydev_client), GFP_KERNEL);
7f8d4cad1   Dmitry Torokhov   Input: extend the...
251
252
  	if (!client)
  		return -ENOMEM;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
253

b126207cc   Dmitry Torokhov   Input: joydev - i...
254
  	spin_lock_init(&client->buffer_lock);
d0ffb9be8   Dmitry Torokhov   Input: handlers -...
255
  	client->joydev = joydev;
b126207cc   Dmitry Torokhov   Input: joydev - i...
256
  	joydev_attach_client(joydev, client);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
257

b126207cc   Dmitry Torokhov   Input: joydev - i...
258
259
260
  	error = joydev_open_device(joydev);
  	if (error)
  		goto err_free_client;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
261

d0ffb9be8   Dmitry Torokhov   Input: handlers -...
262
  	file->private_data = client;
c5bf68fe0   Kirill Smelkov   *: convert stream...
263
  	stream_open(inode, file);
3d7bbd457   Dmitry Torokhov   Input: mark input...
264

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
265
  	return 0;
9657d75c5   Dmitry Torokhov   Input: convert fr...
266
267
  
   err_free_client:
b126207cc   Dmitry Torokhov   Input: joydev - i...
268
  	joydev_detach_client(joydev, client);
9657d75c5   Dmitry Torokhov   Input: convert fr...
269
  	kfree(client);
9657d75c5   Dmitry Torokhov   Input: convert fr...
270
  	return error;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
271
  }
b126207cc   Dmitry Torokhov   Input: joydev - i...
272
273
274
  static int joydev_generate_startup_event(struct joydev_client *client,
  					 struct input_dev *input,
  					 struct js_event *event)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
275
  {
b126207cc   Dmitry Torokhov   Input: joydev - i...
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
  	struct joydev *joydev = client->joydev;
  	int have_event;
  
  	spin_lock_irq(&client->buffer_lock);
  
  	have_event = client->startup < joydev->nabs + joydev->nkey;
  
  	if (have_event) {
  
  		event->time = jiffies_to_msecs(jiffies);
  		if (client->startup < joydev->nkey) {
  			event->type = JS_EVENT_BUTTON | JS_EVENT_INIT;
  			event->number = client->startup;
  			event->value = !!test_bit(joydev->keypam[event->number],
  						  input->key);
  		} else {
  			event->type = JS_EVENT_AXIS | JS_EVENT_INIT;
  			event->number = client->startup - joydev->nkey;
  			event->value = joydev->abs[event->number];
  		}
  		client->startup++;
  	}
  
  	spin_unlock_irq(&client->buffer_lock);
  
  	return have_event;
  }
  
  static int joydev_fetch_next_event(struct joydev_client *client,
  				   struct js_event *event)
  {
  	int have_event;
  
  	spin_lock_irq(&client->buffer_lock);
  
  	have_event = client->head != client->tail;
  	if (have_event) {
  		*event = client->buffer[client->tail++];
  		client->tail &= JOYDEV_BUFFER_SIZE - 1;
  	}
  
  	spin_unlock_irq(&client->buffer_lock);
  
  	return have_event;
  }
  
  /*
   * Old joystick interface
   */
  static ssize_t joydev_0x_read(struct joydev_client *client,
  			      struct input_dev *input,
  			      char __user *buf)
  {
  	struct joydev *joydev = client->joydev;
  	struct JS_DATA_TYPE data;
  	int i;
  
  	spin_lock_irq(&input->event_lock);
  
  	/*
  	 * Get device state
  	 */
  	for (data.buttons = i = 0; i < 32 && i < joydev->nkey; i++)
  		data.buttons |=
  			test_bit(joydev->keypam[i], input->key) ? (1 << i) : 0;
  	data.x = (joydev->abs[0] / 256 + 128) >> joydev->glue.JS_CORR.x;
  	data.y = (joydev->abs[1] / 256 + 128) >> joydev->glue.JS_CORR.y;
  
  	/*
  	 * Reset reader's event queue
  	 */
  	spin_lock(&client->buffer_lock);
  	client->startup = 0;
  	client->tail = client->head;
  	spin_unlock(&client->buffer_lock);
  
  	spin_unlock_irq(&input->event_lock);
  
  	if (copy_to_user(buf, &data, sizeof(struct JS_DATA_TYPE)))
  		return -EFAULT;
  
  	return sizeof(struct JS_DATA_TYPE);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
358
  }
b126207cc   Dmitry Torokhov   Input: joydev - i...
359
360
361
362
363
364
365
366
367
368
  static inline int joydev_data_pending(struct joydev_client *client)
  {
  	struct joydev *joydev = client->joydev;
  
  	return client->startup < joydev->nabs + joydev->nkey ||
  		client->head != client->tail;
  }
  
  static ssize_t joydev_read(struct file *file, char __user *buf,
  			   size_t count, loff_t *ppos)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
369
  {
d0ffb9be8   Dmitry Torokhov   Input: handlers -...
370
371
  	struct joydev_client *client = file->private_data;
  	struct joydev *joydev = client->joydev;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
372
  	struct input_dev *input = joydev->handle.dev;
b126207cc   Dmitry Torokhov   Input: joydev - i...
373
374
  	struct js_event event;
  	int retval;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
375

d0ffb9be8   Dmitry Torokhov   Input: handlers -...
376
  	if (!joydev->exist)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
377
378
379
380
  		return -ENODEV;
  
  	if (count < sizeof(struct js_event))
  		return -EINVAL;
b126207cc   Dmitry Torokhov   Input: joydev - i...
381
382
  	if (count == sizeof(struct JS_DATA_TYPE))
  		return joydev_0x_read(client, input, buf);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
383

b126207cc   Dmitry Torokhov   Input: joydev - i...
384
  	if (!joydev_data_pending(client) && (file->f_flags & O_NONBLOCK))
1e0afb288   Dmitry Torokhov   Input: fix format...
385
  		return -EAGAIN;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
386

d0ffb9be8   Dmitry Torokhov   Input: handlers -...
387
  	retval = wait_event_interruptible(joydev->wait,
b126207cc   Dmitry Torokhov   Input: joydev - i...
388
  			!joydev->exist || joydev_data_pending(client));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
389
390
  	if (retval)
  		return retval;
d0ffb9be8   Dmitry Torokhov   Input: handlers -...
391
  	if (!joydev->exist)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
392
  		return -ENODEV;
b126207cc   Dmitry Torokhov   Input: joydev - i...
393
394
  	while (retval + sizeof(struct js_event) <= count &&
  	       joydev_generate_startup_event(client, input, &event)) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
395
396
397
  
  		if (copy_to_user(buf + retval, &event, sizeof(struct js_event)))
  			return -EFAULT;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
398
399
  		retval += sizeof(struct js_event);
  	}
b126207cc   Dmitry Torokhov   Input: joydev - i...
400
401
  	while (retval + sizeof(struct js_event) <= count &&
  	       joydev_fetch_next_event(client, &event)) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
402

b126207cc   Dmitry Torokhov   Input: joydev - i...
403
  		if (copy_to_user(buf + retval, &event, sizeof(struct js_event)))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
404
  			return -EFAULT;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
405
406
407
408
409
410
411
  		retval += sizeof(struct js_event);
  	}
  
  	return retval;
  }
  
  /* No kernel lock - fine */
afc9a42b7   Al Viro   the rest of drive...
412
  static __poll_t joydev_poll(struct file *file, poll_table *wait)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
413
  {
d0ffb9be8   Dmitry Torokhov   Input: handlers -...
414
415
  	struct joydev_client *client = file->private_data;
  	struct joydev *joydev = client->joydev;
1e0afb288   Dmitry Torokhov   Input: fix format...
416

d0ffb9be8   Dmitry Torokhov   Input: handlers -...
417
  	poll_wait(file, &joydev->wait, wait);
a9a08845e   Linus Torvalds   vfs: do bulk POLL...
418
419
  	return (joydev_data_pending(client) ? (EPOLLIN | EPOLLRDNORM) : 0) |
  		(joydev->exist ?  0 : (EPOLLHUP | EPOLLERR));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
420
  }
999b874f4   Stephen Kitt   Input: joydev - v...
421
422
423
424
425
426
427
428
429
430
  static int joydev_handle_JSIOCSAXMAP(struct joydev *joydev,
  				     void __user *argp, size_t len)
  {
  	__u8 *abspam;
  	int i;
  	int retval = 0;
  
  	len = min(len, sizeof(joydev->abspam));
  
  	/* Validate the map. */
5702222c9   Javier Martinez Canillas   Input: joydev - u...
431
  	abspam = memdup_user(argp, len);
5b21e3c74   Javier Martinez Canillas   Input: joydev - f...
432
433
  	if (IS_ERR(abspam))
  		return PTR_ERR(abspam);
999b874f4   Stephen Kitt   Input: joydev - v...
434
435
436
437
438
439
440
441
442
  
  	for (i = 0; i < joydev->nabs; i++) {
  		if (abspam[i] > ABS_MAX) {
  			retval = -EINVAL;
  			goto out;
  		}
  	}
  
  	memcpy(joydev->abspam, abspam, len);
d2520a426   Kenneth Waters   Input: joydev - f...
443
444
  	for (i = 0; i < joydev->nabs; i++)
  		joydev->absmap[joydev->abspam[i]] = i;
999b874f4   Stephen Kitt   Input: joydev - v...
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
   out:
  	kfree(abspam);
  	return retval;
  }
  
  static int joydev_handle_JSIOCSBTNMAP(struct joydev *joydev,
  				      void __user *argp, size_t len)
  {
  	__u16 *keypam;
  	int i;
  	int retval = 0;
  
  	len = min(len, sizeof(joydev->keypam));
  
  	/* Validate the map. */
5702222c9   Javier Martinez Canillas   Input: joydev - u...
460
  	keypam = memdup_user(argp, len);
5b21e3c74   Javier Martinez Canillas   Input: joydev - f...
461
462
  	if (IS_ERR(keypam))
  		return PTR_ERR(keypam);
999b874f4   Stephen Kitt   Input: joydev - v...
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
  
  	for (i = 0; i < joydev->nkey; i++) {
  		if (keypam[i] > KEY_MAX || keypam[i] < BTN_MISC) {
  			retval = -EINVAL;
  			goto out;
  		}
  	}
  
  	memcpy(joydev->keypam, keypam, len);
  
  	for (i = 0; i < joydev->nkey; i++)
  		joydev->keymap[keypam[i] - BTN_MISC] = i;
  
   out:
  	kfree(keypam);
  	return retval;
  }
b126207cc   Dmitry Torokhov   Input: joydev - i...
480
481
  static int joydev_ioctl_common(struct joydev *joydev,
  				unsigned int cmd, void __user *argp)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
482
  {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
483
  	struct input_dev *dev = joydev->handle.dev;
ec8b4b708   Stephen Kitt   Input: joydev - d...
484
  	size_t len;
987a6c029   Daniel Mack   Input: switch to ...
485
  	int i;
ec8b4b708   Stephen Kitt   Input: joydev - d...
486
  	const char *name;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
487

ec8b4b708   Stephen Kitt   Input: joydev - d...
488
  	/* Process fixed-sized commands. */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
489
  	switch (cmd) {
b126207cc   Dmitry Torokhov   Input: joydev - i...
490
491
  	case JS_SET_CAL:
  		return copy_from_user(&joydev->glue.JS_CORR, argp,
024ac44c7   Jeremy Fitzhardinge   Input: This patch...
492
  				sizeof(joydev->glue.JS_CORR)) ? -EFAULT : 0;
1e0afb288   Dmitry Torokhov   Input: fix format...
493

b126207cc   Dmitry Torokhov   Input: joydev - i...
494
495
  	case JS_GET_CAL:
  		return copy_to_user(argp, &joydev->glue.JS_CORR,
024ac44c7   Jeremy Fitzhardinge   Input: This patch...
496
  				sizeof(joydev->glue.JS_CORR)) ? -EFAULT : 0;
1e0afb288   Dmitry Torokhov   Input: fix format...
497

b126207cc   Dmitry Torokhov   Input: joydev - i...
498
499
  	case JS_SET_TIMEOUT:
  		return get_user(joydev->glue.JS_TIMEOUT, (s32 __user *) argp);
1e0afb288   Dmitry Torokhov   Input: fix format...
500

b126207cc   Dmitry Torokhov   Input: joydev - i...
501
502
  	case JS_GET_TIMEOUT:
  		return put_user(joydev->glue.JS_TIMEOUT, (s32 __user *) argp);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
503

b126207cc   Dmitry Torokhov   Input: joydev - i...
504
505
  	case JSIOCGVERSION:
  		return put_user(JS_VERSION, (__u32 __user *) argp);
1e0afb288   Dmitry Torokhov   Input: fix format...
506

b126207cc   Dmitry Torokhov   Input: joydev - i...
507
508
  	case JSIOCGAXES:
  		return put_user(joydev->nabs, (__u8 __user *) argp);
1e0afb288   Dmitry Torokhov   Input: fix format...
509

b126207cc   Dmitry Torokhov   Input: joydev - i...
510
511
  	case JSIOCGBUTTONS:
  		return put_user(joydev->nkey, (__u8 __user *) argp);
1e0afb288   Dmitry Torokhov   Input: fix format...
512

b126207cc   Dmitry Torokhov   Input: joydev - i...
513
514
515
  	case JSIOCSCORR:
  		if (copy_from_user(joydev->corr, argp,
  			      sizeof(joydev->corr[0]) * joydev->nabs))
987a6c029   Daniel Mack   Input: switch to ...
516
  			return -EFAULT;
1e0afb288   Dmitry Torokhov   Input: fix format...
517

b126207cc   Dmitry Torokhov   Input: joydev - i...
518
  		for (i = 0; i < joydev->nabs; i++) {
987a6c029   Daniel Mack   Input: switch to ...
519
520
  			int val = input_abs_get_val(dev, joydev->abspam[i]);
  			joydev->abs[i] = joydev_correct(val, &joydev->corr[i]);
b126207cc   Dmitry Torokhov   Input: joydev - i...
521
522
  		}
  		return 0;
1e0afb288   Dmitry Torokhov   Input: fix format...
523

b126207cc   Dmitry Torokhov   Input: joydev - i...
524
525
526
  	case JSIOCGCORR:
  		return copy_to_user(argp, joydev->corr,
  			sizeof(joydev->corr[0]) * joydev->nabs) ? -EFAULT : 0;
ec8b4b708   Stephen Kitt   Input: joydev - d...
527
528
529
530
531
532
533
534
535
536
  	}
  
  	/*
  	 * Process variable-sized commands (the axis and button map commands
  	 * are considered variable-sized to decouple them from the values of
  	 * ABS_MAX and KEY_MAX).
  	 */
  	switch (cmd & ~IOCSIZE_MASK) {
  
  	case (JSIOCSAXMAP & ~IOCSIZE_MASK):
999b874f4   Stephen Kitt   Input: joydev - v...
537
  		return joydev_handle_JSIOCSAXMAP(joydev, argp, _IOC_SIZE(cmd));
b126207cc   Dmitry Torokhov   Input: joydev - i...
538

ec8b4b708   Stephen Kitt   Input: joydev - d...
539
540
  	case (JSIOCGAXMAP & ~IOCSIZE_MASK):
  		len = min_t(size_t, _IOC_SIZE(cmd), sizeof(joydev->abspam));
999b874f4   Stephen Kitt   Input: joydev - v...
541
  		return copy_to_user(argp, joydev->abspam, len) ? -EFAULT : len;
ec8b4b708   Stephen Kitt   Input: joydev - d...
542
543
  
  	case (JSIOCSBTNMAP & ~IOCSIZE_MASK):
999b874f4   Stephen Kitt   Input: joydev - v...
544
  		return joydev_handle_JSIOCSBTNMAP(joydev, argp, _IOC_SIZE(cmd));
b126207cc   Dmitry Torokhov   Input: joydev - i...
545

ec8b4b708   Stephen Kitt   Input: joydev - d...
546
547
  	case (JSIOCGBTNMAP & ~IOCSIZE_MASK):
  		len = min_t(size_t, _IOC_SIZE(cmd), sizeof(joydev->keypam));
999b874f4   Stephen Kitt   Input: joydev - v...
548
  		return copy_to_user(argp, joydev->keypam, len) ? -EFAULT : len;
b126207cc   Dmitry Torokhov   Input: joydev - i...
549

ec8b4b708   Stephen Kitt   Input: joydev - d...
550
551
552
553
554
555
556
  	case JSIOCGNAME(0):
  		name = dev->name;
  		if (!name)
  			return 0;
  
  		len = min_t(size_t, _IOC_SIZE(cmd), strlen(name) + 1);
  		return copy_to_user(argp, name, len) ? -EFAULT : len;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
557
  	}
ec8b4b708   Stephen Kitt   Input: joydev - d...
558

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
559
560
  	return -EINVAL;
  }
024ac44c7   Jeremy Fitzhardinge   Input: This patch...
561
  #ifdef CONFIG_COMPAT
b126207cc   Dmitry Torokhov   Input: joydev - i...
562
563
  static long joydev_compat_ioctl(struct file *file,
  				unsigned int cmd, unsigned long arg)
024ac44c7   Jeremy Fitzhardinge   Input: This patch...
564
  {
d0ffb9be8   Dmitry Torokhov   Input: handlers -...
565
566
  	struct joydev_client *client = file->private_data;
  	struct joydev *joydev = client->joydev;
024ac44c7   Jeremy Fitzhardinge   Input: This patch...
567
568
569
  	void __user *argp = (void __user *)arg;
  	s32 tmp32;
  	struct JS_DATA_SAVE_TYPE_32 ds32;
b126207cc   Dmitry Torokhov   Input: joydev - i...
570
  	int retval;
024ac44c7   Jeremy Fitzhardinge   Input: This patch...
571

b126207cc   Dmitry Torokhov   Input: joydev - i...
572
573
574
575
576
577
578
579
580
581
  	retval = mutex_lock_interruptible(&joydev->mutex);
  	if (retval)
  		return retval;
  
  	if (!joydev->exist) {
  		retval = -ENODEV;
  		goto out;
  	}
  
  	switch (cmd) {
1e0afb288   Dmitry Torokhov   Input: fix format...
582

024ac44c7   Jeremy Fitzhardinge   Input: This patch...
583
  	case JS_SET_TIMELIMIT:
b126207cc   Dmitry Torokhov   Input: joydev - i...
584
585
  		retval = get_user(tmp32, (s32 __user *) arg);
  		if (retval == 0)
024ac44c7   Jeremy Fitzhardinge   Input: This patch...
586
587
  			joydev->glue.JS_TIMELIMIT = tmp32;
  		break;
b126207cc   Dmitry Torokhov   Input: joydev - i...
588

024ac44c7   Jeremy Fitzhardinge   Input: This patch...
589
590
  	case JS_GET_TIMELIMIT:
  		tmp32 = joydev->glue.JS_TIMELIMIT;
b126207cc   Dmitry Torokhov   Input: joydev - i...
591
  		retval = put_user(tmp32, (s32 __user *) arg);
024ac44c7   Jeremy Fitzhardinge   Input: This patch...
592
593
594
  		break;
  
  	case JS_SET_ALL:
b126207cc   Dmitry Torokhov   Input: joydev - i...
595
596
597
  		retval = copy_from_user(&ds32, argp,
  					sizeof(ds32)) ? -EFAULT : 0;
  		if (retval == 0) {
024ac44c7   Jeremy Fitzhardinge   Input: This patch...
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
  			joydev->glue.JS_TIMEOUT    = ds32.JS_TIMEOUT;
  			joydev->glue.BUSY          = ds32.BUSY;
  			joydev->glue.JS_EXPIRETIME = ds32.JS_EXPIRETIME;
  			joydev->glue.JS_TIMELIMIT  = ds32.JS_TIMELIMIT;
  			joydev->glue.JS_SAVE       = ds32.JS_SAVE;
  			joydev->glue.JS_CORR       = ds32.JS_CORR;
  		}
  		break;
  
  	case JS_GET_ALL:
  		ds32.JS_TIMEOUT    = joydev->glue.JS_TIMEOUT;
  		ds32.BUSY          = joydev->glue.BUSY;
  		ds32.JS_EXPIRETIME = joydev->glue.JS_EXPIRETIME;
  		ds32.JS_TIMELIMIT  = joydev->glue.JS_TIMELIMIT;
  		ds32.JS_SAVE       = joydev->glue.JS_SAVE;
  		ds32.JS_CORR       = joydev->glue.JS_CORR;
b126207cc   Dmitry Torokhov   Input: joydev - i...
614
  		retval = copy_to_user(argp, &ds32, sizeof(ds32)) ? -EFAULT : 0;
024ac44c7   Jeremy Fitzhardinge   Input: This patch...
615
616
617
  		break;
  
  	default:
b126207cc   Dmitry Torokhov   Input: joydev - i...
618
619
  		retval = joydev_ioctl_common(joydev, cmd, argp);
  		break;
024ac44c7   Jeremy Fitzhardinge   Input: This patch...
620
  	}
b126207cc   Dmitry Torokhov   Input: joydev - i...
621
622
623
624
  
   out:
  	mutex_unlock(&joydev->mutex);
  	return retval;
024ac44c7   Jeremy Fitzhardinge   Input: This patch...
625
626
  }
  #endif /* CONFIG_COMPAT */
b126207cc   Dmitry Torokhov   Input: joydev - i...
627
628
  static long joydev_ioctl(struct file *file,
  			 unsigned int cmd, unsigned long arg)
024ac44c7   Jeremy Fitzhardinge   Input: This patch...
629
  {
d0ffb9be8   Dmitry Torokhov   Input: handlers -...
630
631
  	struct joydev_client *client = file->private_data;
  	struct joydev *joydev = client->joydev;
024ac44c7   Jeremy Fitzhardinge   Input: This patch...
632
  	void __user *argp = (void __user *)arg;
b126207cc   Dmitry Torokhov   Input: joydev - i...
633
  	int retval;
024ac44c7   Jeremy Fitzhardinge   Input: This patch...
634

b126207cc   Dmitry Torokhov   Input: joydev - i...
635
636
637
  	retval = mutex_lock_interruptible(&joydev->mutex);
  	if (retval)
  		return retval;
024ac44c7   Jeremy Fitzhardinge   Input: This patch...
638

b126207cc   Dmitry Torokhov   Input: joydev - i...
639
640
641
  	if (!joydev->exist) {
  		retval = -ENODEV;
  		goto out;
024ac44c7   Jeremy Fitzhardinge   Input: This patch...
642
  	}
b126207cc   Dmitry Torokhov   Input: joydev - i...
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
  
  	switch (cmd) {
  
  	case JS_SET_TIMELIMIT:
  		retval = get_user(joydev->glue.JS_TIMELIMIT,
  				  (long __user *) arg);
  		break;
  
  	case JS_GET_TIMELIMIT:
  		retval = put_user(joydev->glue.JS_TIMELIMIT,
  				  (long __user *) arg);
  		break;
  
  	case JS_SET_ALL:
  		retval = copy_from_user(&joydev->glue, argp,
41091ad0b   Baodong Chen   Input: random for...
658
  					sizeof(joydev->glue)) ? -EFAULT : 0;
b126207cc   Dmitry Torokhov   Input: joydev - i...
659
660
661
662
663
664
665
666
667
668
669
670
671
672
  		break;
  
  	case JS_GET_ALL:
  		retval = copy_to_user(argp, &joydev->glue,
  				      sizeof(joydev->glue)) ? -EFAULT : 0;
  		break;
  
  	default:
  		retval = joydev_ioctl_common(joydev, cmd, argp);
  		break;
  	}
   out:
  	mutex_unlock(&joydev->mutex);
  	return retval;
024ac44c7   Jeremy Fitzhardinge   Input: This patch...
673
  }
66e661188   Dmitry Torokhov   Input: constify i...
674
  static const struct file_operations joydev_fops = {
b126207cc   Dmitry Torokhov   Input: joydev - i...
675
676
677
678
679
680
  	.owner		= THIS_MODULE,
  	.read		= joydev_read,
  	.poll		= joydev_poll,
  	.open		= joydev_open,
  	.release	= joydev_release,
  	.unlocked_ioctl	= joydev_ioctl,
024ac44c7   Jeremy Fitzhardinge   Input: This patch...
681
  #ifdef CONFIG_COMPAT
b126207cc   Dmitry Torokhov   Input: joydev - i...
682
  	.compat_ioctl	= joydev_compat_ioctl,
024ac44c7   Jeremy Fitzhardinge   Input: This patch...
683
  #endif
b126207cc   Dmitry Torokhov   Input: joydev - i...
684
  	.fasync		= joydev_fasync,
6038f373a   Arnd Bergmann   llseek: automatic...
685
  	.llseek		= no_llseek,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
686
  };
b126207cc   Dmitry Torokhov   Input: joydev - i...
687
  /*
25985edce   Lucas De Marchi   Fix common misspe...
688
   * Mark device non-existent. This disables writes, ioctls and
b126207cc   Dmitry Torokhov   Input: joydev - i...
689
690
691
692
693
694
   * prevents new users from opening the device. Already posted
   * blocking reads will stay, however new ones will fail.
   */
  static void joydev_mark_dead(struct joydev *joydev)
  {
  	mutex_lock(&joydev->mutex);
20da92de8   Dmitry Torokhov   Input: change inp...
695
  	joydev->exist = false;
b126207cc   Dmitry Torokhov   Input: joydev - i...
696
697
698
699
700
701
702
703
704
  	mutex_unlock(&joydev->mutex);
  }
  
  static void joydev_cleanup(struct joydev *joydev)
  {
  	struct input_handle *handle = &joydev->handle;
  
  	joydev_mark_dead(joydev);
  	joydev_hangup(joydev);
7f8d4cad1   Dmitry Torokhov   Input: extend the...
705

25985edce   Lucas De Marchi   Fix common misspe...
706
  	/* joydev is marked dead so no one else accesses joydev->open */
b126207cc   Dmitry Torokhov   Input: joydev - i...
707
708
709
  	if (joydev->open)
  		input_close_device(handle);
  }
20ac95d52   Roderick Colenbrander   Input: joydev - b...
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
  /*
   * These codes are copied from from hid-ids.h, unfortunately there is no common
   * usb_ids/bt_ids.h header.
   */
  #define USB_VENDOR_ID_SONY			0x054c
  #define USB_DEVICE_ID_SONY_PS3_CONTROLLER		0x0268
  #define USB_DEVICE_ID_SONY_PS4_CONTROLLER		0x05c4
  #define USB_DEVICE_ID_SONY_PS4_CONTROLLER_2		0x09cc
  #define USB_DEVICE_ID_SONY_PS4_CONTROLLER_DONGLE	0x0ba0
  
  #define USB_VENDOR_ID_THQ			0x20d6
  #define USB_DEVICE_ID_THQ_PS3_UDRAW			0xcb17
  
  #define ACCEL_DEV(vnd, prd)						\
  	{								\
  		.flags = INPUT_DEVICE_ID_MATCH_VENDOR |			\
  				INPUT_DEVICE_ID_MATCH_PRODUCT |		\
  				INPUT_DEVICE_ID_MATCH_PROPBIT,		\
  		.vendor = (vnd),					\
  		.product = (prd),					\
  		.propbit = { BIT_MASK(INPUT_PROP_ACCELEROMETER) },	\
  	}
  
  static const struct input_device_id joydev_blacklist[] = {
  	/* Avoid touchpads and touchscreens */
  	{
  		.flags = INPUT_DEVICE_ID_MATCH_EVBIT |
  				INPUT_DEVICE_ID_MATCH_KEYBIT,
  		.evbit = { BIT_MASK(EV_KEY) },
  		.keybit = { [BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH) },
  	},
  	/* Avoid tablets, digitisers and similar devices */
  	{
  		.flags = INPUT_DEVICE_ID_MATCH_EVBIT |
  				INPUT_DEVICE_ID_MATCH_KEYBIT,
  		.evbit = { BIT_MASK(EV_KEY) },
  		.keybit = { [BIT_WORD(BTN_DIGI)] = BIT_MASK(BTN_DIGI) },
  	},
  	/* Disable accelerometers on composite devices */
  	ACCEL_DEV(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_CONTROLLER),
  	ACCEL_DEV(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS4_CONTROLLER),
  	ACCEL_DEV(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS4_CONTROLLER_2),
  	ACCEL_DEV(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS4_CONTROLLER_DONGLE),
  	ACCEL_DEV(USB_VENDOR_ID_THQ, USB_DEVICE_ID_THQ_PS3_UDRAW),
  	{ /* sentinel */ }
  };
  
  static bool joydev_dev_is_blacklisted(struct input_dev *dev)
  {
  	const struct input_device_id *id;
  
  	for (id = joydev_blacklist; id->flags; id++) {
  		if (input_match_device_id(dev, id)) {
  			dev_dbg(&dev->dev,
  				"joydev: blacklisting '%s'
  ", dev->name);
  			return true;
  		}
  	}
  
  	return false;
  }
15397f153   Thomas Hellstrom   Input: joydev - d...
772
773
774
  static bool joydev_dev_is_absolute_mouse(struct input_dev *dev)
  {
  	DECLARE_BITMAP(jd_scratch, KEY_CNT);
8d3c60c76   Alexander Tsoy   Input: joydev - e...
775
  	bool ev_match = false;
15397f153   Thomas Hellstrom   Input: joydev - d...
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
  
  	BUILD_BUG_ON(ABS_CNT > KEY_CNT || EV_CNT > KEY_CNT);
  
  	/*
  	 * Virtualization (VMware, etc) and remote management (HP
  	 * ILO2) solutions use absolute coordinates for their virtual
  	 * pointing devices so that there is one-to-one relationship
  	 * between pointer position on the host screen and virtual
  	 * guest screen, and so their mice use ABS_X, ABS_Y and 3
  	 * primary button events. This clashes with what joydev
  	 * considers to be joysticks (a device with at minimum ABS_X
  	 * axis).
  	 *
  	 * Here we are trying to separate absolute mice from
  	 * joysticks. A device is, for joystick detection purposes,
  	 * considered to be an absolute mouse if the following is
  	 * true:
  	 *
8d3c60c76   Alexander Tsoy   Input: joydev - e...
794
795
796
797
798
799
  	 * 1) Event types are exactly
  	 *      EV_ABS, EV_KEY and EV_SYN
  	 *    or
  	 *      EV_ABS, EV_KEY, EV_SYN and EV_MSC
  	 *    or
  	 *      EV_ABS, EV_KEY, EV_SYN, EV_MSC and EV_REL.
15397f153   Thomas Hellstrom   Input: joydev - d...
800
801
802
803
804
805
  	 * 2) Absolute events are exactly ABS_X and ABS_Y.
  	 * 3) Keys are exactly BTN_LEFT, BTN_RIGHT and BTN_MIDDLE.
  	 * 4) Device is not on "Amiga" bus.
  	 */
  
  	bitmap_zero(jd_scratch, EV_CNT);
8d3c60c76   Alexander Tsoy   Input: joydev - e...
806
  	/* VMware VMMouse, HP ILO2 */
15397f153   Thomas Hellstrom   Input: joydev - d...
807
808
809
  	__set_bit(EV_ABS, jd_scratch);
  	__set_bit(EV_KEY, jd_scratch);
  	__set_bit(EV_SYN, jd_scratch);
8d3c60c76   Alexander Tsoy   Input: joydev - e...
810
811
812
813
814
815
816
817
818
819
820
821
822
823
  	if (bitmap_equal(jd_scratch, dev->evbit, EV_CNT))
  		ev_match = true;
  
  	/* HP ILO2, AMI BMC firmware */
  	__set_bit(EV_MSC, jd_scratch);
  	if (bitmap_equal(jd_scratch, dev->evbit, EV_CNT))
  		ev_match = true;
  
  	/* VMware Virtual USB Mouse, QEMU USB Tablet, ATEN BMC firmware */
  	__set_bit(EV_REL, jd_scratch);
  	if (bitmap_equal(jd_scratch, dev->evbit, EV_CNT))
  		ev_match = true;
  
  	if (!ev_match)
15397f153   Thomas Hellstrom   Input: joydev - d...
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
  		return false;
  
  	bitmap_zero(jd_scratch, ABS_CNT);
  	__set_bit(ABS_X, jd_scratch);
  	__set_bit(ABS_Y, jd_scratch);
  	if (!bitmap_equal(dev->absbit, jd_scratch, ABS_CNT))
  		return false;
  
  	bitmap_zero(jd_scratch, KEY_CNT);
  	__set_bit(BTN_LEFT, jd_scratch);
  	__set_bit(BTN_RIGHT, jd_scratch);
  	__set_bit(BTN_MIDDLE, jd_scratch);
  
  	if (!bitmap_equal(dev->keybit, jd_scratch, KEY_CNT))
  		return false;
  
  	/*
  	 * Amiga joystick (amijoy) historically uses left/middle/right
  	 * button events.
  	 */
  	if (dev->id.bustype == BUS_AMIGA)
  		return false;
  
  	return true;
  }
0b7024ac4   Dmitry Torokhov   Input: add match(...
849
850
851
  
  static bool joydev_match(struct input_handler *handler, struct input_dev *dev)
  {
20ac95d52   Roderick Colenbrander   Input: joydev - b...
852
853
  	/* Disable blacklisted devices */
  	if (joydev_dev_is_blacklisted(dev))
0b7024ac4   Dmitry Torokhov   Input: add match(...
854
  		return false;
15397f153   Thomas Hellstrom   Input: joydev - d...
855
856
857
  	/* Avoid absolute mice */
  	if (joydev_dev_is_absolute_mouse(dev))
  		return false;
0b7024ac4   Dmitry Torokhov   Input: add match(...
858
859
  	return true;
  }
5b2a08262   Dmitry Torokhov   Input: rework han...
860
861
  static int joydev_connect(struct input_handler *handler, struct input_dev *dev,
  			  const struct input_device_id *id)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
862
863
  {
  	struct joydev *joydev;
7f8d4cad1   Dmitry Torokhov   Input: extend the...
864
  	int i, j, t, minor, dev_no;
5b2a08262   Dmitry Torokhov   Input: rework han...
865
  	int error;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
866

7f8d4cad1   Dmitry Torokhov   Input: extend the...
867
868
869
870
871
872
  	minor = input_get_new_minor(JOYDEV_MINOR_BASE, JOYDEV_MINORS, true);
  	if (minor < 0) {
  		error = minor;
  		pr_err("failed to reserve new minor: %d
  ", error);
  		return error;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
873
  	}
5b2a08262   Dmitry Torokhov   Input: rework han...
874
  	joydev = kzalloc(sizeof(struct joydev), GFP_KERNEL);
7f8d4cad1   Dmitry Torokhov   Input: extend the...
875
876
877
878
  	if (!joydev) {
  		error = -ENOMEM;
  		goto err_free_minor;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
879

d0ffb9be8   Dmitry Torokhov   Input: handlers -...
880
  	INIT_LIST_HEAD(&joydev->client_list);
b126207cc   Dmitry Torokhov   Input: joydev - i...
881
882
  	spin_lock_init(&joydev->client_lock);
  	mutex_init(&joydev->mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
883
  	init_waitqueue_head(&joydev->wait);
20da92de8   Dmitry Torokhov   Input: change inp...
884
  	joydev->exist = true;
7f8d4cad1   Dmitry Torokhov   Input: extend the...
885
886
887
888
889
890
  
  	dev_no = minor;
  	/* Normalize device number if it falls into legacy range */
  	if (dev_no < JOYDEV_MINOR_BASE + JOYDEV_MINORS)
  		dev_no -= JOYDEV_MINOR_BASE;
  	dev_set_name(&joydev->dev, "js%d", dev_no);
b126207cc   Dmitry Torokhov   Input: joydev - i...
891

a7097ff89   Dmitry Torokhov   Input: make sure ...
892
  	joydev->handle.dev = input_get_device(dev);
3d5cb60ef   Thadeu Lima de Souza Cascardo   Input: simplify n...
893
  	joydev->handle.name = dev_name(&joydev->dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
894
895
  	joydev->handle.handler = handler;
  	joydev->handle.private = joydev;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
896

f23487cdb   Anshul Garg   Input: joydev - u...
897
898
899
900
901
  	for_each_set_bit(i, dev->absbit, ABS_CNT) {
  		joydev->absmap[i] = joydev->nabs;
  		joydev->abspam[joydev->nabs] = i;
  		joydev->nabs++;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
902
903
904
905
906
907
908
  
  	for (i = BTN_JOYSTICK - BTN_MISC; i < KEY_MAX - BTN_MISC + 1; i++)
  		if (test_bit(i + BTN_MISC, dev->keybit)) {
  			joydev->keymap[i] = joydev->nkey;
  			joydev->keypam[joydev->nkey] = i + BTN_MISC;
  			joydev->nkey++;
  		}
f24949e8e   Vojtech Pavlik   Input: Fix button...
909
  	for (i = 0; i < BTN_JOYSTICK - BTN_MISC; i++)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
910
911
912
913
914
915
916
917
  		if (test_bit(i + BTN_MISC, dev->keybit)) {
  			joydev->keymap[i] = joydev->nkey;
  			joydev->keypam[joydev->nkey] = i + BTN_MISC;
  			joydev->nkey++;
  		}
  
  	for (i = 0; i < joydev->nabs; i++) {
  		j = joydev->abspam[i];
987a6c029   Daniel Mack   Input: switch to ...
918
  		if (input_abs_get_max(dev, j) == input_abs_get_min(dev, j)) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
919
  			joydev->corr[i].type = JS_CORR_NONE;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
920
921
922
  			continue;
  		}
  		joydev->corr[i].type = JS_CORR_BROKEN;
987a6c029   Daniel Mack   Input: switch to ...
923
924
925
926
927
  		joydev->corr[i].prec = input_abs_get_fuzz(dev, j);
  
  		t = (input_abs_get_max(dev, j) + input_abs_get_min(dev, j)) / 2;
  		joydev->corr[i].coef[0] = t - input_abs_get_flat(dev, j);
  		joydev->corr[i].coef[1] = t + input_abs_get_flat(dev, j);
b126207cc   Dmitry Torokhov   Input: joydev - i...
928

987a6c029   Daniel Mack   Input: switch to ...
929
930
  		t = (input_abs_get_max(dev, j) - input_abs_get_min(dev, j)) / 2
  			- 2 * input_abs_get_flat(dev, j);
b126207cc   Dmitry Torokhov   Input: joydev - i...
931
932
933
  		if (t) {
  			joydev->corr[i].coef[2] = (1 << 29) / t;
  			joydev->corr[i].coef[3] = (1 << 29) / t;
b126207cc   Dmitry Torokhov   Input: joydev - i...
934
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
935
  	}
7f8d4cad1   Dmitry Torokhov   Input: extend the...
936
  	joydev->dev.devt = MKDEV(INPUT_MAJOR, minor);
9657d75c5   Dmitry Torokhov   Input: convert fr...
937
938
  	joydev->dev.class = &input_class;
  	joydev->dev.parent = &dev->dev;
9657d75c5   Dmitry Torokhov   Input: convert fr...
939
940
  	joydev->dev.release = joydev_free;
  	device_initialize(&joydev->dev);
5b2a08262   Dmitry Torokhov   Input: rework han...
941

b126207cc   Dmitry Torokhov   Input: joydev - i...
942
  	error = input_register_handle(&joydev->handle);
5b2a08262   Dmitry Torokhov   Input: rework han...
943
  	if (error)
9657d75c5   Dmitry Torokhov   Input: convert fr...
944
  		goto err_free_joydev;
5b2a08262   Dmitry Torokhov   Input: rework han...
945

7f8d4cad1   Dmitry Torokhov   Input: extend the...
946
  	cdev_init(&joydev->cdev, &joydev_fops);
b126207cc   Dmitry Torokhov   Input: joydev - i...
947

358a89ca2   Logan Gunthorpe   input: utilize ne...
948
  	error = cdev_device_add(&joydev->cdev, &joydev->dev);
b126207cc   Dmitry Torokhov   Input: joydev - i...
949
950
  	if (error)
  		goto err_cleanup_joydev;
5b2a08262   Dmitry Torokhov   Input: rework han...
951
952
  
  	return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
953

b126207cc   Dmitry Torokhov   Input: joydev - i...
954
955
   err_cleanup_joydev:
  	joydev_cleanup(joydev);
b126207cc   Dmitry Torokhov   Input: joydev - i...
956
  	input_unregister_handle(&joydev->handle);
5b2a08262   Dmitry Torokhov   Input: rework han...
957
   err_free_joydev:
9657d75c5   Dmitry Torokhov   Input: convert fr...
958
  	put_device(&joydev->dev);
7f8d4cad1   Dmitry Torokhov   Input: extend the...
959
960
   err_free_minor:
  	input_free_minor(minor);
5b2a08262   Dmitry Torokhov   Input: rework han...
961
  	return error;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
962
963
964
965
966
  }
  
  static void joydev_disconnect(struct input_handle *handle)
  {
  	struct joydev *joydev = handle->private;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
967

358a89ca2   Logan Gunthorpe   input: utilize ne...
968
  	cdev_device_del(&joydev->cdev, &joydev->dev);
b126207cc   Dmitry Torokhov   Input: joydev - i...
969
  	joydev_cleanup(joydev);
7f8d4cad1   Dmitry Torokhov   Input: extend the...
970
  	input_free_minor(MINOR(joydev->dev.devt));
b126207cc   Dmitry Torokhov   Input: joydev - i...
971
  	input_unregister_handle(handle);
9657d75c5   Dmitry Torokhov   Input: convert fr...
972
  	put_device(&joydev->dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
973
  }
66e661188   Dmitry Torokhov   Input: constify i...
974
  static const struct input_device_id joydev_ids[] = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
975
  	{
b126207cc   Dmitry Torokhov   Input: joydev - i...
976
977
  		.flags = INPUT_DEVICE_ID_MATCH_EVBIT |
  				INPUT_DEVICE_ID_MATCH_ABSBIT,
7b19ada2e   Jiri Slaby   get rid of input ...
978
979
  		.evbit = { BIT_MASK(EV_ABS) },
  		.absbit = { BIT_MASK(ABS_X) },
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
980
981
  	},
  	{
b126207cc   Dmitry Torokhov   Input: joydev - i...
982
983
  		.flags = INPUT_DEVICE_ID_MATCH_EVBIT |
  				INPUT_DEVICE_ID_MATCH_ABSBIT,
7b19ada2e   Jiri Slaby   get rid of input ...
984
  		.evbit = { BIT_MASK(EV_ABS) },
9fb6de1b0   Ville Ranki   Input: joydev - r...
985
986
987
988
989
990
  		.absbit = { BIT_MASK(ABS_Z) },
  	},
  	{
  		.flags = INPUT_DEVICE_ID_MATCH_EVBIT |
  				INPUT_DEVICE_ID_MATCH_ABSBIT,
  		.evbit = { BIT_MASK(EV_ABS) },
7b19ada2e   Jiri Slaby   get rid of input ...
991
  		.absbit = { BIT_MASK(ABS_WHEEL) },
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
992
993
  	},
  	{
b126207cc   Dmitry Torokhov   Input: joydev - i...
994
995
  		.flags = INPUT_DEVICE_ID_MATCH_EVBIT |
  				INPUT_DEVICE_ID_MATCH_ABSBIT,
7b19ada2e   Jiri Slaby   get rid of input ...
996
997
  		.evbit = { BIT_MASK(EV_ABS) },
  		.absbit = { BIT_MASK(ABS_THROTTLE) },
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
998
  	},
26a6931ba   Christoph Fritz   Input: joydev - a...
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
  	{
  		.flags = INPUT_DEVICE_ID_MATCH_EVBIT |
  				INPUT_DEVICE_ID_MATCH_KEYBIT,
  		.evbit = { BIT_MASK(EV_KEY) },
  		.keybit = {[BIT_WORD(BTN_JOYSTICK)] = BIT_MASK(BTN_JOYSTICK) },
  	},
  	{
  		.flags = INPUT_DEVICE_ID_MATCH_EVBIT |
  				INPUT_DEVICE_ID_MATCH_KEYBIT,
  		.evbit = { BIT_MASK(EV_KEY) },
  		.keybit = { [BIT_WORD(BTN_GAMEPAD)] = BIT_MASK(BTN_GAMEPAD) },
  	},
  	{
  		.flags = INPUT_DEVICE_ID_MATCH_EVBIT |
  				INPUT_DEVICE_ID_MATCH_KEYBIT,
  		.evbit = { BIT_MASK(EV_KEY) },
  		.keybit = { [BIT_WORD(BTN_TRIGGER_HAPPY)] = BIT_MASK(BTN_TRIGGER_HAPPY) },
  	},
1e0afb288   Dmitry Torokhov   Input: fix format...
1017
  	{ }	/* Terminating entry */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1018
1019
1020
1021
1022
  };
  
  MODULE_DEVICE_TABLE(input, joydev_ids);
  
  static struct input_handler joydev_handler = {
b126207cc   Dmitry Torokhov   Input: joydev - i...
1023
  	.event		= joydev_event,
0b7024ac4   Dmitry Torokhov   Input: add match(...
1024
  	.match		= joydev_match,
b126207cc   Dmitry Torokhov   Input: joydev - i...
1025
1026
  	.connect	= joydev_connect,
  	.disconnect	= joydev_disconnect,
7f8d4cad1   Dmitry Torokhov   Input: extend the...
1027
  	.legacy_minors	= true,
b126207cc   Dmitry Torokhov   Input: joydev - i...
1028
1029
1030
  	.minor		= JOYDEV_MINOR_BASE,
  	.name		= "joydev",
  	.id_table	= joydev_ids,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1031
1032
1033
1034
  };
  
  static int __init joydev_init(void)
  {
4263cf0fa   Dmitry Torokhov   Input: make input...
1035
  	return input_register_handler(&joydev_handler);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1036
1037
1038
1039
1040
1041
1042
1043
1044
  }
  
  static void __exit joydev_exit(void)
  {
  	input_unregister_handler(&joydev_handler);
  }
  
  module_init(joydev_init);
  module_exit(joydev_exit);