Subversion Repositories shark

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
494 giacomo 1
/*
2
 * Input driver to ExplorerPS/2 device driver module.
3
 *
4
 * Copyright (c) 1999-2002 Vojtech Pavlik
5
 *
6
 * This program is free software; you can redistribute it and/or modify
7
 * it under the terms of the GNU General Public License version 2 as published by
8
 * the Free Software Foundation.
9
 */
10
 
11
#include <linuxcomp.h>
12
 
13
#define MOUSEDEV_MINOR_BASE     32
14
#define MOUSEDEV_MINORS         32
15
#define MOUSEDEV_MIX            31
16
 
17
#include <linux/slab.h>
18
#include <linux/poll.h>
19
#include <linux/module.h>
20
#include <linux/init.h>
21
#include <linux/input.h>
22
#include <linux/config.h>
23
#include <linux/smp_lock.h>
24
#include <linux/random.h>
25
#include <linux/major.h>
26
#include <linux/device.h>
27
#include <linux/devfs_fs_kernel.h>
28
#ifdef CONFIG_INPUT_MOUSEDEV_PSAUX
29
#include <linux/miscdevice.h>
30
#endif
31
 
32
MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
33
MODULE_DESCRIPTION("Mouse (ExplorerPS/2) device interfaces");
34
MODULE_LICENSE("GPL");
35
 
36
#ifndef CONFIG_INPUT_MOUSEDEV_SCREEN_X
37
#define CONFIG_INPUT_MOUSEDEV_SCREEN_X  1024
38
#endif
39
#ifndef CONFIG_INPUT_MOUSEDEV_SCREEN_Y
40
#define CONFIG_INPUT_MOUSEDEV_SCREEN_Y  768
41
#endif
42
 
43
struct mousedev {
44
        int exist;
45
        int open;
46
        int minor;
47
        int misc;
48
        char name[16];
49
        wait_queue_head_t wait;
50
        struct list_head list;
51
        struct input_handle handle;
52
};
53
 
54
struct mousedev_list {
55
        struct fasync_struct *fasync;
56
        struct mousedev *mousedev;
57
        struct list_head node;
58
        int dx, dy, dz, oldx, oldy;
59
        signed char ps2[6];
60
        unsigned long buttons;
61
        unsigned char ready, buffer, bufsiz;
62
        unsigned char mode, imexseq, impsseq;
63
        int finger;
64
};
65
 
66
#define MOUSEDEV_SEQ_LEN        6
67
 
68
static unsigned char mousedev_imps_seq[] = { 0xf3, 200, 0xf3, 100, 0xf3, 80 };
69
static unsigned char mousedev_imex_seq[] = { 0xf3, 200, 0xf3, 200, 0xf3, 80 };
70
 
71
static struct input_handler mousedev_handler;
72
 
73
static struct mousedev *mousedev_table[MOUSEDEV_MINORS];
74
static struct mousedev mousedev_mix;
75
 
76
static int xres = CONFIG_INPUT_MOUSEDEV_SCREEN_X;
77
static int yres = CONFIG_INPUT_MOUSEDEV_SCREEN_Y;
78
 
79
static void mousedev_abs_event(struct input_handle *handle, struct mousedev_list *list, unsigned int code, int value)
80
{
81
        int size;
82
 
83
        /* Ignore joysticks */
84
        if (test_bit(BTN_TRIGGER, handle->dev->keybit))
85
                return;
86
 
87
        /* Handle touchpad data */
88
        if (test_bit(BTN_TOOL_FINGER, handle->dev->keybit)) {
89
 
90
                if (list->finger && list->finger < 3)
91
                        list->finger++;
92
 
93
                switch (code) {
94
                        case ABS_X:
95
                                if (list->finger == 3)
96
                                        list->dx += (value - list->oldx) / 8;
97
                                list->oldx = value;
98
                                return;
99
                        case ABS_Y:
100
                                if (list->finger == 3)
101
                                        list->dy -= (value - list->oldy) / 8;
102
                                list->oldy = value;
103
                                return;
104
                }
105
                return;
106
        }
107
 
108
        /* Handle tablet data */
109
        switch (code) {
110
                case ABS_X:
111
                        size = handle->dev->absmax[ABS_X] - handle->dev->absmin[ABS_X];
112
                        if (size == 0) size = xres;
113
                        list->dx += (value * xres - list->oldx) / size;
114
                        list->oldx += list->dx * size;
115
                        return;
116
                case ABS_Y:
117
                        size = handle->dev->absmax[ABS_Y] - handle->dev->absmin[ABS_Y];
118
                        if (size == 0) size = yres;
119
                        list->dy -= (value * yres - list->oldy) / size;
120
                        list->oldy -= list->dy * size;
121
                        return;
122
        }
123
}
124
 
