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