1/* 2 * $Id: tsdev.c,v 1.1.1.1 2007/08/03 18:52:32 Exp $ 3 * 4 * Copyright (c) 2001 "Crazy" james Simmons 5 * 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. 12 * 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 16 * emulation. 17 */ 18 19/* 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. 24 * 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. 29 * 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 33 * 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>. 36 */ 37 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 43 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/random.h> 52#include <linux/time.h> 53#include <linux/device.h> 54 55#ifndef CONFIG_INPUT_TSDEV_SCREEN_X 56#define CONFIG_INPUT_TSDEV_SCREEN_X 240 57#endif 58#ifndef CONFIG_INPUT_TSDEV_SCREEN_Y 59#define CONFIG_INPUT_TSDEV_SCREEN_Y 320 60#endif 61 62/* This driver emulates both protocols of the old h3600_ts and h3600_tsraw 63 * devices. The first one must output X/Y data in 'cooked' format, e.g. 64 * filtered, dejittered and calibrated. Second device just outputs raw 65 * data received from the hardware. 66 * 67 * This driver doesn't support filtering and dejittering; it supports only 68 * calibration. Filtering and dejittering must be done in the low-level 69 * driver, if needed, because it may gain additional benefits from knowing 70 * the low-level details, the nature of noise and so on. 71 * 72 * The driver precomputes a calibration matrix given the initial xres and 73 * yres values (quite innacurate for most touchscreens) that will result 74 * in a more or less expected range of output values. The driver supports 75 * the TS_SET_CAL ioctl, which will replace the calibration matrix with a 76 * new one, supposedly generated from the values taken from the raw device. 77 */ 78 79MODULE_AUTHOR("James Simmons <jsimmons@transvirtual.com>"); 80MODULE_DESCRIPTION("Input driver to touchscreen converter"); 81MODULE_LICENSE("GPL"); 82 83static int xres = CONFIG_INPUT_TSDEV_SCREEN_X; 84module_param(xres, uint, 0); 85MODULE_PARM_DESC(xres, "Horizontal screen resolution (can be negative for X-mirror)"); 86 87static int yres = CONFIG_INPUT_TSDEV_SCREEN_Y; 88module_param(yres, uint, 0); 89MODULE_PARM_DESC(yres, "Vertical screen resolution (can be negative for Y-mirror)"); 90 91/* From Compaq's Touch Screen Specification version 0.2 (draft) */ 92struct ts_event { 93 short pressure; 94 short x; 95 short y; 96 short millisecs; 97}; 98 99struct ts_calibration { 100 int xscale; 101 int xtrans; 102 int yscale; 103 int ytrans; 104 int xyswap; 105}; 106 107struct tsdev { 108 int exist; 109 int open; 110 int minor; 111 char name[8]; 112 wait_queue_head_t wait; 113 struct list_head client_list; 114 struct input_handle handle; 115 int x, y, pressure; 116 struct ts_calibration cal; 117}; 118 119struct tsdev_client { 120 struct fasync_struct *fasync; 121 struct list_head node; 122 struct tsdev *tsdev; 123 int head, tail; 124 struct ts_event event[TSDEV_BUFFER_SIZE]; 125 int raw; 126}; 127 128/* The following ioctl codes are defined ONLY for backward compatibility. 129 * Don't use tsdev for new developement; use the tslib library instead. 130 * Touchscreen calibration is a fully userspace task. 131 */ 132/* Use 'f' as magic number */ 133#define IOC_H3600_TS_MAGIC 'f' 134#define TS_GET_CAL _IOR(IOC_H3600_TS_MAGIC, 10, struct ts_calibration) 135#define TS_SET_CAL _IOW(IOC_H3600_TS_MAGIC, 11, struct ts_calibration) 136 137static struct tsdev *tsdev_table[TSDEV_MINORS/2]; 138 139static int tsdev_fasync(int fd, struct file *file, int on) 140{ 141 struct tsdev_client *client = file->private_data; 142 int retval; 143 144 retval = fasync_helper(fd, file, on, &client->fasync); 145 return retval < 0 ? retval : 0; 146} 147 148static int tsdev_open(struct inode *inode, struct file *file) 149{ 150 int i = iminor(inode) - TSDEV_MINOR_BASE; 151 struct tsdev_client *client; 152 struct tsdev *tsdev; 153 int error; 154 155 printk(KERN_WARNING "tsdev (compaq touchscreen emulation) is scheduled " 156 "for removal.\nSee Documentation/feature-removal-schedule.txt " 157 "for details.\n"); 158 159 if (i >= TSDEV_MINORS) 160 return -ENODEV; 161 162 tsdev = tsdev_table[i & TSDEV_MINOR_MASK]; 163 if (!tsdev || !tsdev->exist) 164 return -ENODEV; 165 166 client = kzalloc(sizeof(struct tsdev_client), GFP_KERNEL); 167 if (!client) 168 return -ENOMEM; 169 170 client->tsdev = tsdev; 171 client->raw = (i >= TSDEV_MINORS / 2) ? 1 : 0; 172 list_add_tail(&client->node, &tsdev->client_list); 173 174 if (!tsdev->open++ && tsdev->exist) { 175 error = input_open_device(&tsdev->handle); 176 if (error) { 177 list_del(&client->node); 178 kfree(client); 179 return error; 180 } 181 } 182 183 file->private_data = client; 184 return 0; 185} 186 187static void tsdev_free(struct tsdev *tsdev) 188{ 189 tsdev_table[tsdev->minor] = NULL; 190 kfree(tsdev); 191} 192 193static int tsdev_release(struct inode *inode, struct file *file) 194{ 195 struct tsdev_client *client = file->private_data; 196 struct tsdev *tsdev = client->tsdev; 197 198 tsdev_fasync(-1, file, 0); 199 200 list_del(&client->node); 201 kfree(client); 202 203 if (!--tsdev->open) { 204 if (tsdev->exist) 205 input_close_device(&tsdev->handle); 206 else 207 tsdev_free(tsdev); 208 } 209 210 return 0; 211} 212 213static ssize_t tsdev_read(struct file *file, char __user *buffer, size_t count, 214 loff_t *ppos) 215{ 216 struct tsdev_client *client = file->private_data; 217 struct tsdev *tsdev = client->tsdev; 218 int retval = 0; 219 220 if (client->head == client->tail && tsdev->exist && (file->f_flags & O_NONBLOCK)) 221 return -EAGAIN; 222 223 retval = wait_event_interruptible(tsdev->wait, 224 client->head != client->tail || !tsdev->exist); 225 if (retval) 226 return retval; 227 228 if (!tsdev->exist) 229 return -ENODEV; 230 231 while (client->head != client->tail && 232 retval + sizeof (struct ts_event) <= count) { 233 if (copy_to_user (buffer + retval, client->event + client->tail, 234 sizeof (struct ts_event))) 235 return -EFAULT; 236 client->tail = (client->tail + 1) & (TSDEV_BUFFER_SIZE - 1); 237 retval += sizeof (struct ts_event); 238 } 239 240 return retval; 241} 242 243/* No kernel lock - fine */ 244static unsigned int tsdev_poll(struct file *file, poll_table *wait) 245{ 246 struct tsdev_client *client = file->private_data; 247 struct tsdev *tsdev = client->tsdev; 248 249 poll_wait(file, &tsdev->wait, wait); 250 return ((client->head == client->tail) ? 0 : (POLLIN | POLLRDNORM)) | 251 (tsdev->exist ? 0 : (POLLHUP | POLLERR)); 252} 253 254static int tsdev_ioctl(struct inode *inode, struct file *file, 255 unsigned int cmd, unsigned long arg) 256{ 257 struct tsdev_client *client = file->private_data; 258 struct tsdev *tsdev = client->tsdev; 259 int retval = 0; 260 261 switch (cmd) { 262 case TS_GET_CAL: 263 if (copy_to_user((void __user *)arg, &tsdev->cal, 264 sizeof (struct ts_calibration))) 265 retval = -EFAULT; 266 break; 267 268 case TS_SET_CAL: 269 if (copy_from_user(&tsdev->cal, (void __user *)arg, 270 sizeof (struct ts_calibration))) 271 retval = -EFAULT; 272 break; 273 274 default: 275 retval = -EINVAL; 276 break; 277 } 278 279 return retval; 280} 281 282static const struct file_operations tsdev_fops = { 283 .owner = THIS_MODULE, 284 .open = tsdev_open, 285 .release = tsdev_release, 286 .read = tsdev_read, 287 .poll = tsdev_poll, 288 .fasync = tsdev_fasync, 289 .ioctl = tsdev_ioctl, 290}; 291 292static void tsdev_event(struct input_handle *handle, unsigned int type, 293 unsigned int code, int value) 294{ 295 struct tsdev *tsdev = handle->private; 296 struct tsdev_client *client; 297 struct timeval time; 298 299 switch (type) { 300 case EV_ABS: 301 switch (code) { 302 case ABS_X: 303 tsdev->x = value; 304 break; 305 306 case ABS_Y: 307 tsdev->y = value; 308 break; 309 310 case ABS_PRESSURE: 311 if (value > handle->dev->absmax[ABS_PRESSURE]) 312 value = handle->dev->absmax[ABS_PRESSURE]; 313 value -= handle->dev->absmin[ABS_PRESSURE]; 314 if (value < 0) 315 value = 0; 316 tsdev->pressure = value; 317 break; 318 } 319 break; 320 321 case EV_REL: 322 switch (code) { 323 case REL_X: 324 tsdev->x += value; 325 if (tsdev->x < 0) 326 tsdev->x = 0; 327 else if (tsdev->x > xres) 328 tsdev->x = xres; 329 break; 330 331 case REL_Y: 332 tsdev->y += value; 333 if (tsdev->y < 0) 334 tsdev->y = 0; 335 else if (tsdev->y > yres) 336 tsdev->y = yres; 337 break; 338 } 339 break; 340 341 case EV_KEY: 342 if (code == BTN_TOUCH || code == BTN_MOUSE) { 343 switch (value) { 344 case 0: 345 tsdev->pressure = 0; 346 break; 347 348 case 1: 349 if (!tsdev->pressure) 350 tsdev->pressure = 1; 351 break; 352 } 353 } 354 break; 355 } 356 357 if (type != EV_SYN || code != SYN_REPORT) 358 return; 359 360 list_for_each_entry(client, &tsdev->client_list, node) { 361 int x, y, tmp; 362 363 do_gettimeofday(&time); 364 client->event[client->head].millisecs = time.tv_usec / 100; 365 client->event[client->head].pressure = tsdev->pressure; 366 367 x = tsdev->x; 368 y = tsdev->y; 369 370 /* Calibration */ 371 if (!client->raw) { 372 x = ((x * tsdev->cal.xscale) >> 8) + tsdev->cal.xtrans; 373 y = ((y * tsdev->cal.yscale) >> 8) + tsdev->cal.ytrans; 374 if (tsdev->cal.xyswap) { 375 tmp = x; x = y; y = tmp; 376 } 377 } 378 379 client->event[client->head].x = x; 380 client->event[client->head].y = y; 381 client->head = (client->head + 1) & (TSDEV_BUFFER_SIZE - 1); 382 kill_fasync(&client->fasync, SIGIO, POLL_IN); 383 } 384 wake_up_interruptible(&tsdev->wait); 385} 386 387static int tsdev_connect(struct input_handler *handler, struct input_dev *dev, 388 const struct input_device_id *id) 389{ 390 struct tsdev *tsdev; 391 struct class_device *cdev; 392 dev_t devt; 393 int minor, delta; 394 int error; 395 396 for (minor = 0; minor < TSDEV_MINORS / 2 && tsdev_table[minor]; minor++); 397 if (minor >= TSDEV_MINORS / 2) { 398 printk(KERN_ERR 399 "tsdev: You have way too many touchscreens\n"); 400 return -ENFILE; 401 } 402 403 tsdev = kzalloc(sizeof(struct tsdev), GFP_KERNEL); 404 if (!tsdev) 405 return -ENOMEM; 406 407 INIT_LIST_HEAD(&tsdev->client_list); 408 init_waitqueue_head(&tsdev->wait); 409 410 sprintf(tsdev->name, "ts%d", minor); 411 412 tsdev->exist = 1; 413 tsdev->minor = minor; 414 tsdev->handle.dev = dev; 415 tsdev->handle.name = tsdev->name; 416 tsdev->handle.handler = handler; 417 tsdev->handle.private = tsdev; 418 419 /* Precompute the rough calibration matrix */ 420 delta = dev->absmax [ABS_X] - dev->absmin [ABS_X] + 1; 421 if (delta == 0) 422 delta = 1; 423 tsdev->cal.xscale = (xres << 8) / delta; 424 tsdev->cal.xtrans = - ((dev->absmin [ABS_X] * tsdev->cal.xscale) >> 8); 425 426 delta = dev->absmax [ABS_Y] - dev->absmin [ABS_Y] + 1; 427 if (delta == 0) 428 delta = 1; 429 tsdev->cal.yscale = (yres << 8) / delta; 430 tsdev->cal.ytrans = - ((dev->absmin [ABS_Y] * tsdev->cal.yscale) >> 8); 431 432 tsdev_table[minor] = tsdev; 433 434 devt = MKDEV(INPUT_MAJOR, TSDEV_MINOR_BASE + minor), 435 436 cdev = class_device_create(&input_class, &dev->cdev, devt, 437 dev->cdev.dev, tsdev->name); 438 if (IS_ERR(cdev)) { 439 error = PTR_ERR(cdev); 440 goto err_free_tsdev; 441 } 442 443 /* temporary symlink to keep userspace happy */ 444 error = sysfs_create_link(&input_class.subsys.kobj, 445 &cdev->kobj, tsdev->name); 446 if (error) 447 goto err_cdev_destroy; 448 449 error = input_register_handle(&tsdev->handle); 450 if (error) 451 goto err_remove_link; 452 453 return 0; 454 455 err_remove_link: 456 sysfs_remove_link(&input_class.subsys.kobj, tsdev->name); 457 err_cdev_destroy: 458 class_device_destroy(&input_class, devt); 459 err_free_tsdev: 460 tsdev_table[minor] = NULL; 461 kfree(tsdev); 462 return error; 463} 464 465static void tsdev_disconnect(struct input_handle *handle) 466{ 467 struct tsdev *tsdev = handle->private; 468 struct tsdev_client *client; 469 470 input_unregister_handle(handle); 471 472 sysfs_remove_link(&input_class.subsys.kobj, tsdev->name); 473 class_device_destroy(&input_class, 474 MKDEV(INPUT_MAJOR, TSDEV_MINOR_BASE + tsdev->minor)); 475 tsdev->exist = 0; 476 477 if (tsdev->open) { 478 input_close_device(handle); 479 list_for_each_entry(client, &tsdev->client_list, node) 480 kill_fasync(&client->fasync, SIGIO, POLL_HUP); 481 wake_up_interruptible(&tsdev->wait); 482 } else 483 tsdev_free(tsdev); 484} 485 486static const struct input_device_id tsdev_ids[] = { 487 { 488 .flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_KEYBIT | INPUT_DEVICE_ID_MATCH_RELBIT, 489 .evbit = { BIT(EV_KEY) | BIT(EV_REL) }, 490 .keybit = { [LONG(BTN_LEFT)] = BIT(BTN_LEFT) }, 491 .relbit = { BIT(REL_X) | BIT(REL_Y) }, 492 }, /* A mouse like device, at least one button, two relative axes */ 493 494 { 495 .flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_KEYBIT | INPUT_DEVICE_ID_MATCH_ABSBIT, 496 .evbit = { BIT(EV_KEY) | BIT(EV_ABS) }, 497 .keybit = { [LONG(BTN_TOUCH)] = BIT(BTN_TOUCH) }, 498 .absbit = { BIT(ABS_X) | BIT(ABS_Y) }, 499 }, /* A tablet like device, at least touch detection, two absolute axes */ 500 501 { 502 .flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_ABSBIT, 503 .evbit = { BIT(EV_ABS) }, 504 .absbit = { BIT(ABS_X) | BIT(ABS_Y) | BIT(ABS_PRESSURE) }, 505 }, /* A tablet like device with several gradations of pressure */ 506 507 {} /* Terminating entry */ 508}; 509 510MODULE_DEVICE_TABLE(input, tsdev_ids); 511 512static struct input_handler tsdev_handler = { 513 .event = tsdev_event, 514 .connect = tsdev_connect, 515 .disconnect = tsdev_disconnect, 516 .fops = &tsdev_fops, 517 .minor = TSDEV_MINOR_BASE, 518 .name = "tsdev", 519 .id_table = tsdev_ids, 520}; 521 522static int __init tsdev_init(void) 523{ 524 return input_register_handler(&tsdev_handler); 525} 526 527static void __exit tsdev_exit(void) 528{ 529 input_unregister_handler(&tsdev_handler); 530} 531 532module_init(tsdev_init); 533module_exit(tsdev_exit); 534