125
static void mousedev_event(struct input_handle *handle, unsigned int type, unsigned int code, int value)
126
{
127
        struct mousedev *mousedevs[3] = { handle->private, &mousedev_mix, NULL };
128
        struct mousedev **mousedev = mousedevs;
129
        struct mousedev_list *list;
130
        int index, wake;
131
 
132
        while (*mousedev) {
133
 
134
                wake = 0;
135
 
136
                list_for_each_entry(list, &(*mousedev)->list, node)
137
                        switch (type) {
138
                                case EV_ABS:
139
                                        mousedev_abs_event(handle, list, code, value);
140
                                        break;
141
 
142
                                case EV_REL:
143
                                        switch (code) {
144
                                                case REL_X:     list->dx += value; break;
145
                                                case REL_Y:     list->dy -= value; break;
146
                                                case REL_WHEEL: if (list->mode) list->dz -= value; break;
147
                                        }
148
                                        break;
149
 
150
                                case EV_KEY:
151
                                        switch (code) {
152
                                                case BTN_TOUCH: /* Handle touchpad data */
153
                                                        if (test_bit(BTN_TOOL_FINGER, handle->dev->keybit)) {
154
                                                                list->finger = value;
155
                                                                return;
156
                                                        }
157
                                                case BTN_0:
158
                                                case BTN_FORWARD:
159
                                                case BTN_LEFT:   index = 0; break;
160
                                                case BTN_4:
161
                                                case BTN_EXTRA:  if (list->mode == 2) { index = 4; break; }
162
                                                case BTN_STYLUS:
163
                                                case BTN_1:
164
                                                case BTN_RIGHT:  index = 1; break;
165
                                                case BTN_3:
166
                                                case BTN_BACK:
167
                                                case BTN_SIDE:   if (list->mode == 2) { index = 3; break; }
168
                                                case BTN_2:
169
                                                case BTN_STYLUS2:
170
                                                case BTN_MIDDLE: index = 2; break;     
171
                                                default: return;
172
                                        }
173
                                        switch (value) {
174
                                                case 0: clear_bit(index, &list->buttons); break;
175
                                                case 1: set_bit(index, &list->buttons); break;
176
                                                case 2: return;
177
                                        }
178
                                        break;
179
 
180
                                case EV_SYN:
181
                                        switch (code) {
182
                                                case SYN_REPORT:
183
                                                        list->ready = 1;
184
                                                        kill_fasync(&list->fasync, SIGIO, POLL_IN);
185
                                                        wake = 1;
186
                                                        break;
187
                                        }
188
                        }
189
 
190
                if (wake)
191
                        wake_up_interruptible(&((*mousedev)->wait));
192
 
193
                mousedev++;
194
        }
195
}
196
 
197
static int mousedev_fasync(int fd, struct file *file, int on)
198
{
199
        int retval;
200
        struct mousedev_list *list = file->private_data;
201
        retval = fasync_helper(fd, file, on, &list->fasync);
202
        return retval < 0 ? retval : 0;
203
}
204
 
205
static void mousedev_free(struct mousedev *mousedev)
206
{
207
        devfs_remove("input/mouse%d", mousedev->minor);
208
        mousedev_table[mousedev->minor] = NULL;
209
        kfree(mousedev);
210
}
211
 
212
static int mixdev_release(void)
213
{
214
        struct input_handle *handle;
215
 
216
        list_for_each_entry(handle, &mousedev_handler.h_list, h_node) {
217
                struct mousedev *mousedev = handle->private;
218
 
219
                if (!mousedev->open) {
220
                        if (mousedev->exist)
221
                                input_close_device(&mousedev->handle);
222
                        else
223
                                mousedev_free(mousedev);
224
                }
225
        }
226
 
227
        return 0;
228
}
229
 
230
static int mousedev_release(struct inode * inode, struct file * file)
231
{
232
        struct mousedev_list *list = file->private_data;
233
 
234
        mousedev_fasync(-1, file, 0);
235
 
236
        list_del(&list->node);
237
 
238
        if (!--list->mousedev->open) {
239
                if (list->mousedev->minor == MOUSEDEV_MIX)
240
                        return mixdev_release();
241
 
242
                if (!mousedev_mix.open) {
243
                        if (list->mousedev->exist)
244
                                input_close_device(&list->mousedev->handle);
245
                        else
246
                                mousedev_free(list->mousedev);
247
                }
248
        }
249
 
250
        kfree(list);
251
        return 0;
252
}
253
 
