linux-old/drivers/input/mousedev.c
<<
>>
Prefs
   1/*
   2 * $Id: mousedev.c,v 1.24 2000/11/15 10:57:45 vojtech Exp $
   3 *
   4 *  Copyright (c) 1999-2000 Vojtech Pavlik
   5 *
   6 *  Input driver to ImExPS/2 device driver module.
   7 *
   8 *  Sponsored by SuSE
   9 */
  10
  11/*
  12 * This program is free software; you can redistribute it and/or modify
  13 * it under the terms of the GNU General Public License as published by
  14 * the Free Software Foundation; either version 2 of the License, or 
  15 * (at your option) any later version.
  16 * 
  17 * This program is distributed in the hope that it will be useful,
  18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  20 * GNU General Public License for more details.
  21 * 
  22 * You should have received a copy of the GNU General Public License
  23 * along with this program; if not, write to the Free Software
  24 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  25 * 
  26 * Should you need to contact me, the author, you can do so either by
  27 * e-mail - mail your message to <vojtech@suse.cz>, or by paper mail:
  28 * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic
  29 */
  30
  31#define MOUSEDEV_MINOR_BASE     32
  32#define MOUSEDEV_MINORS         32
  33#define MOUSEDEV_MIX            31
  34
  35#include <linux/slab.h>
  36#include <linux/poll.h>
  37#include <linux/module.h>
  38#include <linux/init.h>
  39#include <linux/input.h>
  40#include <linux/config.h>
  41#include <linux/smp_lock.h>
  42#include <linux/random.h>
  43
  44#ifndef CONFIG_INPUT_MOUSEDEV_SCREEN_X
  45#define CONFIG_INPUT_MOUSEDEV_SCREEN_X  1024
  46#endif
  47#ifndef CONFIG_INPUT_MOUSEDEV_SCREEN_Y
  48#define CONFIG_INPUT_MOUSEDEV_SCREEN_Y  768
  49#endif
  50
  51struct mousedev {
  52        int exist;
  53        int open;
  54        int minor;
  55        wait_queue_head_t wait;
  56        struct mousedev_list *list;
  57        struct input_handle handle;
  58        devfs_handle_t devfs;
  59};
  60
  61struct mousedev_list {
  62        struct fasync_struct *fasync;
  63        struct mousedev *mousedev;
  64        struct mousedev_list *next;
  65        int dx, dy, dz, oldx, oldy;
  66        signed char ps2[6];
  67        unsigned long buttons;
  68        unsigned char ready, buffer, bufsiz;
  69        unsigned char mode, imexseq, impsseq;
  70};
  71
  72#define MOUSEDEV_SEQ_LEN        6
  73
  74static unsigned char mousedev_imps_seq[] = { 0xf3, 200, 0xf3, 100, 0xf3, 80 };
  75static unsigned char mousedev_imex_seq[] = { 0xf3, 200, 0xf3, 200, 0xf3, 80 };
  76
  77static struct input_handler mousedev_handler;
  78
  79static struct mousedev *mousedev_table[MOUSEDEV_MINORS];
  80static struct mousedev mousedev_mix;
  81
  82static int xres = CONFIG_INPUT_MOUSEDEV_SCREEN_X;
  83static int yres = CONFIG_INPUT_MOUSEDEV_SCREEN_Y;
  84
  85static void mousedev_event(struct input_handle *handle, unsigned int type, unsigned int code, int value)
  86{
  87        struct mousedev *mousedevs[3] = { handle->private, &mousedev_mix, NULL };
  88        struct mousedev **mousedev = mousedevs;
  89        struct mousedev_list *list;
  90        int index, size;
  91
  92        add_mouse_randomness((type << 4) ^ code ^ (code >> 4) ^ value);
  93
  94        while (*mousedev) {
  95                list = (*mousedev)->list;
  96                while (list) {
  97                        switch (type) {
  98                                case EV_ABS:
  99                                        if (test_bit(BTN_TRIGGER, handle->dev->keybit))
 100                                                break;
 101                                        switch (code) {
 102                                                case ABS_X:     
 103                                                        size = handle->dev->absmax[ABS_X] - handle->dev->absmin[ABS_X];
 104                                                        list->dx += (value * xres - list->oldx) / size;
 105                                                        list->oldx += list->dx * size;
 106                                                        break;
 107                                                case ABS_Y:
 108                                                        size = handle->dev->absmax[ABS_Y] - handle->dev->absmin[ABS_Y];
 109                                                        list->dy -= (value * yres - list->oldy) / size;
 110                                                        list->oldy -= list->dy * size;
 111                                                        break;
 112                                        }
 113                                        break;
 114
 115                                case EV_REL:
 116                                        switch (code) {
 117                                                case REL_X:     list->dx += value; break;
 118                                                case REL_Y:     list->dy -= value; break;
 119                                                case REL_WHEEL: if (list->mode) list->dz -= value; break;
 120                                        }
 121                                        break;
 122
 123                                case EV_KEY:
 124                                        switch (code) {
 125                                                case BTN_0:
 126                                                case BTN_TOUCH:
 127                                                case BTN_LEFT:   index = 0; break;
 128                                                case BTN_4:
 129                                                case BTN_EXTRA:  if (list->mode == 2) { index = 4; break; }
 130                                                case BTN_STYLUS:
 131                                                case BTN_1:
 132                                                case BTN_RIGHT:  index = 1; break;
 133                                                case BTN_3:
 134                                                case BTN_SIDE:   if (list->mode == 2) { index = 3; break; }
 135                                                case BTN_2:
 136                                                case BTN_STYLUS2:
 137                                                case BTN_MIDDLE: index = 2; break;      
 138                                                default: return;
 139                                        }
 140                                        switch (value) {
 141                                                case 0: clear_bit(index, &list->buttons); break;
 142                                                case 1: set_bit(index, &list->buttons); break;
 143                                                case 2: return;
 144                                        }
 145                                        break;
 146                        }
 147                                        
 148                        list->ready = 1;
 149
 150                        kill_fasync(&list->fasync, SIGIO, POLL_IN);
 151
 152                        list = list->next;
 153                }
 154
 155                wake_up_interruptible(&((*mousedev)->wait));
 156                mousedev++;
 157        }
 158}
 159
 160static int mousedev_fasync(int fd, struct file *file, int on)
 161{
 162        int retval;
 163        struct mousedev_list *list = file->private_data;
 164        retval = fasync_helper(fd, file, on, &list->fasync);
 165        return retval < 0 ? retval : 0;
 166}
 167
 168static int mousedev_release(struct inode * inode, struct file * file)
 169{
 170        struct mousedev_list *list = file->private_data;
 171        struct mousedev_list **listptr;
 172
 173        lock_kernel();
 174        listptr = &list->mousedev->list;
 175        mousedev_fasync(-1, file, 0);
 176
 177        while (*listptr && (*listptr != list))
 178                listptr = &((*listptr)->next);
 179        *listptr = (*listptr)->next;
 180
 181        if (!--list->mousedev->open) {
 182                if (list->mousedev->minor == MOUSEDEV_MIX) {
 183                        struct input_handle *handle = mousedev_handler.handle;
 184                        while (handle) {
 185                                struct mousedev *mousedev = handle->private;
 186                                if (!mousedev->open) {
 187                                        if (mousedev->exist) {
 188                                                input_close_device(&mousedev->handle);
 189                                        } else {
 190                                                input_unregister_minor(mousedev->devfs);
 191                                                mousedev_table[mousedev->minor] = NULL;
 192                                                kfree(mousedev);
 193                                        }
 194                                }
 195                                handle = handle->hnext;
 196                        }
 197                } else {
 198                        if (!mousedev_mix.open) {
 199                                if (list->mousedev->exist) {
 200                                        input_close_device(&list->mousedev->handle);
 201                                } else {
 202                                        input_unregister_minor(list->mousedev->devfs);
 203                                        mousedev_table[list->mousedev->minor] = NULL;
 204                                        kfree(list->mousedev);
 205                                }
 206                        }
 207                }
 208        }
 209        
 210        kfree(list);
 211        unlock_kernel();
 212
 213        return 0;
 214}
 215
 216static int mousedev_open(struct inode * inode, struct file * file)
 217{
 218        struct mousedev_list *list;
 219        int i = MINOR(inode->i_rdev) - MOUSEDEV_MINOR_BASE;
 220
 221        if (i >= MOUSEDEV_MINORS || !mousedev_table[i])
 222                return -ENODEV;
 223
 224        if (!(list = kmalloc(sizeof(struct mousedev_list), GFP_KERNEL)))
 225                return -ENOMEM;
 226        memset(list, 0, sizeof(struct mousedev_list));
 227
 228        list->mousedev = mousedev_table[i];
 229        list->next = mousedev_table[i]->list;
 230        mousedev_table[i]->list = list;
 231        file->private_data = list;
 232
 233        if (!list->mousedev->open++) {
 234                if (list->mousedev->minor == MOUSEDEV_MIX) {
 235                        struct input_handle *handle = mousedev_handler.handle;
 236                        while (handle) {
 237                                struct mousedev *mousedev = handle->private;
 238                                if (!mousedev->open)
 239                                        if (mousedev->exist)    
 240                                                input_open_device(handle);
 241                                handle = handle->hnext;
 242                        }
 243                } else {
 244                        if (!mousedev_mix.open)
 245                                if (list->mousedev->exist)      
 246                                        input_open_device(&list->mousedev->handle);
 247                }
 248        }
 249
 250        return 0;
 251}
 252
 253static void mousedev_packet(struct mousedev_list *list, unsigned char off)
 254{
 255        list->ps2[off] = 0x08 | ((list->dx < 0) << 4) | ((list->dy < 0) << 5) | (list->buttons & 0x07);
 256        list->ps2[off + 1] = (list->dx > 127 ? 127 : (list->dx < -127 ? -127 : list->dx));
 257        list->ps2[off + 2] = (list->dy > 127 ? 127 : (list->dy < -127 ? -127 : list->dy));
 258        list->dx -= list->ps2[off + 1];
 259        list->dy -= list->ps2[off + 2];
 260        list->bufsiz = off + 3;
 261
 262        if (list->mode == 2) {
 263                list->ps2[off + 3] = (list->dz > 7 ? 7 : (list->dz < -7 ? -7 : list->dz));
 264                list->dz -= list->ps2[off + 3];
 265                list->ps2[off + 3] = (list->ps2[off + 3] & 0x0f) | ((list->buttons & 0x18) << 1);
 266                list->bufsiz++;
 267        }
 268        
 269        if (list->mode == 1) {
 270                list->ps2[off + 3] = (list->dz > 127 ? 127 : (list->dz < -127 ? -127 : list->dz));
 271                list->dz -= list->ps2[off + 3];
 272                list->bufsiz++;
 273        }
 274
 275        if (!list->dx && !list->dy && (!list->mode || !list->dz)) list->ready = 0;
 276        list->buffer = list->bufsiz;
 277}
 278
 279
 280static ssize_t mousedev_write(struct file * file, const char * buffer, size_t count, loff_t *ppos)
 281{
 282        struct mousedev_list *list = file->private_data;
 283        unsigned char c;
 284        int i;
 285
 286        for (i = 0; i < count; i++) {
 287
 288                if (get_user(c, buffer + i))
 289                        return -EFAULT;
 290
 291                if (c == mousedev_imex_seq[list->imexseq]) {
 292                        if (++list->imexseq == MOUSEDEV_SEQ_LEN) {
 293                                list->imexseq = 0;
 294                                list->mode = 2;
 295                        }
 296                } else list->imexseq = 0;
 297
 298                if (c == mousedev_imps_seq[list->impsseq]) {
 299                        if (++list->impsseq == MOUSEDEV_SEQ_LEN) {
 300                                list->impsseq = 0;
 301                                list->mode = 1;
 302                        }
 303                } else list->impsseq = 0;
 304
 305                list->ps2[0] = 0xfa;
 306                list->bufsiz = 1;
 307                list->ready = 1;
 308
 309                switch (c) {
 310
 311                        case 0xeb: /* Poll */
 312                                mousedev_packet(list, 1);
 313                                break;
 314
 315                        case 0xf2: /* Get ID */
 316                                switch (list->mode) {
 317                                        case 0: list->ps2[1] = 0; break;
 318                                        case 1: list->ps2[1] = 3; break;
 319                                        case 2: list->ps2[1] = 4; break;
 320                                }
 321                                list->bufsiz = 2;
 322                                break;
 323
 324                        case 0xe9: /* Get info */
 325                                list->ps2[1] = 0x60; list->ps2[2] = 3; list->ps2[3] = 200;
 326                                list->bufsiz = 4;
 327                                break;
 328                }
 329
 330                list->buffer = list->bufsiz;
 331        }
 332
 333        kill_fasync(&list->fasync, SIGIO, POLL_IN);
 334
 335        wake_up_interruptible(&list->mousedev->wait);
 336                
 337        return count;
 338}
 339
 340static ssize_t mousedev_read(struct file * file, char * buffer, size_t count, loff_t *ppos)
 341{
 342        DECLARE_WAITQUEUE(wait, current);
 343        struct mousedev_list *list = file->private_data;
 344        int retval = 0;
 345
 346        if (!list->ready && !list->buffer) {
 347
 348                add_wait_queue(&list->mousedev->wait, &wait);
 349                current->state = TASK_INTERRUPTIBLE;
 350
 351                while (!list->ready) {
 352
 353                        if (file->f_flags & O_NONBLOCK) {
 354                                retval = -EAGAIN;
 355                                break;
 356                        }
 357                        if (signal_pending(current)) {
 358                                retval = -ERESTARTSYS;
 359                                break;
 360                        }
 361
 362                        schedule();
 363                }
 364
 365                current->state = TASK_RUNNING;
 366                remove_wait_queue(&list->mousedev->wait, &wait);
 367        }
 368
 369        if (retval)
 370                return retval;
 371
 372        if (!list->buffer)
 373                mousedev_packet(list, 0);
 374
 375        if (count > list->buffer)
 376                count = list->buffer;
 377
 378        if (copy_to_user(buffer, list->ps2 + list->bufsiz - list->buffer, count))
 379                return -EFAULT;
 380        
 381        list->buffer -= count;
 382
 383        return count;   
 384}
 385
 386/* No kernel lock - fine */
 387static unsigned int mousedev_poll(struct file *file, poll_table *wait)
 388{
 389        struct mousedev_list *list = file->private_data;
 390        poll_wait(file, &list->mousedev->wait, wait);
 391        if (list->ready || list->buffer)
 392                return POLLIN | POLLRDNORM;
 393        return 0;
 394}
 395
 396struct file_operations mousedev_fops = {
 397        owner:          THIS_MODULE,
 398        read:           mousedev_read,
 399        write:          mousedev_write,
 400        poll:           mousedev_poll,
 401        open:           mousedev_open,
 402        release:        mousedev_release,
 403        fasync:         mousedev_fasync,
 404};
 405
 406static struct input_handle *mousedev_connect(struct input_handler *handler, struct input_dev *dev)
 407{
 408        struct mousedev *mousedev;
 409        int minor = 0;
 410
 411        if (!test_bit(EV_KEY, dev->evbit) ||
 412            (!test_bit(BTN_LEFT, dev->keybit) && 
 413             !test_bit(BTN_MIDDLE, dev->keybit) && 
 414             !test_bit(BTN_TOUCH, dev->keybit)))
 415                return NULL;
 416
 417        if ((!test_bit(EV_REL, dev->evbit) || !test_bit(REL_X, dev->relbit)) &&
 418            (!test_bit(EV_REL, dev->evbit) || !test_bit(REL_WHEEL, dev->relbit)) &&
 419            (!test_bit(EV_ABS, dev->evbit) || !test_bit(ABS_X, dev->absbit)))
 420                return NULL;
 421
 422        for (minor = 0; minor < MOUSEDEV_MINORS && mousedev_table[minor]; minor++);
 423        if (minor == MOUSEDEV_MINORS) {
 424                printk(KERN_ERR "mousedev: no more free mousedev devices\n");
 425                return NULL;
 426        }
 427
 428        if (!(mousedev = kmalloc(sizeof(struct mousedev), GFP_KERNEL)))
 429                return NULL;
 430        memset(mousedev, 0, sizeof(struct mousedev));
 431        init_waitqueue_head(&mousedev->wait);
 432
 433        mousedev->exist = 1;
 434        mousedev->minor = minor;
 435        mousedev_table[minor] = mousedev;
 436
 437        mousedev->handle.dev = dev;
 438        mousedev->handle.handler = handler;
 439        mousedev->handle.private = mousedev;
 440
 441        mousedev->devfs = input_register_minor("mouse%d", minor, MOUSEDEV_MINOR_BASE);
 442
 443        if (mousedev_mix.open)
 444                input_open_device(&mousedev->handle);
 445
 446//      printk(KERN_INFO "mouse%d: PS/2 mouse device for input%d\n", minor, dev->number);
 447
 448        return &mousedev->handle;
 449}
 450
 451static void mousedev_disconnect(struct input_handle *handle)
 452{
 453        struct mousedev *mousedev = handle->private;
 454
 455        mousedev->exist = 0;
 456
 457        if (mousedev->open) {
 458                input_close_device(handle);
 459        } else {
 460                if (mousedev_mix.open)
 461                        input_close_device(handle);
 462                input_unregister_minor(mousedev->devfs);
 463                mousedev_table[mousedev->minor] = NULL;
 464                kfree(mousedev);
 465        }
 466}
 467        
 468static struct input_handler mousedev_handler = {
 469        event:          mousedev_event,
 470        connect:        mousedev_connect,
 471        disconnect:     mousedev_disconnect,
 472        fops:           &mousedev_fops,
 473        minor:          MOUSEDEV_MINOR_BASE,
 474};
 475
 476static int __init mousedev_init(void)
 477{
 478        input_register_handler(&mousedev_handler);
 479
 480        memset(&mousedev_mix, 0, sizeof(struct mousedev));
 481        init_waitqueue_head(&mousedev_mix.wait);
 482        mousedev_table[MOUSEDEV_MIX] = &mousedev_mix;
 483        mousedev_mix.exist = 1;
 484        mousedev_mix.minor = MOUSEDEV_MIX;
 485        mousedev_mix.devfs = input_register_minor("mice", MOUSEDEV_MIX, MOUSEDEV_MINOR_BASE);
 486
 487        printk(KERN_INFO "mice: PS/2 mouse device common for all mice\n");
 488
 489        return 0;
 490}
 491
 492static void __exit mousedev_exit(void)
 493{
 494        input_unregister_minor(mousedev_mix.devfs);
 495        input_unregister_handler(&mousedev_handler);
 496}
 497
 498module_init(mousedev_init);
 499module_exit(mousedev_exit);
 500
 501MODULE_AUTHOR("Vojtech Pavlik <vojtech@suse.cz>");
 502MODULE_DESCRIPTION("Input driver to PS/2 or ImPS/2 device driver");
 503MODULE_LICENSE("GPL");
 504
 505MODULE_PARM(xres, "i");
 506MODULE_PARM_DESC(xres, "Horizontal screen resolution");
 507MODULE_PARM(yres, "i");
 508MODULE_PARM_DESC(yres, "Vertical screen resolution");
 509
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.