2 * $Id: tsdev.c,v 1.15 2002/04/10 16:50:19 jsimmons Exp $
4 * Copyright (c) 2001 "Crazy" james Simmons
6 * Compaq touchscreen protocol driver. The protocol emulated by this driver
7 * is obsolete; for new programs use the tslib library which can read directly
8 * from evdev and perform dejittering, variance filtering and calibration -
9 * all in user space, not at kernel level. The meaning of this driver is
10 * to allow usage of newer input drivers with old applications that use the
11 * old /dev/h3600_ts and /dev/h3600_tsraw devices.
13 * 09-Apr-2004: Andrew Zabolotny <zap@homelink.ru>
14 * Fixed to actually work, not just output random numbers.
15 * Added support for both h3600_ts and h3600_tsraw protocol
20 * This program is free software; you can redistribute it and/or modify
21 * it under the terms of the GNU General Public License as published by
22 * the Free Software Foundation; either version 2 of the License, or
23 * (at your option) any later version.
25 * This program is distributed in the hope that it will be useful,
26 * but WITHOUT ANY WARRANTY; without even the implied warranty of
27 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
28 * GNU General Public License for more details.
30 * You should have received a copy of the GNU General Public License
31 * along with this program; if not, write to the Free Software
32 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
34 * Should you need to contact me, the author, you can do so either by
35 * e-mail - mail your message to <jsimmons@infradead.org>.
38 #define TSDEV_MINOR_BASE 128
39 #define TSDEV_MINORS 32
40 /* First 16 devices are h3600_ts compatible; second 16 are h3600_tsraw */
41 #define TSDEV_MINOR_MASK 15
42 #define TSDEV_BUFFER_SIZE 64
44 #include <linux/slab.h>
45 #include <linux/poll.h>
46 #include <linux/module.h>
47 #include <linux/moduleparam.h>
48 #include <linux/init.h>
49 #include <linux/input.h>
50 #include <linux/major.h>
51 #include <linux/config.h>
52 #include <linux/smp_lock.h>
53 #include <linux/random.h>
54 #include <linux/time.h>
55 #include <linux/device.h>
56 #include <linux/devfs_fs_kernel.h>
58 #ifndef CONFIG_INPUT_TSDEV_SCREEN_X
59 #define CONFIG_INPUT_TSDEV_SCREEN_X 240
61 #ifndef CONFIG_INPUT_TSDEV_SCREEN_Y
62 #define CONFIG_INPUT_TSDEV_SCREEN_Y 320
65 /* This driver emulates both protocols of the old h3600_ts and h3600_tsraw
66 * devices. The first one must output X/Y data in 'cooked' format, e.g.
67 * filtered, dejittered and calibrated. Second device just outputs raw
68 * data received from the hardware.
70 * This driver doesn't support filtering and dejittering; it supports only
71 * calibration. Filtering and dejittering must be done in the low-level
72 * driver, if needed, because it may gain additional benefits from knowing
73 * the low-level details, the nature of noise and so on.
75 * The driver precomputes a calibration matrix given the initial xres and
76 * yres values (quite innacurate for most touchscreens) that will result
77 * in a more or less expected range of output values. The driver supports
78 * the TS_SET_CAL ioctl, which will replace the calibration matrix with a
79 * new one, supposedly generated from the values taken from the raw device.
82 MODULE_AUTHOR("James Simmons <jsimmons@transvirtual.com>");
83 MODULE_DESCRIPTION("Input driver to touchscreen converter");
84 MODULE_LICENSE("GPL");
86 static int xres
= CONFIG_INPUT_TSDEV_SCREEN_X
;
87 module_param(xres
, uint
, 0);
88 MODULE_PARM_DESC(xres
, "Horizontal screen resolution (can be negative for X-mirror)");
90 static int yres
= CONFIG_INPUT_TSDEV_SCREEN_Y
;
91 module_param(yres
, uint
, 0);
92 MODULE_PARM_DESC(yres
, "Vertical screen resolution (can be negative for Y-mirror)");
94 /* From Compaq's Touch Screen Specification version 0.2 (draft) */
102 struct ts_calibration
{
115 wait_queue_head_t wait
;
116 struct list_head list
;
117 struct input_handle handle
;
119 struct ts_calibration cal
;
123 struct fasync_struct
*fasync
;
124 struct list_head node
;
127 struct ts_event event
[TSDEV_BUFFER_SIZE
];
131 /* The following ioctl codes are defined ONLY for backward compatibility.
132 * Don't use tsdev for new developement; use the tslib library instead.
133 * Touchscreen calibration is a fully userspace task.
135 /* Use 'f' as magic number */
136 #define IOC_H3600_TS_MAGIC 'f'
137 #define TS_GET_CAL _IOR(IOC_H3600_TS_MAGIC, 10, struct ts_calibration)
138 #define TS_SET_CAL _IOW(IOC_H3600_TS_MAGIC, 11, struct ts_calibration)
140 static struct input_handler tsdev_handler
;
142 static struct tsdev
*tsdev_table
[TSDEV_MINORS
/2];
144 static int tsdev_fasync(int fd
, struct file
*file
, int on
)
146 struct tsdev_list
*list
= file
->private_data
;
149 retval
= fasync_helper(fd
, file
, on
, &list
->fasync
);
150 return retval
< 0 ? retval
: 0;
153 static int tsdev_open(struct inode
*inode
, struct file
*file
)
155 int i
= iminor(inode
) - TSDEV_MINOR_BASE
;
156 struct tsdev_list
*list
;
158 if (i
>= TSDEV_MINORS
|| !tsdev_table
[i
& TSDEV_MINOR_MASK
])
161 if (!(list
= kmalloc(sizeof(struct tsdev_list
), GFP_KERNEL
)))
163 memset(list
, 0, sizeof(struct tsdev_list
));
165 list
->raw
= (i
>= TSDEV_MINORS
/2) ? 1 : 0;
167 i
&= TSDEV_MINOR_MASK
;
168 list
->tsdev
= tsdev_table
[i
];
169 list_add_tail(&list
->node
, &tsdev_table
[i
]->list
);
170 file
->private_data
= list
;
172 if (!list
->tsdev
->open
++)
173 if (list
->tsdev
->exist
)
174 input_open_device(&list
->tsdev
->handle
);
178 static void tsdev_free(struct tsdev
*tsdev
)
180 tsdev_table
[tsdev
->minor
] = NULL
;
184 static int tsdev_release(struct inode
*inode
, struct file
*file
)
186 struct tsdev_list
*list
= file
->private_data
;
188 tsdev_fasync(-1, file
, 0);
189 list_del(&list
->node
);
191 if (!--list
->tsdev
->open
) {
192 if (list
->tsdev
->exist
)
193 input_close_device(&list
->tsdev
->handle
);
195 tsdev_free(list
->tsdev
);
201 static ssize_t
tsdev_read(struct file
*file
, char __user
*buffer
, size_t count
,
204 struct tsdev_list
*list
= file
->private_data
;
207 if (list
->head
== list
->tail
&& list
->tsdev
->exist
&& (file
->f_flags
& O_NONBLOCK
))
210 retval
= wait_event_interruptible(list
->tsdev
->wait
,
211 list
->head
!= list
->tail
|| !list
->tsdev
->exist
);
216 if (!list
->tsdev
->exist
)
219 while (list
->head
!= list
->tail
&&
220 retval
+ sizeof (struct ts_event
) <= count
) {
221 if (copy_to_user (buffer
+ retval
, list
->event
+ list
->tail
,
222 sizeof (struct ts_event
)))
224 list
->tail
= (list
->tail
+ 1) & (TSDEV_BUFFER_SIZE
- 1);
225 retval
+= sizeof (struct ts_event
);
231 /* No kernel lock - fine */
232 static unsigned int tsdev_poll(struct file
*file
, poll_table
* wait
)
234 struct tsdev_list
*list
= file
->private_data
;
235 poll_wait(file
, &list
->tsdev
->wait
, wait
);
236 return ((list
->head
== list
->tail
) ? 0 : (POLLIN
| POLLRDNORM
)) |
237 (list
->tsdev
->exist
? 0 : (POLLHUP
| POLLERR
));
240 static int tsdev_ioctl(struct inode
*inode
, struct file
*file
,
241 unsigned int cmd
, unsigned long arg
)
243 struct tsdev_list
*list
= file
->private_data
;
244 struct tsdev
*tsdev
= list
->tsdev
;
249 if (copy_to_user ((void __user
*)arg
, &tsdev
->cal
,
250 sizeof (struct ts_calibration
)))
254 if (copy_from_user (&tsdev
->cal
, (void __user
*)arg
,
255 sizeof (struct ts_calibration
)))
266 static struct file_operations tsdev_fops
= {
267 .owner
= THIS_MODULE
,
269 .release
= tsdev_release
,
272 .fasync
= tsdev_fasync
,
273 .ioctl
= tsdev_ioctl
,
276 static void tsdev_event(struct input_handle
*handle
, unsigned int type
,
277 unsigned int code
, int value
)
279 struct tsdev
*tsdev
= handle
->private;
280 struct tsdev_list
*list
;
293 if (value
> handle
->dev
->absmax
[ABS_PRESSURE
])
294 value
= handle
->dev
->absmax
[ABS_PRESSURE
];
295 value
-= handle
->dev
->absmin
[ABS_PRESSURE
];
298 tsdev
->pressure
= value
;
309 else if (tsdev
->x
> xres
)
316 else if (tsdev
->y
> yres
)
323 if (code
== BTN_TOUCH
|| code
== BTN_MOUSE
) {
329 if (!tsdev
->pressure
)
337 if (type
!= EV_SYN
|| code
!= SYN_REPORT
)
340 list_for_each_entry(list
, &tsdev
->list
, node
) {
343 do_gettimeofday(&time
);
344 list
->event
[list
->head
].millisecs
= time
.tv_usec
/ 100;
345 list
->event
[list
->head
].pressure
= tsdev
->pressure
;
352 x
= ((x
* tsdev
->cal
.xscale
) >> 8) + tsdev
->cal
.xtrans
;
353 y
= ((y
* tsdev
->cal
.yscale
) >> 8) + tsdev
->cal
.ytrans
;
354 if (tsdev
->cal
.xyswap
) {
355 tmp
= x
; x
= y
; y
= tmp
;
359 list
->event
[list
->head
].x
= x
;
360 list
->event
[list
->head
].y
= y
;
361 list
->head
= (list
->head
+ 1) & (TSDEV_BUFFER_SIZE
- 1);
362 kill_fasync(&list
->fasync
, SIGIO
, POLL_IN
);
364 wake_up_interruptible(&tsdev
->wait
);
367 static struct input_handle
*tsdev_connect(struct input_handler
*handler
,
368 struct input_dev
*dev
,
369 struct input_device_id
*id
)
374 for (minor
= 0; minor
< TSDEV_MINORS
/2 && tsdev_table
[minor
];
376 if (minor
>= TSDEV_MINORS
/2) {
378 "tsdev: You have way too many touchscreens\n");
382 if (!(tsdev
= kmalloc(sizeof(struct tsdev
), GFP_KERNEL
)))
384 memset(tsdev
, 0, sizeof(struct tsdev
));
386 INIT_LIST_HEAD(&tsdev
->list
);
387 init_waitqueue_head(&tsdev
->wait
);
389 sprintf(tsdev
->name
, "ts%d", minor
);
392 tsdev
->minor
= minor
;
393 tsdev
->handle
.dev
= dev
;
394 tsdev
->handle
.name
= tsdev
->name
;
395 tsdev
->handle
.handler
= handler
;
396 tsdev
->handle
.private = tsdev
;
398 /* Precompute the rough calibration matrix */
399 delta
= dev
->absmax
[ABS_X
] - dev
->absmin
[ABS_X
] + 1;
402 tsdev
->cal
.xscale
= (xres
<< 8) / delta
;
403 tsdev
->cal
.xtrans
= - ((dev
->absmin
[ABS_X
] * tsdev
->cal
.xscale
) >> 8);
405 delta
= dev
->absmax
[ABS_Y
] - dev
->absmin
[ABS_Y
] + 1;
408 tsdev
->cal
.yscale
= (yres
<< 8) / delta
;
409 tsdev
->cal
.ytrans
= - ((dev
->absmin
[ABS_Y
] * tsdev
->cal
.yscale
) >> 8);
411 tsdev_table
[minor
] = tsdev
;
413 devfs_mk_cdev(MKDEV(INPUT_MAJOR
, TSDEV_MINOR_BASE
+ minor
),
414 S_IFCHR
|S_IRUGO
|S_IWUSR
, "input/ts%d", minor
);
415 devfs_mk_cdev(MKDEV(INPUT_MAJOR
, TSDEV_MINOR_BASE
+ minor
+ TSDEV_MINORS
/2),
416 S_IFCHR
|S_IRUGO
|S_IWUSR
, "input/tsraw%d", minor
);
417 class_device_create(input_class
,
418 MKDEV(INPUT_MAJOR
, TSDEV_MINOR_BASE
+ minor
),
419 dev
->dev
, "ts%d", minor
);
421 return &tsdev
->handle
;
424 static void tsdev_disconnect(struct input_handle
*handle
)
426 struct tsdev
*tsdev
= handle
->private;
427 struct tsdev_list
*list
;
429 class_device_destroy(input_class
,
430 MKDEV(INPUT_MAJOR
, TSDEV_MINOR_BASE
+ tsdev
->minor
));
431 devfs_remove("input/ts%d", tsdev
->minor
);
432 devfs_remove("input/tsraw%d", tsdev
->minor
);
436 input_close_device(handle
);
437 wake_up_interruptible(&tsdev
->wait
);
438 list_for_each_entry(list
, &tsdev
->list
, node
)
439 kill_fasync(&list
->fasync
, SIGIO
, POLL_HUP
);
444 static struct input_device_id tsdev_ids
[] = {
446 .flags
= INPUT_DEVICE_ID_MATCH_EVBIT
| INPUT_DEVICE_ID_MATCH_KEYBIT
| INPUT_DEVICE_ID_MATCH_RELBIT
,
447 .evbit
= { BIT(EV_KEY
) | BIT(EV_REL
) },
448 .keybit
= { [LONG(BTN_LEFT
)] = BIT(BTN_LEFT
) },
449 .relbit
= { BIT(REL_X
) | BIT(REL_Y
) },
450 },/* A mouse like device, at least one button, two relative axes */
453 .flags
= INPUT_DEVICE_ID_MATCH_EVBIT
| INPUT_DEVICE_ID_MATCH_KEYBIT
| INPUT_DEVICE_ID_MATCH_ABSBIT
,
454 .evbit
= { BIT(EV_KEY
) | BIT(EV_ABS
) },
455 .keybit
= { [LONG(BTN_TOUCH
)] = BIT(BTN_TOUCH
) },
456 .absbit
= { BIT(ABS_X
) | BIT(ABS_Y
) },
457 },/* A tablet like device, at least touch detection, two absolute axes */
460 .flags
= INPUT_DEVICE_ID_MATCH_EVBIT
| INPUT_DEVICE_ID_MATCH_ABSBIT
,
461 .evbit
= { BIT(EV_ABS
) },
462 .absbit
= { BIT(ABS_X
) | BIT(ABS_Y
) | BIT(ABS_PRESSURE
) },
463 },/* A tablet like device with several gradations of pressure */
465 {},/* Terminating entry */
468 MODULE_DEVICE_TABLE(input
, tsdev_ids
);
470 static struct input_handler tsdev_handler
= {
471 .event
= tsdev_event
,
472 .connect
= tsdev_connect
,
473 .disconnect
= tsdev_disconnect
,
475 .minor
= TSDEV_MINOR_BASE
,
477 .id_table
= tsdev_ids
,
480 static int __init
tsdev_init(void)
482 input_register_handler(&tsdev_handler
);
483 printk(KERN_INFO
"ts: Compaq touchscreen protocol output\n");
487 static void __exit
tsdev_exit(void)
489 input_unregister_handler(&tsdev_handler
);
492 module_init(tsdev_init
);
493 module_exit(tsdev_exit
);