254
static int mousedev_open(struct inode * inode, struct file * file)
255
{
256
        struct mousedev_list *list;
257
        struct input_handle *handle;
258
        struct mousedev *mousedev;
259
        int i;
260
 
261
#ifdef CONFIG_INPUT_MOUSEDEV_PSAUX
262
        if (imajor(inode) == MISC_MAJOR)
263
                i = MOUSEDEV_MIX;
264
        else
265
#endif
266
                i = iminor(inode) - MOUSEDEV_MINOR_BASE;
267
 
268
        if (i >= MOUSEDEV_MINORS || !mousedev_table[i])
269
                return -ENODEV;
270
 
271
        if (!(list = kmalloc(sizeof(struct mousedev_list), GFP_KERNEL)))
272
                return -ENOMEM;
273
        memset(list, 0, sizeof(struct mousedev_list));
274
 
275
        list->mousedev = mousedev_table[i];
276
        list_add_tail(&list->node, &mousedev_table[i]->list);
277
        file->private_data = list;
278
 
279
        if (!list->mousedev->open++) {
280
                if (list->mousedev->minor == MOUSEDEV_MIX) {
281
                        list_for_each_entry(handle, &mousedev_handler.h_list, h_node) {
282
                                mousedev = handle->private;
283
                                if (!mousedev->open && mousedev->exist)
284
                                        input_open_device(handle);
285
                        }
286
                } else
287
                        if (!mousedev_mix.open && list->mousedev->exist)       
288
                                input_open_device(&list->mousedev->handle);
289
        }
290
 
291
        return 0;
292
}
293
 
294
static void mousedev_packet(struct mousedev_list *list, unsigned char off)
295
{
296
        list->ps2[off] = 0x08 | ((list->dx < 0) << 4) | ((list->dy < 0) << 5) | (list->buttons & 0x07);
297
        list->ps2[off + 1] = (list->dx > 127 ? 127 : (list->dx < -127 ? -127 : list->dx));
298
        list->ps2[off + 2] = (list->dy > 127 ? 127 : (list->dy < -127 ? -127 : list->dy));
299
        list->dx -= list->ps2[off + 1];
300
        list->dy -= list->ps2[off + 2];
301
        list->bufsiz = off + 3;
302
 
303
        if (list->mode == 2) {
304
                list->ps2[off + 3] = (list->dz > 7 ? 7 : (list->dz < -7 ? -7 : list->dz));
305
                list->dz -= list->ps2[off + 3];
306
                list->ps2[off + 3] = (list->ps2[off + 3] & 0x0f) | ((list->buttons & 0x18) << 1);
307
                list->bufsiz++;
308
        }
309
 
310
        if (list->mode == 1) {
311
                list->ps2[off + 3] = (list->dz > 127 ? 127 : (list->dz < -127 ? -127 : list->dz));
312
                list->dz -= list->ps2[off + 3];
313
                list->bufsiz++;
314
        }
315
 
316
        if (!list->dx && !list->dy && (!list->mode || !list->dz)) list->ready = 0;
317
        list->buffer = list->bufsiz;
318
}
319
 
320
 
321
static ssize_t mousedev_write(struct file * file, const char * buffer, size_t count, loff_t *ppos)
322
{
323
        struct mousedev_list *list = file->private_data;
324
        unsigned char c;
325
        unsigned int i;
326
 
327
        for (i = 0; i < count; i++) {
328
 
329
                if (get_user(c, buffer + i))
330
                        return -EFAULT;
331
 
332
                if (c == mousedev_imex_seq[list->imexseq]) {
333
                        if (++list->imexseq == MOUSEDEV_SEQ_LEN) {
334
                                list->imexseq = 0;
335
                                list->mode = 2;
336
                        }
337
                } else list->imexseq = 0;
338
 
339
                if (c == mousedev_imps_seq[list->impsseq]) {
340
                        if (++list->impsseq == MOUSEDEV_SEQ_LEN) {
341
                                list->impsseq = 0;
342
                                list->mode = 1;
343
                        }
344
                } else list->impsseq = 0;
345
 
346
                list->ps2[0] = 0xfa;
347
                list->bufsiz = 1;
348
 
349
                switch (c) {
350
 
351
                        case 0xeb: /* Poll */
352
                                mousedev_packet(list, 1);
353
                                break;
354
 
355
                        case 0xf2: /* Get ID */
356
                                switch (list->mode) {
357
                                        case 0: list->ps2[1] = 0; break;
358
                                        case 1: list->ps2[1] = 3; break;
359
                                        case 2: list->ps2[1] = 4; break;
360
                                }
361
                                list->bufsiz = 2;
362
                                break;
363
 
364
                        case 0xe9: /* Get info */
365
                                list->ps2[1] = 0x60; list->ps2[2] = 3; list->ps2[3] = 200;
366
                                list->bufsiz = 4;
367
                                break;
368
 
369
                        case 0xff: /* Reset */
370
                                list->impsseq = 0;
371
                                list->imexseq = 0;
372
                                list->mode = 0;
373
                                list->ps2[0] = 0xaa;
374
                                list->ps2[1] = 0x00;
375
                                list->bufsiz = 2;
376
                                break;
377
                }
378
 
379
                list->buffer = list->bufsiz;
380
        }
381
 
382
        kill_fasync(&list->fasync, SIGIO, POLL_IN);
383
 
384
        wake_up_interruptible(&list->mousedev->wait);
385
 
386
        return count;
387
}
388
 
