823bccfc40
We need to work on cleaning up the relationship between kobjects, ksets and ktypes. The removal of 'struct subsystem' is the first step of this, especially as it is not really needed at all. Thanks to Kay for fixing the bugs in this patch. Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
789 lines
20 KiB
C
789 lines
20 KiB
C
/*
|
|
* Input driver to ExplorerPS/2 device driver module.
|
|
*
|
|
* Copyright (c) 1999-2002 Vojtech Pavlik
|
|
* Copyright (c) 2004 Dmitry Torokhov
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License version 2 as published by
|
|
* the Free Software Foundation.
|
|
*/
|
|
|
|
#define MOUSEDEV_MINOR_BASE 32
|
|
#define MOUSEDEV_MINORS 32
|
|
#define MOUSEDEV_MIX 31
|
|
|
|
#include <linux/slab.h>
|
|
#include <linux/poll.h>
|
|
#include <linux/module.h>
|
|
#include <linux/moduleparam.h>
|
|
#include <linux/init.h>
|
|
#include <linux/input.h>
|
|
#include <linux/smp_lock.h>
|
|
#include <linux/random.h>
|
|
#include <linux/major.h>
|
|
#include <linux/device.h>
|
|
#ifdef CONFIG_INPUT_MOUSEDEV_PSAUX
|
|
#include <linux/miscdevice.h>
|
|
#endif
|
|
|
|
MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
|
|
MODULE_DESCRIPTION("Mouse (ExplorerPS/2) device interfaces");
|
|
MODULE_LICENSE("GPL");
|
|
|
|
#ifndef CONFIG_INPUT_MOUSEDEV_SCREEN_X
|
|
#define CONFIG_INPUT_MOUSEDEV_SCREEN_X 1024
|
|
#endif
|
|
#ifndef CONFIG_INPUT_MOUSEDEV_SCREEN_Y
|
|
#define CONFIG_INPUT_MOUSEDEV_SCREEN_Y 768
|
|
#endif
|
|
|
|
static int xres = CONFIG_INPUT_MOUSEDEV_SCREEN_X;
|
|
module_param(xres, uint, 0644);
|
|
MODULE_PARM_DESC(xres, "Horizontal screen resolution");
|
|
|
|
static int yres = CONFIG_INPUT_MOUSEDEV_SCREEN_Y;
|
|
module_param(yres, uint, 0644);
|
|
MODULE_PARM_DESC(yres, "Vertical screen resolution");
|
|
|
|
static unsigned tap_time = 200;
|
|
module_param(tap_time, uint, 0644);
|
|
MODULE_PARM_DESC(tap_time, "Tap time for touchpads in absolute mode (msecs)");
|
|
|
|
struct mousedev_hw_data {
|
|
int dx, dy, dz;
|
|
int x, y;
|
|
int abs_event;
|
|
unsigned long buttons;
|
|
};
|
|
|
|
struct mousedev {
|
|
int exist;
|
|
int open;
|
|
int minor;
|
|
char name[16];
|
|
wait_queue_head_t wait;
|
|
struct list_head list;
|
|
struct input_handle handle;
|
|
|
|
struct mousedev_hw_data packet;
|
|
unsigned int pkt_count;
|
|
int old_x[4], old_y[4];
|
|
int frac_dx, frac_dy;
|
|
unsigned long touch;
|
|
};
|
|
|
|
enum mousedev_emul {
|
|
MOUSEDEV_EMUL_PS2,
|
|
MOUSEDEV_EMUL_IMPS,
|
|
MOUSEDEV_EMUL_EXPS
|
|
};
|
|
|
|
struct mousedev_motion {
|
|
int dx, dy, dz;
|
|
unsigned long buttons;
|
|
};
|
|
|
|
#define PACKET_QUEUE_LEN 16
|
|
struct mousedev_list {
|
|
struct fasync_struct *fasync;
|
|
struct mousedev *mousedev;
|
|
struct list_head node;
|
|
|
|
struct mousedev_motion packets[PACKET_QUEUE_LEN];
|
|
unsigned int head, tail;
|
|
spinlock_t packet_lock;
|
|
int pos_x, pos_y;
|
|
|
|
signed char ps2[6];
|
|
unsigned char ready, buffer, bufsiz;
|
|
unsigned char imexseq, impsseq;
|
|
enum mousedev_emul mode;
|
|
unsigned long last_buttons;
|
|
};
|
|
|
|
#define MOUSEDEV_SEQ_LEN 6
|
|
|
|
static unsigned char mousedev_imps_seq[] = { 0xf3, 200, 0xf3, 100, 0xf3, 80 };
|
|
static unsigned char mousedev_imex_seq[] = { 0xf3, 200, 0xf3, 200, 0xf3, 80 };
|
|
|
|
static struct input_handler mousedev_handler;
|
|
|
|
static struct mousedev *mousedev_table[MOUSEDEV_MINORS];
|
|
static struct mousedev mousedev_mix;
|
|
|
|
#define fx(i) (mousedev->old_x[(mousedev->pkt_count - (i)) & 03])
|
|
#define fy(i) (mousedev->old_y[(mousedev->pkt_count - (i)) & 03])
|
|
|
|
static void mousedev_touchpad_event(struct input_dev *dev, struct mousedev *mousedev, unsigned int code, int value)
|
|
{
|
|
int size, tmp;
|
|
enum { FRACTION_DENOM = 128 };
|
|
|
|
if (mousedev->touch) {
|
|
size = dev->absmax[ABS_X] - dev->absmin[ABS_X];
|
|
if (size == 0)
|
|
size = 256 * 2;
|
|
|
|
switch (code) {
|
|
case ABS_X:
|
|
fx(0) = value;
|
|
if (mousedev->pkt_count >= 2) {
|
|
tmp = ((value - fx(2)) * (256 * FRACTION_DENOM)) / size;
|
|
tmp += mousedev->frac_dx;
|
|
mousedev->packet.dx = tmp / FRACTION_DENOM;
|
|
mousedev->frac_dx = tmp - mousedev->packet.dx * FRACTION_DENOM;
|
|
}
|
|
break;
|
|
|
|
case ABS_Y:
|
|
fy(0) = value;
|
|
if (mousedev->pkt_count >= 2) {
|
|
tmp = -((value - fy(2)) * (256 * FRACTION_DENOM)) / size;
|
|
tmp += mousedev->frac_dy;
|
|
mousedev->packet.dy = tmp / FRACTION_DENOM;
|
|
mousedev->frac_dy = tmp - mousedev->packet.dy * FRACTION_DENOM;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void mousedev_abs_event(struct input_dev *dev, struct mousedev *mousedev, unsigned int code, int value)
|
|
{
|
|
int size;
|
|
|
|
switch (code) {
|
|
case ABS_X:
|
|
size = dev->absmax[ABS_X] - dev->absmin[ABS_X];
|
|
if (size == 0)
|
|
size = xres ? : 1;
|
|
if (value > dev->absmax[ABS_X])
|
|
value = dev->absmax[ABS_X];
|
|
if (value < dev->absmin[ABS_X])
|
|
value = dev->absmin[ABS_X];
|
|
mousedev->packet.x = ((value - dev->absmin[ABS_X]) * xres) / size;
|
|
mousedev->packet.abs_event = 1;
|
|
break;
|
|
|
|
case ABS_Y:
|
|
size = dev->absmax[ABS_Y] - dev->absmin[ABS_Y];
|
|
if (size == 0)
|
|
size = yres ? : 1;
|
|
if (value > dev->absmax[ABS_Y])
|
|
value = dev->absmax[ABS_Y];
|
|
if (value < dev->absmin[ABS_Y])
|
|
value = dev->absmin[ABS_Y];
|
|
mousedev->packet.y = yres - ((value - dev->absmin[ABS_Y]) * yres) / size;
|
|
mousedev->packet.abs_event = 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void mousedev_rel_event(struct mousedev *mousedev, unsigned int code, int value)
|
|
{
|
|
switch (code) {
|
|
case REL_X: mousedev->packet.dx += value; break;
|
|
case REL_Y: mousedev->packet.dy -= value; break;
|
|
case REL_WHEEL: mousedev->packet.dz -= value; break;
|
|
}
|
|
}
|
|
|
|
static void mousedev_key_event(struct mousedev *mousedev, unsigned int code, int value)
|
|
{
|
|
int index;
|
|
|
|
switch (code) {
|
|
case BTN_TOUCH:
|
|
case BTN_0:
|
|
case BTN_LEFT: index = 0; break;
|
|
case BTN_STYLUS:
|
|
case BTN_1:
|
|
case BTN_RIGHT: index = 1; break;
|
|
case BTN_2:
|
|
case BTN_FORWARD:
|
|
case BTN_STYLUS2:
|
|
case BTN_MIDDLE: index = 2; break;
|
|
case BTN_3:
|
|
case BTN_BACK:
|
|
case BTN_SIDE: index = 3; break;
|
|
case BTN_4:
|
|
case BTN_EXTRA: index = 4; break;
|
|
default: return;
|
|
}
|
|
|
|
if (value) {
|
|
set_bit(index, &mousedev->packet.buttons);
|
|
set_bit(index, &mousedev_mix.packet.buttons);
|
|
} else {
|
|
clear_bit(index, &mousedev->packet.buttons);
|
|
clear_bit(index, &mousedev_mix.packet.buttons);
|
|
}
|
|
}
|
|
|
|
static void mousedev_notify_readers(struct mousedev *mousedev, struct mousedev_hw_data *packet)
|
|
{
|
|
struct mousedev_list *list;
|
|
struct mousedev_motion *p;
|
|
unsigned long flags;
|
|
int wake_readers = 0;
|
|
|
|
list_for_each_entry(list, &mousedev->list, node) {
|
|
spin_lock_irqsave(&list->packet_lock, flags);
|
|
|
|
p = &list->packets[list->head];
|
|
if (list->ready && p->buttons != mousedev->packet.buttons) {
|
|
unsigned int new_head = (list->head + 1) % PACKET_QUEUE_LEN;
|
|
if (new_head != list->tail) {
|
|
p = &list->packets[list->head = new_head];
|
|
memset(p, 0, sizeof(struct mousedev_motion));
|
|
}
|
|
}
|
|
|
|
if (packet->abs_event) {
|
|
p->dx += packet->x - list->pos_x;
|
|
p->dy += packet->y - list->pos_y;
|
|
list->pos_x = packet->x;
|
|
list->pos_y = packet->y;
|
|
}
|
|
|
|
list->pos_x += packet->dx;
|
|
list->pos_x = list->pos_x < 0 ? 0 : (list->pos_x >= xres ? xres : list->pos_x);
|
|
list->pos_y += packet->dy;
|
|
list->pos_y = list->pos_y < 0 ? 0 : (list->pos_y >= yres ? yres : list->pos_y);
|
|
|
|
p->dx += packet->dx;
|
|
p->dy += packet->dy;
|
|
p->dz += packet->dz;
|
|
p->buttons = mousedev->packet.buttons;
|
|
|
|
if (p->dx || p->dy || p->dz || p->buttons != list->last_buttons)
|
|
list->ready = 1;
|
|
|
|
spin_unlock_irqrestore(&list->packet_lock, flags);
|
|
|
|
if (list->ready) {
|
|
kill_fasync(&list->fasync, SIGIO, POLL_IN);
|
|
wake_readers = 1;
|
|
}
|
|
}
|
|
|
|
if (wake_readers)
|
|
wake_up_interruptible(&mousedev->wait);
|
|
}
|
|
|
|
static void mousedev_touchpad_touch(struct mousedev *mousedev, int value)
|
|
{
|
|
if (!value) {
|
|
if (mousedev->touch &&
|
|
time_before(jiffies, mousedev->touch + msecs_to_jiffies(tap_time))) {
|
|
/*
|
|
* Toggle left button to emulate tap.
|
|
* We rely on the fact that mousedev_mix always has 0
|
|
* motion packet so we won't mess current position.
|
|
*/
|
|
set_bit(0, &mousedev->packet.buttons);
|
|
set_bit(0, &mousedev_mix.packet.buttons);
|
|
mousedev_notify_readers(mousedev, &mousedev_mix.packet);
|
|
mousedev_notify_readers(&mousedev_mix, &mousedev_mix.packet);
|
|
clear_bit(0, &mousedev->packet.buttons);
|
|
clear_bit(0, &mousedev_mix.packet.buttons);
|
|
}
|
|
mousedev->touch = mousedev->pkt_count = 0;
|
|
mousedev->frac_dx = 0;
|
|
mousedev->frac_dy = 0;
|
|
|
|
} else if (!mousedev->touch)
|
|
mousedev->touch = jiffies;
|
|
}
|
|
|
|
static void mousedev_event(struct input_handle *handle, unsigned int type, unsigned int code, int value)
|
|
{
|
|
struct mousedev *mousedev = handle->private;
|
|
|
|
switch (type) {
|
|
case EV_ABS:
|
|
/* Ignore joysticks */
|
|
if (test_bit(BTN_TRIGGER, handle->dev->keybit))
|
|
return;
|
|
|
|
if (test_bit(BTN_TOOL_FINGER, handle->dev->keybit))
|
|
mousedev_touchpad_event(handle->dev, mousedev, code, value);
|
|
else
|
|
mousedev_abs_event(handle->dev, mousedev, code, value);
|
|
|
|
break;
|
|
|
|
case EV_REL:
|
|
mousedev_rel_event(mousedev, code, value);
|
|
break;
|
|
|
|
case EV_KEY:
|
|
if (value != 2) {
|
|
if (code == BTN_TOUCH && test_bit(BTN_TOOL_FINGER, handle->dev->keybit))
|
|
mousedev_touchpad_touch(mousedev, value);
|
|
else
|
|
mousedev_key_event(mousedev, code, value);
|
|
}
|
|
break;
|
|
|
|
case EV_SYN:
|
|
if (code == SYN_REPORT) {
|
|
if (mousedev->touch) {
|
|
mousedev->pkt_count++;
|
|
/* Input system eats duplicate events, but we need all of them
|
|
* to do correct averaging so apply present one forward
|
|
*/
|
|
fx(0) = fx(1);
|
|
fy(0) = fy(1);
|
|
}
|
|
|
|
mousedev_notify_readers(mousedev, &mousedev->packet);
|
|
mousedev_notify_readers(&mousedev_mix, &mousedev->packet);
|
|
|
|
mousedev->packet.dx = mousedev->packet.dy = mousedev->packet.dz = 0;
|
|
mousedev->packet.abs_event = 0;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
static int mousedev_fasync(int fd, struct file *file, int on)
|
|
{
|
|
int retval;
|
|
struct mousedev_list *list = file->private_data;
|
|
|
|
retval = fasync_helper(fd, file, on, &list->fasync);
|
|
|
|
return retval < 0 ? retval : 0;
|
|
}
|
|
|
|
static void mousedev_free(struct mousedev *mousedev)
|
|
{
|
|
mousedev_table[mousedev->minor] = NULL;
|
|
kfree(mousedev);
|
|
}
|
|
|
|
static void mixdev_release(void)
|
|
{
|
|
struct input_handle *handle;
|
|
|
|
list_for_each_entry(handle, &mousedev_handler.h_list, h_node) {
|
|
struct mousedev *mousedev = handle->private;
|
|
|
|
if (!mousedev->open) {
|
|
if (mousedev->exist)
|
|
input_close_device(&mousedev->handle);
|
|
else
|
|
mousedev_free(mousedev);
|
|
}
|
|
}
|
|
}
|
|
|
|
static int mousedev_release(struct inode * inode, struct file * file)
|
|
{
|
|
struct mousedev_list *list = file->private_data;
|
|
|
|
mousedev_fasync(-1, file, 0);
|
|
|
|
list_del(&list->node);
|
|
|
|
if (!--list->mousedev->open) {
|
|
if (list->mousedev->minor == MOUSEDEV_MIX)
|
|
mixdev_release();
|
|
else if (!mousedev_mix.open) {
|
|
if (list->mousedev->exist)
|
|
input_close_device(&list->mousedev->handle);
|
|
else
|
|
mousedev_free(list->mousedev);
|
|
}
|
|
}
|
|
|
|
kfree(list);
|
|
return 0;
|
|
}
|
|
|
|
static int mousedev_open(struct inode * inode, struct file * file)
|
|
{
|
|
struct mousedev_list *list;
|
|
struct input_handle *handle;
|
|
struct mousedev *mousedev;
|
|
int i;
|
|
|
|
#ifdef CONFIG_INPUT_MOUSEDEV_PSAUX
|
|
if (imajor(inode) == MISC_MAJOR)
|
|
i = MOUSEDEV_MIX;
|
|
else
|
|
#endif
|
|
i = iminor(inode) - MOUSEDEV_MINOR_BASE;
|
|
|
|
if (i >= MOUSEDEV_MINORS || !mousedev_table[i])
|
|
return -ENODEV;
|
|
|
|
if (!(list = kzalloc(sizeof(struct mousedev_list), GFP_KERNEL)))
|
|
return -ENOMEM;
|
|
|
|
spin_lock_init(&list->packet_lock);
|
|
list->pos_x = xres / 2;
|
|
list->pos_y = yres / 2;
|
|
list->mousedev = mousedev_table[i];
|
|
list_add_tail(&list->node, &mousedev_table[i]->list);
|
|
file->private_data = list;
|
|
|
|
if (!list->mousedev->open++) {
|
|
if (list->mousedev->minor == MOUSEDEV_MIX) {
|
|
list_for_each_entry(handle, &mousedev_handler.h_list, h_node) {
|
|
mousedev = handle->private;
|
|
if (!mousedev->open && mousedev->exist)
|
|
input_open_device(handle);
|
|
}
|
|
} else
|
|
if (!mousedev_mix.open && list->mousedev->exist)
|
|
input_open_device(&list->mousedev->handle);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static inline int mousedev_limit_delta(int delta, int limit)
|
|
{
|
|
return delta > limit ? limit : (delta < -limit ? -limit : delta);
|
|
}
|
|
|
|
static void mousedev_packet(struct mousedev_list *list, signed char *ps2_data)
|
|
{
|
|
struct mousedev_motion *p;
|
|
unsigned long flags;
|
|
|
|
spin_lock_irqsave(&list->packet_lock, flags);
|
|
p = &list->packets[list->tail];
|
|
|
|
ps2_data[0] = 0x08 | ((p->dx < 0) << 4) | ((p->dy < 0) << 5) | (p->buttons & 0x07);
|
|
ps2_data[1] = mousedev_limit_delta(p->dx, 127);
|
|
ps2_data[2] = mousedev_limit_delta(p->dy, 127);
|
|
p->dx -= ps2_data[1];
|
|
p->dy -= ps2_data[2];
|
|
|
|
switch (list->mode) {
|
|
case MOUSEDEV_EMUL_EXPS:
|
|
ps2_data[3] = mousedev_limit_delta(p->dz, 7);
|
|
p->dz -= ps2_data[3];
|
|
ps2_data[3] = (ps2_data[3] & 0x0f) | ((p->buttons & 0x18) << 1);
|
|
list->bufsiz = 4;
|
|
break;
|
|
|
|
case MOUSEDEV_EMUL_IMPS:
|
|
ps2_data[0] |= ((p->buttons & 0x10) >> 3) | ((p->buttons & 0x08) >> 1);
|
|
ps2_data[3] = mousedev_limit_delta(p->dz, 127);
|
|
p->dz -= ps2_data[3];
|
|
list->bufsiz = 4;
|
|
break;
|
|
|
|
case MOUSEDEV_EMUL_PS2:
|
|
default:
|
|
ps2_data[0] |= ((p->buttons & 0x10) >> 3) | ((p->buttons & 0x08) >> 1);
|
|
p->dz = 0;
|
|
list->bufsiz = 3;
|
|
break;
|
|
}
|
|
|
|
if (!p->dx && !p->dy && !p->dz) {
|
|
if (list->tail == list->head) {
|
|
list->ready = 0;
|
|
list->last_buttons = p->buttons;
|
|
} else
|
|
list->tail = (list->tail + 1) % PACKET_QUEUE_LEN;
|
|
}
|
|
|
|
spin_unlock_irqrestore(&list->packet_lock, flags);
|
|
}
|
|
|
|
|
|
static ssize_t mousedev_write(struct file * file, const char __user * buffer, size_t count, loff_t *ppos)
|
|
{
|
|
struct mousedev_list *list = file->private_data;
|
|
unsigned char c;
|
|
unsigned int i;
|
|
|
|
for (i = 0; i < count; i++) {
|
|
|
|
if (get_user(c, buffer + i))
|
|
return -EFAULT;
|
|
|
|
if (c == mousedev_imex_seq[list->imexseq]) {
|
|
if (++list->imexseq == MOUSEDEV_SEQ_LEN) {
|
|
list->imexseq = 0;
|
|
list->mode = MOUSEDEV_EMUL_EXPS;
|
|
}
|
|
} else
|
|
list->imexseq = 0;
|
|
|
|
if (c == mousedev_imps_seq[list->impsseq]) {
|
|
if (++list->impsseq == MOUSEDEV_SEQ_LEN) {
|
|
list->impsseq = 0;
|
|
list->mode = MOUSEDEV_EMUL_IMPS;
|
|
}
|
|
} else
|
|
list->impsseq = 0;
|
|
|
|
list->ps2[0] = 0xfa;
|
|
|
|
switch (c) {
|
|
|
|
case 0xeb: /* Poll */
|
|
mousedev_packet(list, &list->ps2[1]);
|
|
list->bufsiz++; /* account for leading ACK */
|
|
break;
|
|
|
|
case 0xf2: /* Get ID */
|
|
switch (list->mode) {
|
|
case MOUSEDEV_EMUL_PS2: list->ps2[1] = 0; break;
|
|
case MOUSEDEV_EMUL_IMPS: list->ps2[1] = 3; break;
|
|
case MOUSEDEV_EMUL_EXPS: list->ps2[1] = 4; break;
|
|
}
|
|
list->bufsiz = 2;
|
|
break;
|
|
|
|
case 0xe9: /* Get info */
|
|
list->ps2[1] = 0x60; list->ps2[2] = 3; list->ps2[3] = 200;
|
|
list->bufsiz = 4;
|
|
break;
|
|
|
|
case 0xff: /* Reset */
|
|
list->impsseq = list->imexseq = 0;
|
|
list->mode = MOUSEDEV_EMUL_PS2;
|
|
list->ps2[1] = 0xaa; list->ps2[2] = 0x00;
|
|
list->bufsiz = 3;
|
|
break;
|
|
|
|
default:
|
|
list->bufsiz = 1;
|
|
break;
|
|
}
|
|
|
|
list->buffer = list->bufsiz;
|
|
}
|
|
|
|
kill_fasync(&list->fasync, SIGIO, POLL_IN);
|
|
|
|
wake_up_interruptible(&list->mousedev->wait);
|
|
|
|
return count;
|
|
}
|
|
|
|
static ssize_t mousedev_read(struct file * file, char __user * buffer, size_t count, loff_t *ppos)
|
|
{
|
|
struct mousedev_list *list = file->private_data;
|
|
int retval = 0;
|
|
|
|
if (!list->ready && !list->buffer && (file->f_flags & O_NONBLOCK))
|
|
return -EAGAIN;
|
|
|
|
retval = wait_event_interruptible(list->mousedev->wait,
|
|
!list->mousedev->exist || list->ready || list->buffer);
|
|
|
|
if (retval)
|
|
return retval;
|
|
|
|
if (!list->mousedev->exist)
|
|
return -ENODEV;
|
|
|
|
if (!list->buffer && list->ready) {
|
|
mousedev_packet(list, list->ps2);
|
|
list->buffer = list->bufsiz;
|
|
}
|
|
|
|
if (count > list->buffer)
|
|
count = list->buffer;
|
|
|
|
list->buffer -= count;
|
|
|
|
if (copy_to_user(buffer, list->ps2 + list->bufsiz - list->buffer - count, count))
|
|
return -EFAULT;
|
|
|
|
return count;
|
|
}
|
|
|
|
/* No kernel lock - fine */
|
|
static unsigned int mousedev_poll(struct file *file, poll_table *wait)
|
|
{
|
|
struct mousedev_list *list = file->private_data;
|
|
|
|
poll_wait(file, &list->mousedev->wait, wait);
|
|
return ((list->ready || list->buffer) ? (POLLIN | POLLRDNORM) : 0) |
|
|
(list->mousedev->exist ? 0 : (POLLHUP | POLLERR));
|
|
}
|
|
|
|
static const struct file_operations mousedev_fops = {
|
|
.owner = THIS_MODULE,
|
|
.read = mousedev_read,
|
|
.write = mousedev_write,
|
|
.poll = mousedev_poll,
|
|
.open = mousedev_open,
|
|
.release = mousedev_release,
|
|
.fasync = mousedev_fasync,
|
|
};
|
|
|
|
static struct input_handle *mousedev_connect(struct input_handler *handler, struct input_dev *dev,
|
|
const struct input_device_id *id)
|
|
{
|
|
struct mousedev *mousedev;
|
|
struct class_device *cdev;
|
|
int minor = 0;
|
|
|
|
for (minor = 0; minor < MOUSEDEV_MINORS && mousedev_table[minor]; minor++);
|
|
if (minor == MOUSEDEV_MINORS) {
|
|
printk(KERN_ERR "mousedev: no more free mousedev devices\n");
|
|
return NULL;
|
|
}
|
|
|
|
if (!(mousedev = kzalloc(sizeof(struct mousedev), GFP_KERNEL)))
|
|
return NULL;
|
|
|
|
INIT_LIST_HEAD(&mousedev->list);
|
|
init_waitqueue_head(&mousedev->wait);
|
|
|
|
mousedev->minor = minor;
|
|
mousedev->exist = 1;
|
|
mousedev->handle.dev = dev;
|
|
mousedev->handle.name = mousedev->name;
|
|
mousedev->handle.handler = handler;
|
|
mousedev->handle.private = mousedev;
|
|
sprintf(mousedev->name, "mouse%d", minor);
|
|
|
|
if (mousedev_mix.open)
|
|
input_open_device(&mousedev->handle);
|
|
|
|
mousedev_table[minor] = mousedev;
|
|
|
|
cdev = class_device_create(&input_class, &dev->cdev,
|
|
MKDEV(INPUT_MAJOR, MOUSEDEV_MINOR_BASE + minor),
|
|
dev->cdev.dev, mousedev->name);
|
|
|
|
/* temporary symlink to keep userspace happy */
|
|
sysfs_create_link(&input_class.subsys.kobj, &cdev->kobj,
|
|
mousedev->name);
|
|
|
|
return &mousedev->handle;
|
|
}
|
|
|
|
static void mousedev_disconnect(struct input_handle *handle)
|
|
{
|
|
struct mousedev *mousedev = handle->private;
|
|
struct mousedev_list *list;
|
|
|
|
sysfs_remove_link(&input_class.subsys.kobj, mousedev->name);
|
|
class_device_destroy(&input_class,
|
|
MKDEV(INPUT_MAJOR, MOUSEDEV_MINOR_BASE + mousedev->minor));
|
|
mousedev->exist = 0;
|
|
|
|
if (mousedev->open) {
|
|
input_close_device(handle);
|
|
wake_up_interruptible(&mousedev->wait);
|
|
list_for_each_entry(list, &mousedev->list, node)
|
|
kill_fasync(&list->fasync, SIGIO, POLL_HUP);
|
|
} else {
|
|
if (mousedev_mix.open)
|
|
input_close_device(handle);
|
|
mousedev_free(mousedev);
|
|
}
|
|
}
|
|
|
|
static const struct input_device_id mousedev_ids[] = {
|
|
{
|
|
.flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_KEYBIT | INPUT_DEVICE_ID_MATCH_RELBIT,
|
|
.evbit = { BIT(EV_KEY) | BIT(EV_REL) },
|
|
.keybit = { [LONG(BTN_LEFT)] = BIT(BTN_LEFT) },
|
|
.relbit = { BIT(REL_X) | BIT(REL_Y) },
|
|
}, /* A mouse like device, at least one button, two relative axes */
|
|
{
|
|
.flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_RELBIT,
|
|
.evbit = { BIT(EV_KEY) | BIT(EV_REL) },
|
|
.relbit = { BIT(REL_WHEEL) },
|
|
}, /* A separate scrollwheel */
|
|
{
|
|
.flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_KEYBIT | INPUT_DEVICE_ID_MATCH_ABSBIT,
|
|
.evbit = { BIT(EV_KEY) | BIT(EV_ABS) },
|
|
.keybit = { [LONG(BTN_TOUCH)] = BIT(BTN_TOUCH) },
|
|
.absbit = { BIT(ABS_X) | BIT(ABS_Y) },
|
|
}, /* A tablet like device, at least touch detection, two absolute axes */
|
|
{
|
|
.flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_KEYBIT | INPUT_DEVICE_ID_MATCH_ABSBIT,
|
|
.evbit = { BIT(EV_KEY) | BIT(EV_ABS) },
|
|
.keybit = { [LONG(BTN_TOOL_FINGER)] = BIT(BTN_TOOL_FINGER) },
|
|
.absbit = { BIT(ABS_X) | BIT(ABS_Y) | BIT(ABS_PRESSURE) | BIT(ABS_TOOL_WIDTH) },
|
|
}, /* A touchpad */
|
|
|
|
{ }, /* Terminating entry */
|
|
};
|
|
|
|
MODULE_DEVICE_TABLE(input, mousedev_ids);
|
|
|
|
static struct input_handler mousedev_handler = {
|
|
.event = mousedev_event,
|
|
.connect = mousedev_connect,
|
|
.disconnect = mousedev_disconnect,
|
|
.fops = &mousedev_fops,
|
|
.minor = MOUSEDEV_MINOR_BASE,
|
|
.name = "mousedev",
|
|
.id_table = mousedev_ids,
|
|
};
|
|
|
|
#ifdef CONFIG_INPUT_MOUSEDEV_PSAUX
|
|
static struct miscdevice psaux_mouse = {
|
|
PSMOUSE_MINOR, "psaux", &mousedev_fops
|
|
};
|
|
static int psaux_registered;
|
|
#endif
|
|
|
|
static int __init mousedev_init(void)
|
|
{
|
|
struct class_device *cdev;
|
|
int error;
|
|
|
|
error = input_register_handler(&mousedev_handler);
|
|
if (error)
|
|
return error;
|
|
|
|
memset(&mousedev_mix, 0, sizeof(struct mousedev));
|
|
INIT_LIST_HEAD(&mousedev_mix.list);
|
|
init_waitqueue_head(&mousedev_mix.wait);
|
|
mousedev_table[MOUSEDEV_MIX] = &mousedev_mix;
|
|
mousedev_mix.exist = 1;
|
|
mousedev_mix.minor = MOUSEDEV_MIX;
|
|
|
|
cdev = class_device_create(&input_class, NULL,
|
|
MKDEV(INPUT_MAJOR, MOUSEDEV_MINOR_BASE + MOUSEDEV_MIX), NULL, "mice");
|
|
if (IS_ERR(cdev)) {
|
|
input_unregister_handler(&mousedev_handler);
|
|
return PTR_ERR(cdev);
|
|
}
|
|
|
|
#ifdef CONFIG_INPUT_MOUSEDEV_PSAUX
|
|
error = misc_register(&psaux_mouse);
|
|
if (error)
|
|
printk(KERN_WARNING "mice: could not register psaux device, "
|
|
"error: %d\n", error);
|
|
else
|
|
psaux_registered = 1;
|
|
#endif
|
|
|
|
printk(KERN_INFO "mice: PS/2 mouse device common for all mice\n");
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void __exit mousedev_exit(void)
|
|
{
|
|
#ifdef CONFIG_INPUT_MOUSEDEV_PSAUX
|
|
if (psaux_registered)
|
|
misc_deregister(&psaux_mouse);
|
|
#endif
|
|
class_device_destroy(&input_class,
|
|
MKDEV(INPUT_MAJOR, MOUSEDEV_MINOR_BASE + MOUSEDEV_MIX));
|
|
input_unregister_handler(&mousedev_handler);
|
|
}
|
|
|
|
module_init(mousedev_init);
|
|
module_exit(mousedev_exit);
|