diff options
Diffstat (limited to 'drivers/input/joydev.c')
-rw-r--r-- | drivers/input/joydev.c | 533 |
1 files changed, 533 insertions, 0 deletions
diff --git a/drivers/input/joydev.c b/drivers/input/joydev.c new file mode 100644 index 000000000000..7d7527f8b02d --- /dev/null +++ b/drivers/input/joydev.c | |||
@@ -0,0 +1,533 @@ | |||
1 | /* | ||
2 | * Joystick device driver for the input driver suite. | ||
3 | * | ||
4 | * Copyright (c) 1999-2002 Vojtech Pavlik | ||
5 | * Copyright (c) 1999 Colin Van Dyke | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License as published by | ||
9 | * the Free Software Foundation; either version 2 of the License, or | ||
10 | * (at your option) any later version. | ||
11 | */ | ||
12 | |||
13 | #include <asm/io.h> | ||
14 | #include <asm/system.h> | ||
15 | #include <linux/delay.h> | ||
16 | #include <linux/errno.h> | ||
17 | #include <linux/joystick.h> | ||
18 | #include <linux/input.h> | ||
19 | #include <linux/kernel.h> | ||
20 | #include <linux/major.h> | ||
21 | #include <linux/slab.h> | ||
22 | #include <linux/mm.h> | ||
23 | #include <linux/miscdevice.h> | ||
24 | #include <linux/module.h> | ||
25 | #include <linux/poll.h> | ||
26 | #include <linux/init.h> | ||
27 | #include <linux/smp_lock.h> | ||
28 | #include <linux/device.h> | ||
29 | #include <linux/devfs_fs_kernel.h> | ||
30 | |||
31 | MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>"); | ||
32 | MODULE_DESCRIPTION("Joystick device interfaces"); | ||
33 | MODULE_SUPPORTED_DEVICE("input/js"); | ||
34 | MODULE_LICENSE("GPL"); | ||
35 | |||
36 | #define JOYDEV_MINOR_BASE 0 | ||
37 | #define JOYDEV_MINORS 16 | ||
38 | #define JOYDEV_BUFFER_SIZE 64 | ||
39 | |||
40 | #define MSECS(t) (1000 * ((t) / HZ) + 1000 * ((t) % HZ) / HZ) | ||
41 | |||
42 | struct joydev { | ||
43 | int exist; | ||
44 | int open; | ||
45 | int minor; | ||
46 | char name[16]; | ||
47 | struct input_handle handle; | ||
48 | wait_queue_head_t wait; | ||
49 | struct list_head list; | ||
50 | struct js_corr corr[ABS_MAX + 1]; | ||
51 | struct JS_DATA_SAVE_TYPE glue; | ||
52 | int nabs; | ||
53 | int nkey; | ||
54 | __u16 keymap[KEY_MAX - BTN_MISC + 1]; | ||
55 | __u16 keypam[KEY_MAX - BTN_MISC + 1]; | ||
56 | __u8 absmap[ABS_MAX + 1]; | ||
57 | __u8 abspam[ABS_MAX + 1]; | ||
58 | __s16 abs[ABS_MAX + 1]; | ||
59 | }; | ||
60 | |||
61 | struct joydev_list { | ||
62 | struct js_event buffer[JOYDEV_BUFFER_SIZE]; | ||
63 | int head; | ||
64 | int tail; | ||
65 | int startup; | ||
66 | struct fasync_struct *fasync; | ||
67 | struct joydev *joydev; | ||
68 | struct list_head node; | ||
69 | }; | ||
70 | |||
71 | static struct joydev *joydev_table[JOYDEV_MINORS]; | ||
72 | |||
73 | static int joydev_correct(int value, struct js_corr *corr) | ||
74 | { | ||
75 | switch (corr->type) { | ||
76 | case JS_CORR_NONE: | ||
77 | break; | ||
78 | case JS_CORR_BROKEN: | ||
79 | value = value > corr->coef[0] ? (value < corr->coef[1] ? 0 : | ||
80 | ((corr->coef[3] * (value - corr->coef[1])) >> 14)) : | ||
81 | ((corr->coef[2] * (value - corr->coef[0])) >> 14); | ||
82 | break; | ||
83 | default: | ||
84 | return 0; | ||
85 | } | ||
86 | |||
87 | if (value < -32767) return -32767; | ||
88 | if (value > 32767) return 32767; | ||
89 | |||
90 | return value; | ||
91 | } | ||
92 | |||
93 | static void joydev_event(struct input_handle *handle, unsigned int type, unsigned int code, int value) | ||
94 | { | ||
95 | struct joydev *joydev = handle->private; | ||
96 | struct joydev_list *list; | ||
97 | struct js_event event; | ||
98 | |||
99 | switch (type) { | ||
100 | |||
101 | case EV_KEY: | ||
102 | if (code < BTN_MISC || value == 2) return; | ||
103 | event.type = JS_EVENT_BUTTON; | ||
104 | event.number = joydev->keymap[code - BTN_MISC]; | ||
105 | event.value = value; | ||
106 | break; | ||
107 | |||
108 | case EV_ABS: | ||
109 | event.type = JS_EVENT_AXIS; | ||
110 | event.number = joydev->absmap[code]; | ||
111 | event.value = joydev_correct(value, joydev->corr + event.number); | ||
112 | if (event.value == joydev->abs[event.number]) return; | ||
113 | joydev->abs[event.number] = event.value; | ||
114 | break; | ||
115 | |||
116 | default: | ||
117 | return; | ||
118 | } | ||
119 | |||
120 | event.time = MSECS(jiffies); | ||
121 | |||
122 | list_for_each_entry(list, &joydev->list, node) { | ||
123 | |||
124 | memcpy(list->buffer + list->head, &event, sizeof(struct js_event)); | ||
125 | |||
126 | if (list->startup == joydev->nabs + joydev->nkey) | ||
127 | if (list->tail == (list->head = (list->head + 1) & (JOYDEV_BUFFER_SIZE - 1))) | ||
128 | list->startup = 0; | ||
129 | |||
130 | kill_fasync(&list->fasync, SIGIO, POLL_IN); | ||
131 | } | ||
132 | |||
133 | wake_up_interruptible(&joydev->wait); | ||
134 | } | ||
135 | |||
136 | static int joydev_fasync(int fd, struct file *file, int on) | ||
137 | { | ||
138 | int retval; | ||
139 | struct joydev_list *list = file->private_data; | ||
140 | retval = fasync_helper(fd, file, on, &list->fasync); | ||
141 | return retval < 0 ? retval : 0; | ||
142 | } | ||
143 | |||
144 | static void joydev_free(struct joydev *joydev) | ||
145 | { | ||
146 | joydev_table[joydev->minor] = NULL; | ||
147 | kfree(joydev); | ||
148 | } | ||
149 | |||
150 | static int joydev_release(struct inode * inode, struct file * file) | ||
151 | { | ||
152 | struct joydev_list *list = file->private_data; | ||
153 | |||
154 | joydev_fasync(-1, file, 0); | ||
155 | |||
156 | list_del(&list->node); | ||
157 | |||
158 | if (!--list->joydev->open) { | ||
159 | if (list->joydev->exist) | ||
160 | input_close_device(&list->joydev->handle); | ||
161 | else | ||
162 | joydev_free(list->joydev); | ||
163 | } | ||
164 | |||
165 | kfree(list); | ||
166 | return 0; | ||
167 | } | ||
168 | |||
169 | static int joydev_open(struct inode *inode, struct file *file) | ||
170 | { | ||
171 | struct joydev_list *list; | ||
172 | int i = iminor(inode) - JOYDEV_MINOR_BASE; | ||
173 | |||
174 | if (i >= JOYDEV_MINORS || !joydev_table[i]) | ||
175 | return -ENODEV; | ||
176 | |||
177 | if (!(list = kmalloc(sizeof(struct joydev_list), GFP_KERNEL))) | ||
178 | return -ENOMEM; | ||
179 | memset(list, 0, sizeof(struct joydev_list)); | ||
180 | |||
181 | list->joydev = joydev_table[i]; | ||
182 | list_add_tail(&list->node, &joydev_table[i]->list); | ||
183 | file->private_data = list; | ||
184 | |||
185 | if (!list->joydev->open++) | ||
186 | if (list->joydev->exist) | ||
187 | input_open_device(&list->joydev->handle); | ||
188 | |||
189 | return 0; | ||
190 | } | ||
191 | |||
192 | static ssize_t joydev_write(struct file * file, const char __user * buffer, size_t count, loff_t *ppos) | ||
193 | { | ||
194 | return -EINVAL; | ||
195 | } | ||
196 | |||
197 | static ssize_t joydev_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) | ||
198 | { | ||
199 | struct joydev_list *list = file->private_data; | ||
200 | struct joydev *joydev = list->joydev; | ||
201 | struct input_dev *input = joydev->handle.dev; | ||
202 | int retval = 0; | ||
203 | |||
204 | if (!list->joydev->exist) | ||
205 | return -ENODEV; | ||
206 | |||
207 | if (count < sizeof(struct js_event)) | ||
208 | return -EINVAL; | ||
209 | |||
210 | if (count == sizeof(struct JS_DATA_TYPE)) { | ||
211 | |||
212 | struct JS_DATA_TYPE data; | ||
213 | int i; | ||
214 | |||
215 | for (data.buttons = i = 0; i < 32 && i < joydev->nkey; i++) | ||
216 | data.buttons |= test_bit(joydev->keypam[i], input->key) ? (1 << i) : 0; | ||
217 | data.x = (joydev->abs[0] / 256 + 128) >> joydev->glue.JS_CORR.x; | ||
218 | data.y = (joydev->abs[1] / 256 + 128) >> joydev->glue.JS_CORR.y; | ||
219 | |||
220 | if (copy_to_user(buf, &data, sizeof(struct JS_DATA_TYPE))) | ||
221 | return -EFAULT; | ||
222 | |||
223 | list->startup = 0; | ||
224 | list->tail = list->head; | ||
225 | |||
226 | return sizeof(struct JS_DATA_TYPE); | ||
227 | } | ||
228 | |||
229 | if (list->startup == joydev->nabs + joydev->nkey | ||
230 | && list->head == list->tail && (file->f_flags & O_NONBLOCK)) | ||
231 | return -EAGAIN; | ||
232 | |||
233 | retval = wait_event_interruptible(list->joydev->wait, | ||
234 | !list->joydev->exist || | ||
235 | list->startup < joydev->nabs + joydev->nkey || | ||
236 | list->head != list->tail); | ||
237 | |||
238 | if (retval) | ||
239 | return retval; | ||
240 | |||
241 | if (!list->joydev->exist) | ||
242 | return -ENODEV; | ||
243 | |||
244 | while (list->startup < joydev->nabs + joydev->nkey && retval + sizeof(struct js_event) <= count) { | ||
245 | |||
246 | struct js_event event; | ||
247 | |||
248 | event.time = MSECS(jiffies); | ||
249 | |||
250 | if (list->startup < joydev->nkey) { | ||
251 | event.type = JS_EVENT_BUTTON | JS_EVENT_INIT; | ||
252 | event.number = list->startup; | ||
253 | event.value = !!test_bit(joydev->keypam[event.number], input->key); | ||
254 | } else { | ||
255 | event.type = JS_EVENT_AXIS | JS_EVENT_INIT; | ||
256 | event.number = list->startup - joydev->nkey; | ||
257 | event.value = joydev->abs[event.number]; | ||
258 | } | ||
259 | |||
260 | if (copy_to_user(buf + retval, &event, sizeof(struct js_event))) | ||
261 | return -EFAULT; | ||
262 | |||
263 | list->startup++; | ||
264 | retval += sizeof(struct js_event); | ||
265 | } | ||
266 | |||
267 | while (list->head != list->tail && retval + sizeof(struct js_event) <= count) { | ||
268 | |||
269 | if (copy_to_user(buf + retval, list->buffer + list->tail, sizeof(struct js_event))) | ||
270 | return -EFAULT; | ||
271 | |||
272 | list->tail = (list->tail + 1) & (JOYDEV_BUFFER_SIZE - 1); | ||
273 | retval += sizeof(struct js_event); | ||
274 | } | ||
275 | |||
276 | return retval; | ||
277 | } | ||
278 | |||
279 | /* No kernel lock - fine */ | ||
280 | static unsigned int joydev_poll(struct file *file, poll_table *wait) | ||
281 | { | ||
282 | struct joydev_list *list = file->private_data; | ||
283 | poll_wait(file, &list->joydev->wait, wait); | ||
284 | return ((list->head != list->tail || list->startup < list->joydev->nabs + list->joydev->nkey) ? | ||
285 | (POLLIN | POLLRDNORM) : 0) | (list->joydev->exist ? 0 : (POLLHUP | POLLERR)); | ||
286 | } | ||
287 | |||
288 | static int joydev_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) | ||
289 | { | ||
290 | struct joydev_list *list = file->private_data; | ||
291 | struct joydev *joydev = list->joydev; | ||
292 | struct input_dev *dev = joydev->handle.dev; | ||
293 | void __user *argp = (void __user *)arg; | ||
294 | int i, j; | ||
295 | |||
296 | if (!joydev->exist) return -ENODEV; | ||
297 | |||
298 | switch (cmd) { | ||
299 | |||
300 | case JS_SET_CAL: | ||
301 | return copy_from_user(&joydev->glue.JS_CORR, argp, | ||
302 | sizeof(struct JS_DATA_TYPE)) ? -EFAULT : 0; | ||
303 | case JS_GET_CAL: | ||
304 | return copy_to_user(argp, &joydev->glue.JS_CORR, | ||
305 | sizeof(struct JS_DATA_TYPE)) ? -EFAULT : 0; | ||
306 | case JS_SET_TIMEOUT: | ||
307 | return get_user(joydev->glue.JS_TIMEOUT, (int __user *) arg); | ||
308 | case JS_GET_TIMEOUT: | ||
309 | return put_user(joydev->glue.JS_TIMEOUT, (int __user *) arg); | ||
310 | case JS_SET_TIMELIMIT: | ||
311 | return get_user(joydev->glue.JS_TIMELIMIT, (long __user *) arg); | ||
312 | case JS_GET_TIMELIMIT: | ||
313 | return put_user(joydev->glue.JS_TIMELIMIT, (long __user *) arg); | ||
314 | case JS_SET_ALL: | ||
315 | return copy_from_user(&joydev->glue, argp, | ||
316 | sizeof(struct JS_DATA_SAVE_TYPE)) ? -EFAULT : 0; | ||
317 | case JS_GET_ALL: | ||
318 | return copy_to_user(argp, &joydev->glue, | ||
319 | sizeof(struct JS_DATA_SAVE_TYPE)) ? -EFAULT : 0; | ||
320 | |||
321 | case JSIOCGVERSION: | ||
322 | return put_user(JS_VERSION, (__u32 __user *) arg); | ||
323 | case JSIOCGAXES: | ||
324 | return put_user(joydev->nabs, (__u8 __user *) arg); | ||
325 | case JSIOCGBUTTONS: | ||
326 | return put_user(joydev->nkey, (__u8 __user *) arg); | ||
327 | case JSIOCSCORR: | ||
328 | if (copy_from_user(joydev->corr, argp, | ||
329 | sizeof(struct js_corr) * joydev->nabs)) | ||
330 | return -EFAULT; | ||
331 | for (i = 0; i < joydev->nabs; i++) { | ||
332 | j = joydev->abspam[i]; | ||
333 | joydev->abs[i] = joydev_correct(dev->abs[j], joydev->corr + i); | ||
334 | } | ||
335 | return 0; | ||
336 | case JSIOCGCORR: | ||
337 | return copy_to_user(argp, joydev->corr, | ||
338 | sizeof(struct js_corr) * joydev->nabs) ? -EFAULT : 0; | ||
339 | case JSIOCSAXMAP: | ||
340 | if (copy_from_user(joydev->abspam, argp, sizeof(__u8) * (ABS_MAX + 1))) | ||
341 | return -EFAULT; | ||
342 | for (i = 0; i < joydev->nabs; i++) { | ||
343 | if (joydev->abspam[i] > ABS_MAX) return -EINVAL; | ||
344 | joydev->absmap[joydev->abspam[i]] = i; | ||
345 | } | ||
346 | return 0; | ||
347 | case JSIOCGAXMAP: | ||
348 | return copy_to_user(argp, joydev->abspam, | ||
349 | sizeof(__u8) * (ABS_MAX + 1)) ? -EFAULT : 0; | ||
350 | case JSIOCSBTNMAP: | ||
351 | if (copy_from_user(joydev->keypam, argp, sizeof(__u16) * (KEY_MAX - BTN_MISC + 1))) | ||
352 | return -EFAULT; | ||
353 | for (i = 0; i < joydev->nkey; i++) { | ||
354 | if (joydev->keypam[i] > KEY_MAX || joydev->keypam[i] < BTN_MISC) return -EINVAL; | ||
355 | joydev->keymap[joydev->keypam[i] - BTN_MISC] = i; | ||
356 | } | ||
357 | return 0; | ||
358 | case JSIOCGBTNMAP: | ||
359 | return copy_to_user(argp, joydev->keypam, | ||
360 | sizeof(__u16) * (KEY_MAX - BTN_MISC + 1)) ? -EFAULT : 0; | ||
361 | default: | ||
362 | if ((cmd & ~(_IOC_SIZEMASK << _IOC_SIZESHIFT)) == JSIOCGNAME(0)) { | ||
363 | int len; | ||
364 | if (!dev->name) return 0; | ||
365 | len = strlen(dev->name) + 1; | ||
366 | if (len > _IOC_SIZE(cmd)) len = _IOC_SIZE(cmd); | ||
367 | if (copy_to_user(argp, dev->name, len)) return -EFAULT; | ||
368 | return len; | ||
369 | } | ||
370 | } | ||
371 | return -EINVAL; | ||
372 | } | ||
373 | |||
374 | static struct file_operations joydev_fops = { | ||
375 | .owner = THIS_MODULE, | ||
376 | .read = joydev_read, | ||
377 | .write = joydev_write, | ||
378 | .poll = joydev_poll, | ||
379 | .open = joydev_open, | ||
380 | .release = joydev_release, | ||
381 | .ioctl = joydev_ioctl, | ||
382 | .fasync = joydev_fasync, | ||
383 | }; | ||
384 | |||
385 | static struct input_handle *joydev_connect(struct input_handler *handler, struct input_dev *dev, struct input_device_id *id) | ||
386 | { | ||
387 | struct joydev *joydev; | ||
388 | int i, j, t, minor; | ||
389 | |||
390 | for (minor = 0; minor < JOYDEV_MINORS && joydev_table[minor]; minor++); | ||
391 | if (minor == JOYDEV_MINORS) { | ||
392 | printk(KERN_ERR "joydev: no more free joydev devices\n"); | ||
393 | return NULL; | ||
394 | } | ||
395 | |||
396 | if (!(joydev = kmalloc(sizeof(struct joydev), GFP_KERNEL))) | ||
397 | return NULL; | ||
398 | memset(joydev, 0, sizeof(struct joydev)); | ||
399 | |||
400 | INIT_LIST_HEAD(&joydev->list); | ||
401 | init_waitqueue_head(&joydev->wait); | ||
402 | |||
403 | joydev->minor = minor; | ||
404 | joydev->exist = 1; | ||
405 | joydev->handle.dev = dev; | ||
406 | joydev->handle.name = joydev->name; | ||
407 | joydev->handle.handler = handler; | ||
408 | joydev->handle.private = joydev; | ||
409 | sprintf(joydev->name, "js%d", minor); | ||
410 | |||
411 | for (i = 0; i < ABS_MAX + 1; i++) | ||
412 | if (test_bit(i, dev->absbit)) { | ||
413 | joydev->absmap[i] = joydev->nabs; | ||
414 | joydev->abspam[joydev->nabs] = i; | ||
415 | joydev->nabs++; | ||
416 | } | ||
417 | |||
418 | for (i = BTN_JOYSTICK - BTN_MISC; i < KEY_MAX - BTN_MISC + 1; i++) | ||
419 | if (test_bit(i + BTN_MISC, dev->keybit)) { | ||
420 | joydev->keymap[i] = joydev->nkey; | ||
421 | joydev->keypam[joydev->nkey] = i + BTN_MISC; | ||
422 | joydev->nkey++; | ||
423 | } | ||
424 | |||
425 | for (i = 0; i < BTN_JOYSTICK - BTN_MISC + 1; i++) | ||
426 | if (test_bit(i + BTN_MISC, dev->keybit)) { | ||
427 | joydev->keymap[i] = joydev->nkey; | ||
428 | joydev->keypam[joydev->nkey] = i + BTN_MISC; | ||
429 | joydev->nkey++; | ||
430 | } | ||
431 | |||
432 | for (i = 0; i < joydev->nabs; i++) { | ||
433 | j = joydev->abspam[i]; | ||
434 | if (dev->absmax[j] == dev->absmin[j]) { | ||
435 | joydev->corr[i].type = JS_CORR_NONE; | ||
436 | joydev->abs[i] = dev->abs[j]; | ||
437 | continue; | ||
438 | } | ||
439 | joydev->corr[i].type = JS_CORR_BROKEN; | ||
440 | joydev->corr[i].prec = dev->absfuzz[j]; | ||
441 | joydev->corr[i].coef[0] = (dev->absmax[j] + dev->absmin[j]) / 2 - dev->absflat[j]; | ||
442 | joydev->corr[i].coef[1] = (dev->absmax[j] + dev->absmin[j]) / 2 + dev->absflat[j]; | ||
443 | if (!(t = ((dev->absmax[j] - dev->absmin[j]) / 2 - 2 * dev->absflat[j]))) | ||
444 | continue; | ||
445 | joydev->corr[i].coef[2] = (1 << 29) / t; | ||
446 | joydev->corr[i].coef[3] = (1 << 29) / t; | ||
447 | |||
448 | joydev->abs[i] = joydev_correct(dev->abs[j], joydev->corr + i); | ||
449 | } | ||
450 | |||
451 | joydev_table[minor] = joydev; | ||
452 | |||
453 | devfs_mk_cdev(MKDEV(INPUT_MAJOR, JOYDEV_MINOR_BASE + minor), | ||
454 | S_IFCHR|S_IRUGO|S_IWUSR, "input/js%d", minor); | ||
455 | class_simple_device_add(input_class, | ||
456 | MKDEV(INPUT_MAJOR, JOYDEV_MINOR_BASE + minor), | ||
457 | dev->dev, "js%d", minor); | ||
458 | |||
459 | return &joydev->handle; | ||
460 | } | ||
461 | |||
462 | static void joydev_disconnect(struct input_handle *handle) | ||
463 | { | ||
464 | struct joydev *joydev = handle->private; | ||
465 | struct joydev_list *list; | ||
466 | |||
467 | class_simple_device_remove(MKDEV(INPUT_MAJOR, JOYDEV_MINOR_BASE + joydev->minor)); | ||
468 | devfs_remove("input/js%d", joydev->minor); | ||
469 | joydev->exist = 0; | ||
470 | |||
471 | if (joydev->open) { | ||
472 | input_close_device(handle); | ||
473 | wake_up_interruptible(&joydev->wait); | ||
474 | list_for_each_entry(list, &joydev->list, node) | ||
475 | kill_fasync(&list->fasync, SIGIO, POLL_HUP); | ||
476 | } else | ||
477 | joydev_free(joydev); | ||
478 | } | ||
479 | |||
480 | static struct input_device_id joydev_blacklist[] = { | ||
481 | { | ||
482 | .flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_KEYBIT, | ||
483 | .evbit = { BIT(EV_KEY) }, | ||
484 | .keybit = { [LONG(BTN_TOUCH)] = BIT(BTN_TOUCH) }, | ||
485 | }, /* Avoid itouchpads, touchscreens and tablets */ | ||
486 | { }, /* Terminating entry */ | ||
487 | }; | ||
488 | |||
489 | static struct input_device_id joydev_ids[] = { | ||
490 | { | ||
491 | .flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_ABSBIT, | ||
492 | .evbit = { BIT(EV_ABS) }, | ||
493 | .absbit = { BIT(ABS_X) }, | ||
494 | }, | ||
495 | { | ||
496 | .flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_ABSBIT, | ||
497 | .evbit = { BIT(EV_ABS) }, | ||
498 | .absbit = { BIT(ABS_WHEEL) }, | ||
499 | }, | ||
500 | { | ||
501 | .flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_ABSBIT, | ||
502 | .evbit = { BIT(EV_ABS) }, | ||
503 | .absbit = { BIT(ABS_THROTTLE) }, | ||
504 | }, | ||
505 | { }, /* Terminating entry */ | ||
506 | }; | ||
507 | |||
508 | MODULE_DEVICE_TABLE(input, joydev_ids); | ||
509 | |||
510 | static struct input_handler joydev_handler = { | ||
511 | .event = joydev_event, | ||
512 | .connect = joydev_connect, | ||
513 | .disconnect = joydev_disconnect, | ||
514 | .fops = &joydev_fops, | ||
515 | .minor = JOYDEV_MINOR_BASE, | ||
516 | .name = "joydev", | ||
517 | .id_table = joydev_ids, | ||
518 | .blacklist = joydev_blacklist, | ||
519 | }; | ||
520 | |||
521 | static int __init joydev_init(void) | ||
522 | { | ||
523 | input_register_handler(&joydev_handler); | ||
524 | return 0; | ||
525 | } | ||
526 | |||
527 | static void __exit joydev_exit(void) | ||
528 | { | ||
529 | input_unregister_handler(&joydev_handler); | ||
530 | } | ||
531 | |||
532 | module_init(joydev_init); | ||
533 | module_exit(joydev_exit); | ||