389
static ssize_t mousedev_read(struct file * file, char * buffer, size_t count, loff_t *ppos)
390
{
391
        struct mousedev_list *list = file->private_data;
392
        int retval = 0;
393
 
394
        if (!list->ready && !list->buffer && (file->f_flags & O_NONBLOCK))
395
                return -EAGAIN;
396
 
397
        retval = wait_event_interruptible(list->mousedev->wait, list->ready || list->buffer);
398
 
399
        if (retval)
400
                return retval;
401
 
402
        if (!list->buffer && list->ready)
403
                mousedev_packet(list, 0);
404
 
405
        if (count > list->buffer)
406
                count = list->buffer;
407
 
408
        list->buffer -= count;
409
 
410
        if (copy_to_user(buffer, list->ps2 + list->bufsiz - list->buffer - count, count))
411
                return -EFAULT;
412
 
413
        return count;  
414
}
415
 
416
/* No kernel lock - fine */
417
static unsigned int mousedev_poll(struct file *file, poll_table *wait)
418
{
419
        struct mousedev_list *list = file->private_data;
420
        poll_wait(file, &list->mousedev->wait, wait);
421
        if (list->ready || list->buffer)
422
                return POLLIN | POLLRDNORM;
423
        return 0;
424
}
425
 
426
struct file_operations mousedev_fops = {
427
        .owner =        THIS_MODULE,
428
        .read =         mousedev_read,
429
        .write =        mousedev_write,
430
        .poll =         mousedev_poll,
431
        .open =         mousedev_open,
432
        .release =      mousedev_release,
433
        .fasync =       mousedev_fasync,
434
};
435
 
436
static struct input_handle *mousedev_connect(struct input_handler *handler, struct input_dev *dev, struct input_device_id *id)
437
{
438
        struct mousedev *mousedev;
439
        int minor = 0;
440
 
441
        for (minor = 0; minor < MOUSEDEV_MINORS && mousedev_table[minor]; minor++);
442
        if (minor == MOUSEDEV_MINORS) {
443
                printk(KERN_ERR "mousedev: no more free mousedev devices\n");
444
                return NULL;
445
        }
446
 
447
        if (!(mousedev = kmalloc(sizeof(struct mousedev), GFP_KERNEL)))
448
                return NULL;
449
        memset(mousedev, 0, sizeof(struct mousedev));
450
 
451
        INIT_LIST_HEAD(&mousedev->list);
452
        init_waitqueue_head(&mousedev->wait);
453
 
454
        mousedev->minor = minor;
455
        mousedev->exist = 1;
456
        mousedev->handle.dev = dev;
457
        mousedev->handle.name = mousedev->name;
458
        mousedev->handle.handler = handler;
459
        mousedev->handle.private = mousedev;
460
        sprintf26(mousedev->name, "mouse%d", minor);
461
 
462
        if (mousedev_mix.open)
463
                input_open_device(&mousedev->handle);
464
 
465
        mousedev_table[minor] = mousedev;
466
 
467
        devfs_mk_cdev(MKDEV(INPUT_MAJOR, MOUSEDEV_MINOR_BASE + minor),
468
                        S_IFCHR|S_IRUGO|S_IWUSR, "input/mouse%d", minor);
469
 
470
        return &mousedev->handle;
471
}
472
 
473
static void mousedev_disconnect(struct input_handle *handle)
474
{
475
        struct mousedev *mousedev = handle->private;
476
 
477
        mousedev->exist = 0;
478
 
479
        if (mousedev->open) {
480
                input_close_device(handle);
481
        } else {
482
                if (mousedev_mix.open)
483
                        input_close_device(handle);
484
                mousedev_free(mousedev);
485
        }
486
}
487
 
488
static struct input_device_id mousedev_ids[] = {
489
        {
490
                .flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_KEYBIT | INPUT_DEVICE_ID_MATCH_RELBIT,
491
                .evbit = { BIT(EV_KEY) | BIT(EV_REL) },
492
                .keybit = { [LONG(BTN_LEFT)] = BIT(BTN_LEFT) },
493
                .relbit = { BIT(REL_X) | BIT(REL_Y) },
494
        },      /* A mouse like device, at least one button, two relative axes */
495
        {
496
                .flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_RELBIT,
497
                .evbit = { BIT(EV_KEY) | BIT(EV_REL) },
498
                .relbit = { BIT(REL_WHEEL) },
499
        },      /* A separate scrollwheel */
500
        {
501
                .flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_KEYBIT | INPUT_DEVICE_ID_MATCH_ABSBIT,
502
                .evbit = { BIT(EV_KEY) | BIT(EV_ABS) },
503
                .keybit = { [LONG(BTN_TOUCH)] = BIT(BTN_TOUCH) },
504
                .absbit = { BIT(ABS_X) | BIT(ABS_Y) },
505
        },      /* A tablet like device, at least touch detection, two absolute axes */
506
        {
507
                .flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_KEYBIT | INPUT_DEVICE_ID_MATCH_ABSBIT,
508
                .evbit = { BIT(EV_KEY) | BIT(EV_ABS) },
509
                .keybit = { [LONG(BTN_TOOL_FINGER)] = BIT(BTN_TOOL_FINGER) },
510
                .absbit = { BIT(ABS_X) | BIT(ABS_Y) | BIT(ABS_PRESSURE) | BIT(ABS_TOOL_WIDTH) },
511
        },      /* A touchpad */
512
 
513
        { },    /* Terminating entry */
514
};
515
 
516
MODULE_DEVICE_TABLE(input, mousedev_ids);
517
 
518
static struct input_handler mousedev_handler = {
519
        .event =        mousedev_event,
520
        .connect =      mousedev_connect,
521
        .disconnect =   mousedev_disconnect,
522
        .fops =         &mousedev_fops,
523
        .minor =        MOUSEDEV_MINOR_BASE,
524
        .name =         "mousedev",
525
        .id_table =     mousedev_ids,
526
};
527
 
528
#ifdef CONFIG_INPUT_MOUSEDEV_PSAUX
529
static struct miscdevice psaux_mouse = {
530
        PSMOUSE_MINOR, "psaux", &mousedev_fops
531
};
532
#endif
533
 
534
/*static*/ int __init mousedev_init(void)
535
{
536
        input_register_handler(&mousedev_handler);
537
 
538
        memset(&mousedev_mix, 0, sizeof(struct mousedev));
539
        INIT_LIST_HEAD(&mousedev_mix.list);
540
        init_waitqueue_head(&mousedev_mix.wait);
541
        mousedev_table[MOUSEDEV_MIX] = &mousedev_mix;
542
        mousedev_mix.exist = 1;
543
        mousedev_mix.minor = MOUSEDEV_MIX;
544
 
545
        devfs_mk_cdev(MKDEV(INPUT_MAJOR, MOUSEDEV_MINOR_BASE + MOUSEDEV_MIX),
546
                        S_IFCHR|S_IRUGO|S_IWUSR, "input/mice");
547
 
548
 
549
#ifdef CONFIG_INPUT_MOUSEDEV_PSAUX
550
        if (!(mousedev_mix.misc = !misc_register(&psaux_mouse)))
551
                printk(KERN_WARNING "mice: could not misc_register the device\n");
552
#endif
553
 
554
        printk(KERN_INFO "mice: PS/2 mouse device common for all mice\n");
555
 
556
        return 0;
557
}
558
 
559
/*static*/ void __exit mousedev_exit(void)
560
{
561
#ifdef CONFIG_INPUT_MOUSEDEV_PSAUX
562
        if (mousedev_mix.misc)
563
                misc_deregister(&psaux_mouse);
564
#endif
565
        devfs_remove("input/mice");
566
        input_unregister_handler(&mousedev_handler);
567
}
568
 
569
module_init(mousedev_init);
570
module_exit(mousedev_exit);
571
 
572
MODULE_PARM(xres, "i");
573
MODULE_PARM_DESC(xres, "Horizontal screen resolution");
574
MODULE_PARM(yres, "i");
575
MODULE_PARM_DESC(yres, "Vertical screen resolution");