summaryrefslogtreecommitdiffstats
path: root/drivers/input
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>2000-08-28 22:00:09 +0000
committerRalf Baechle <ralf@linux-mips.org>2000-08-28 22:00:09 +0000
commit1a1d77dd589de5a567fa95e36aa6999c704ceca4 (patch)
tree141e31f89f18b9fe0831f31852e0435ceaccafc5 /drivers/input
parentfb9c690a18b3d66925a65b17441c37fa14d4370b (diff)
Merge with 2.4.0-test7.
Diffstat (limited to 'drivers/input')
-rw-r--r--drivers/input/Config.in20
-rw-r--r--drivers/input/Makefile51
-rw-r--r--drivers/input/evdev.c356
-rw-r--r--drivers/input/input.c426
-rw-r--r--drivers/input/joydev.c497
-rw-r--r--drivers/input/keybdev.c200
-rw-r--r--drivers/input/mousedev.c489
7 files changed, 2039 insertions, 0 deletions
diff --git a/drivers/input/Config.in b/drivers/input/Config.in
new file mode 100644
index 000000000..966ae308a
--- /dev/null
+++ b/drivers/input/Config.in
@@ -0,0 +1,20 @@
+#
+# Input core configuration
+#
+
+mainmenu_option next_comment
+comment 'Input core support'
+
+tristate 'Input core support' CONFIG_INPUT
+if [ "$CONFIG_INPUT" != "n" ]; then
+ dep_tristate ' Keyboard support' CONFIG_INPUT_KEYBDEV $CONFIG_INPUT
+ dep_tristate ' Mouse support' CONFIG_INPUT_MOUSEDEV $CONFIG_INPUT
+ if [ "$CONFIG_INPUT_MOUSEDEV" != "n" ]; then
+ int ' Horizontal screen resolution' CONFIG_INPUT_MOUSEDEV_SCREEN_X 1024
+ int ' Vertical screen resolution' CONFIG_INPUT_MOUSEDEV_SCREEN_Y 768
+ fi
+ dep_tristate ' Joystick support' CONFIG_INPUT_JOYDEV $CONFIG_INPUT
+ dep_tristate ' Event interface support' CONFIG_INPUT_EVDEV $CONFIG_INPUT
+fi
+
+endmenu
diff --git a/drivers/input/Makefile b/drivers/input/Makefile
new file mode 100644
index 000000000..af6305cec
--- /dev/null
+++ b/drivers/input/Makefile
@@ -0,0 +1,51 @@
+#
+# Makefile for the input core drivers.
+#
+
+# Subdirs.
+
+SUB_DIRS :=
+MOD_SUB_DIRS := $(SUB_DIRS)
+MOD_IN_SUB_DIRS := $(SUB_DIRS)
+ALL_SUB_DIRS := $(SUB_DIRS)
+
+# The target object and module list name.
+
+O_TARGET := inputdrv.o
+M_OBJS :=
+O_OBJS :=
+
+# Objects that export symbols.
+
+export-objs := input.o
+
+# Object file lists.
+
+obj-y :=
+obj-m :=
+obj-n :=
+obj- :=
+
+# Each configuration option enables a list of files.
+
+obj-$(CONFIG_INPUT) += input.o
+obj-$(CONFIG_INPUT_KEYBDEV) += keybdev.o
+obj-$(CONFIG_INPUT_MOUSEDEV) += mousedev.o
+obj-$(CONFIG_INPUT_JOYDEV) += joydev.o
+obj-$(CONFIG_INPUT_EVDEV) += evdev.o
+
+# Files that are both resident and modular: remove from modular.
+
+obj-m := $(filter-out $(obj-y), $(obj-m))
+int-m := $(filter-out $(int-y), $(int-m))
+
+# Translate to Rules.make lists.
+
+O_OBJS := $(sort $(filter-out $(export-objs), $(obj-y)))
+OX_OBJS := $(sort $(filter $(export-objs), $(obj-y)))
+M_OBJS := $(sort $(filter-out $(export-objs), $(obj-m)))
+MX_OBJS := $(sort $(filter $(export-objs), $(obj-m)))
+
+# The global Rules.make.
+
+include $(TOPDIR)/Rules.make
diff --git a/drivers/input/evdev.c b/drivers/input/evdev.c
new file mode 100644
index 000000000..65109e9ca
--- /dev/null
+++ b/drivers/input/evdev.c
@@ -0,0 +1,356 @@
+/*
+ * $Id: evdev.c,v 1.10 2000/06/23 09:23:00 vojtech Exp $
+ *
+ * Copyright (c) 1999-2000 Vojtech Pavlik
+ *
+ * Event char devices, giving access to raw input device events.
+ *
+ * Sponsored by SuSE
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Should you need to contact me, the author, you can do so either by
+ * e-mail - mail your message to <vojtech@suse.cz>, or by paper mail:
+ * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic
+ */
+
+#define EVDEV_MINOR_BASE 64
+#define EVDEV_MINORS 32
+#define EVDEV_BUFFER_SIZE 64
+
+#include <linux/poll.h>
+#include <linux/malloc.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/smp_lock.h>
+
+struct evdev {
+ int exist;
+ int open;
+ int minor;
+ struct input_handle handle;
+ wait_queue_head_t wait;
+ devfs_handle_t devfs;
+ struct evdev_list *list;
+};
+
+struct evdev_list {
+ struct input_event buffer[EVDEV_BUFFER_SIZE];
+ int head;
+ int tail;
+ struct fasync_struct *fasync;
+ struct evdev *evdev;
+ struct evdev_list *next;
+};
+
+static struct evdev *evdev_table[EVDEV_MINORS] = { NULL, /* ... */ };
+
+static void evdev_event(struct input_handle *handle, unsigned int type, unsigned int code, int value)
+{
+ struct evdev *evdev = handle->private;
+ struct evdev_list *list = evdev->list;
+
+ while (list) {
+
+ get_fast_time(&list->buffer[list->head].time);
+ list->buffer[list->head].type = type;
+ list->buffer[list->head].code = code;
+ list->buffer[list->head].value = value;
+ list->head = (list->head + 1) & (EVDEV_BUFFER_SIZE - 1);
+
+ kill_fasync(&list->fasync, SIGIO, POLL_IN);
+
+ list = list->next;
+ }
+
+ wake_up_interruptible(&evdev->wait);
+}
+
+static int evdev_fasync(int fd, struct file *file, int on)
+{
+ int retval;
+ struct evdev_list *list = file->private_data;
+ retval = fasync_helper(fd, file, on, &list->fasync);
+ return retval < 0 ? retval : 0;
+}
+
+static int evdev_release(struct inode * inode, struct file * file)
+{
+ struct evdev_list *list = file->private_data;
+ struct evdev_list **listptr;
+
+ lock_kernel();
+ listptr = &list->evdev->list;
+ evdev_fasync(-1, file, 0);
+
+ while (*listptr && (*listptr != list))
+ listptr = &((*listptr)->next);
+ *listptr = (*listptr)->next;
+
+ if (!--list->evdev->open) {
+ if (list->evdev->exist) {
+ input_close_device(&list->evdev->handle);
+ } else {
+ input_unregister_minor(list->evdev->devfs);
+ evdev_table[list->evdev->minor] = NULL;
+ kfree(list->evdev);
+ }
+ }
+
+ kfree(list);
+ unlock_kernel();
+
+ return 0;
+}
+
+static int evdev_open(struct inode * inode, struct file * file)
+{
+ struct evdev_list *list;
+ int i = MINOR(inode->i_rdev) - EVDEV_MINOR_BASE;
+
+ if (i > EVDEV_MINORS || !evdev_table[i])
+ return -ENODEV;
+
+ if (!(list = kmalloc(sizeof(struct evdev_list), GFP_KERNEL)))
+ return -ENOMEM;
+ memset(list, 0, sizeof(struct evdev_list));
+
+ list->evdev = evdev_table[i];
+ list->next = evdev_table[i]->list;
+ evdev_table[i]->list = list;
+
+ file->private_data = list;
+
+ if (!list->evdev->open++)
+ if (list->evdev->exist)
+ input_open_device(&list->evdev->handle);
+
+ return 0;
+}
+
+static ssize_t evdev_write(struct file * file, const char * buffer, size_t count, loff_t *ppos)
+{
+ return -EINVAL;
+}
+
+static ssize_t evdev_read(struct file * file, char * buffer, size_t count, loff_t *ppos)
+{
+ DECLARE_WAITQUEUE(wait, current);
+ struct evdev_list *list = file->private_data;
+ int retval = 0;
+
+ if (list->head == list->tail) {
+
+ add_wait_queue(&list->evdev->wait, &wait);
+ current->state = TASK_INTERRUPTIBLE;
+
+ while (list->head == list->tail) {
+
+ if (file->f_flags & O_NONBLOCK) {
+ retval = -EAGAIN;
+ break;
+ }
+ if (signal_pending(current)) {
+ retval = -ERESTARTSYS;
+ break;
+ }
+
+ schedule();
+ }
+
+ current->state = TASK_RUNNING;
+ remove_wait_queue(&list->evdev->wait, &wait);
+ }
+
+ if (retval)
+ return retval;
+
+ while (list->head != list->tail && retval + sizeof(struct input_event) <= count) {
+ if (copy_to_user(buffer + retval, list->buffer + list->tail,
+ sizeof(struct input_event))) return -EFAULT;
+ list->tail = (list->tail + 1) & (EVDEV_BUFFER_SIZE - 1);
+ retval += sizeof(struct input_event);
+ }
+
+ return retval;
+}
+
+/* No kernel lock - fine */
+static unsigned int evdev_poll(struct file *file, poll_table *wait)
+{
+ struct evdev_list *list = file->private_data;
+ poll_wait(file, &list->evdev->wait, wait);
+ if (list->head != list->tail)
+ return POLLIN | POLLRDNORM;
+ return 0;
+}
+
+static int evdev_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
+{
+ struct evdev_list *list = file->private_data;
+ struct evdev *evdev = list->evdev;
+ struct input_dev *dev = evdev->handle.dev;
+ int retval;
+
+ switch (cmd) {
+
+ case EVIOCGVERSION:
+ return put_user(EV_VERSION, (int *) arg);
+
+ case EVIOCGID:
+ if ((retval = put_user(dev->idbus, ((short *) arg) + 0))) return retval;
+ if ((retval = put_user(dev->idvendor, ((short *) arg) + 1))) return retval;
+ if ((retval = put_user(dev->idproduct, ((short *) arg) + 2))) return retval;
+ if ((retval = put_user(dev->idversion, ((short *) arg) + 3))) return retval;
+ return 0;
+
+ default:
+
+ if (_IOC_TYPE(cmd) != 'E' || _IOC_DIR(cmd) != _IOC_READ)
+ return -EINVAL;
+
+ if ((_IOC_NR(cmd) & ~EV_MAX) == _IOC_NR(EVIOCGBIT(0,0))) {
+
+ long *bits;
+ int len;
+
+ switch (_IOC_NR(cmd) & EV_MAX) {
+ case 0: bits = dev->evbit; len = EV_MAX; break;
+ case EV_KEY: bits = dev->keybit; len = KEY_MAX; break;
+ case EV_REL: bits = dev->relbit; len = REL_MAX; break;
+ case EV_ABS: bits = dev->absbit; len = ABS_MAX; break;
+ case EV_LED: bits = dev->ledbit; len = LED_MAX; break;
+ case EV_SND: bits = dev->sndbit; len = SND_MAX; break;
+ default: return -EINVAL;
+ }
+ len = NBITS(len) * sizeof(long);
+ if (len > _IOC_SIZE(cmd)) {
+ printk(KERN_WARNING "evdev.c: Truncating bitfield length from %d to %d\n",
+ len, _IOC_SIZE(cmd));
+ len = _IOC_SIZE(cmd);
+ }
+ return copy_to_user((char *) arg, bits, len) ? -EFAULT : len;
+ }
+
+ if (_IOC_NR(cmd) == _IOC_NR(EVIOCGNAME(0))) {
+ int len;
+ if (!dev->name) return 0;
+ len = strlen(dev->name) + 1;
+ if (len > _IOC_SIZE(cmd)) len = _IOC_SIZE(cmd);
+ return copy_to_user((char *) arg, dev->name, len) ? -EFAULT : len;
+ }
+
+ if ((_IOC_NR(cmd) & ~ABS_MAX) == _IOC_NR(EVIOCGABS(0))) {
+
+ int t = _IOC_NR(cmd) & ABS_MAX;
+
+ if ((retval = put_user(dev->abs[t], ((int *) arg) + 0))) return retval;
+ if ((retval = put_user(dev->absmin[t], ((int *) arg) + 1))) return retval;
+ if ((retval = put_user(dev->absmax[t], ((int *) arg) + 2))) return retval;
+ if ((retval = put_user(dev->absfuzz[t], ((int *) arg) + 3))) return retval;
+ if ((retval = put_user(dev->absflat[t], ((int *) arg) + 4))) return retval;
+
+ return 0;
+ }
+ }
+ return -EINVAL;
+}
+
+static struct file_operations evdev_fops = {
+ owner: THIS_MODULE,
+ read: evdev_read,
+ write: evdev_write,
+ poll: evdev_poll,
+ open: evdev_open,
+ release: evdev_release,
+ ioctl: evdev_ioctl,
+ fasync: evdev_fasync,
+};
+
+static struct input_handle *evdev_connect(struct input_handler *handler, struct input_dev *dev)
+{
+ struct evdev *evdev;
+ int minor;
+
+ for (minor = 0; minor < EVDEV_MINORS && evdev_table[minor]; minor++);
+ if (evdev_table[minor]) {
+ printk(KERN_ERR "evdev: no more free evdev devices\n");
+ return NULL;
+ }
+
+ if (!(evdev = kmalloc(sizeof(struct evdev), GFP_KERNEL)))
+ return NULL;
+ memset(evdev, 0, sizeof(struct evdev));
+
+ init_waitqueue_head(&evdev->wait);
+
+ evdev->minor = minor;
+ evdev_table[minor] = evdev;
+
+ evdev->handle.dev = dev;
+ evdev->handle.handler = handler;
+ evdev->handle.private = evdev;
+
+ evdev->exist = 1;
+
+ evdev->devfs = input_register_minor("event%d", minor, EVDEV_MINOR_BASE);
+
+ printk(KERN_INFO "event%d: Event device for input%d\n", minor, dev->number);
+
+ return &evdev->handle;
+}
+
+static void evdev_disconnect(struct input_handle *handle)
+{
+ struct evdev *evdev = handle->private;
+
+ evdev->exist = 0;
+
+ if (evdev->open) {
+ input_close_device(handle);
+ } else {
+ input_unregister_minor(evdev->devfs);
+ evdev_table[evdev->minor] = NULL;
+ kfree(evdev);
+ }
+}
+
+static struct input_handler evdev_handler = {
+ event: evdev_event,
+ connect: evdev_connect,
+ disconnect: evdev_disconnect,
+ fops: &evdev_fops,
+ minor: EVDEV_MINOR_BASE,
+};
+
+static int __init evdev_init(void)
+{
+ input_register_handler(&evdev_handler);
+ return 0;
+}
+
+static void __exit evdev_exit(void)
+{
+ input_unregister_handler(&evdev_handler);
+}
+
+module_init(evdev_init);
+module_exit(evdev_exit);
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@suse.cz>");
+MODULE_DESCRIPTION("Event character device driver");
diff --git a/drivers/input/input.c b/drivers/input/input.c
new file mode 100644
index 000000000..f3701965e
--- /dev/null
+++ b/drivers/input/input.c
@@ -0,0 +1,426 @@
+/*
+ * $Id: input.c,v 1.7 2000/05/28 17:31:36 vojtech Exp $
+ *
+ * Copyright (c) 1999-2000 Vojtech Pavlik
+ *
+ * The input layer module itself
+ *
+ * Sponsored by SuSE
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Should you need to contact me, the author, you can do so either by
+ * e-mail - mail your message to <vojtech@suse.cz>, or by paper mail:
+ * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic
+ */
+
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/module.h>
+#include <linux/random.h>
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@suse.cz>");
+MODULE_DESCRIPTION("Input layer module");
+
+EXPORT_SYMBOL(input_register_device);
+EXPORT_SYMBOL(input_unregister_device);
+EXPORT_SYMBOL(input_register_handler);
+EXPORT_SYMBOL(input_unregister_handler);
+EXPORT_SYMBOL(input_register_minor);
+EXPORT_SYMBOL(input_unregister_minor);
+EXPORT_SYMBOL(input_open_device);
+EXPORT_SYMBOL(input_close_device);
+EXPORT_SYMBOL(input_event);
+
+#define INPUT_MAJOR 13
+#define INPUT_DEVICES 256
+
+static struct input_dev *input_dev = NULL;
+static struct input_handler *input_handler = NULL;
+static struct input_handler *input_table[8] = { NULL, /* ... */ };
+static devfs_handle_t input_devfs_handle = NULL;
+static int input_number = 0;
+static long input_devices[NBITS(INPUT_DEVICES)] = { 0, /* ... */ };
+
+void input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
+{
+ struct input_handle *handle = dev->handle;
+
+/*
+ * Filter non-events, and bad input values out.
+ */
+
+ if (type > EV_MAX || !test_bit(type, dev->evbit))
+ return;
+
+ switch (type) {
+
+ case EV_KEY:
+
+ if (code > KEY_MAX || !test_bit(code, dev->keybit) || !!test_bit(code, dev->key) == value)
+ return;
+
+ if (value == 2) break;
+
+ change_bit(code, dev->key);
+
+ if (test_bit(EV_REP, dev->evbit) && dev->timer.function) {
+ if (value) {
+ mod_timer(&dev->timer, jiffies + dev->rep[REP_DELAY]);
+ dev->repeat_key = code;
+ break;
+ }
+ if (dev->repeat_key == code)
+ del_timer(&dev->timer);
+ }
+
+ break;
+
+ case EV_ABS:
+
+ if (code > ABS_MAX || !test_bit(code, dev->absbit))
+ return;
+
+ if (dev->absfuzz[code]) {
+ if ((value > dev->abs[code] - (dev->absfuzz[code] >> 1)) &&
+ (value < dev->abs[code] + (dev->absfuzz[code] >> 1)))
+ return;
+
+ if ((value > dev->abs[code] - dev->absfuzz[code]) &&
+ (value < dev->abs[code] + dev->absfuzz[code]))
+ value = (dev->abs[code] * 3 + value) >> 2;
+
+ if ((value > dev->abs[code] - (dev->absfuzz[code] << 1)) &&
+ (value < dev->abs[code] + (dev->absfuzz[code] << 1)))
+ value = (dev->abs[code] + value) >> 1;
+ }
+
+ if (dev->abs[code] == value)
+ return;
+
+ dev->abs[code] = value;
+ break;
+
+ case EV_REL:
+
+ if (code > REL_MAX || !test_bit(code, dev->relbit) || (value == 0))
+ return;
+
+ break;
+
+ case EV_LED:
+
+ if (code > LED_MAX || !test_bit(code, dev->ledbit) || !!test_bit(code, dev->led) == value)
+ return;
+
+ change_bit(code, dev->led);
+ if (dev->event) dev->event(dev, type, code, value);
+
+ break;
+
+ case EV_SND:
+
+ if (code > SND_MAX || !test_bit(code, dev->sndbit) || !!test_bit(code, dev->snd) == value)
+ return;
+
+ change_bit(code, dev->snd);
+ if (dev->event) dev->event(dev, type, code, value);
+
+ break;
+
+ case EV_REP:
+
+ if (code > REP_MAX || dev->rep[code] == value) return;
+
+ dev->rep[code] = value;
+ if (dev->event) dev->event(dev, type, code, value);
+
+ break;
+ }
+/*
+ * Add randomness.
+ */
+
+#if 0 /* BUG */
+ add_input_randomness(((unsigned long) dev) ^ (type << 24) ^ (code << 16) ^ value);
+#endif
+
+/*
+ * Distribute the event to handler modules.
+ */
+
+ while (handle) {
+ if (handle->open)
+ handle->handler->event(handle, type, code, value);
+ handle = handle->dnext;
+ }
+}
+
+static void input_repeat_key(unsigned long data)
+{
+ struct input_dev *dev = (void *) data;
+ input_event(dev, EV_KEY, dev->repeat_key, 2);
+ mod_timer(&dev->timer, jiffies + dev->rep[REP_PERIOD]);
+}
+
+int input_open_device(struct input_handle *handle)
+{
+ handle->open++;
+ if (handle->dev->open)
+ return handle->dev->open(handle->dev);
+ return 0;
+}
+
+void input_close_device(struct input_handle *handle)
+{
+ if (handle->dev->close)
+ handle->dev->close(handle->dev);
+ handle->open--;
+}
+
+static void input_link_handle(struct input_handle *handle)
+{
+ handle->dnext = handle->dev->handle;
+ handle->hnext = handle->handler->handle;
+ handle->dev->handle = handle;
+ handle->handler->handle = handle;
+}
+
+static void input_unlink_handle(struct input_handle *handle)
+{
+ struct input_handle **handleptr;
+
+ handleptr = &handle->dev->handle;
+ while (*handleptr && (*handleptr != handle))
+ handleptr = &((*handleptr)->dnext);
+ *handleptr = (*handleptr)->dnext;
+
+ handleptr = &handle->handler->handle;
+ while (*handleptr && (*handleptr != handle))
+ handleptr = &((*handleptr)->hnext);
+ *handleptr = (*handleptr)->hnext;
+}
+
+void input_register_device(struct input_dev *dev)
+{
+ struct input_handler *handler = input_handler;
+ struct input_handle *handle;
+
+/*
+ * Initialize repeat timer to default values.
+ */
+
+ init_timer(&dev->timer);
+ dev->timer.data = (long) dev;
+ dev->timer.function = input_repeat_key;
+ dev->rep[REP_DELAY] = HZ/4;
+ dev->rep[REP_PERIOD] = HZ/33;
+
+/*
+ * Add the device.
+ */
+
+ if (input_number >= INPUT_DEVICES) {
+ printk(KERN_WARNING "input: ran out of input device numbers!\n");
+ dev->number = input_number;
+ } else {
+ dev->number = find_first_zero_bit(input_devices, INPUT_DEVICES);
+ set_bit(dev->number, input_devices);
+ }
+
+ dev->next = input_dev;
+ input_dev = dev;
+ input_number++;
+
+/*
+ * Notify handlers.
+ */
+
+ while (handler) {
+ if ((handle = handler->connect(handler, dev)))
+ input_link_handle(handle);
+ handler = handler->next;
+ }
+}
+
+void input_unregister_device(struct input_dev *dev)
+{
+ struct input_handle *handle = dev->handle;
+ struct input_dev **devptr = &input_dev;
+ struct input_handle *dnext;
+
+/*
+ * Kill any pending repeat timers.
+ */
+
+ del_timer(&dev->timer);
+
+/*
+ * Notify handlers.
+ */
+
+ while (handle) {
+ dnext = handle->dnext;
+ input_unlink_handle(handle);
+ handle->handler->disconnect(handle);
+ handle = dnext;
+ }
+
+/*
+ * Remove the device.
+ */
+
+ while (*devptr && (*devptr != dev))
+ devptr = &((*devptr)->next);
+ *devptr = (*devptr)->next;
+
+ input_number--;
+
+ if (dev->number < INPUT_DEVICES)
+ clear_bit(dev->number, input_devices);
+}
+
+void input_register_handler(struct input_handler *handler)
+{
+ struct input_dev *dev = input_dev;
+ struct input_handle *handle;
+
+/*
+ * Add minors if needed.
+ */
+
+ if (handler->fops != NULL)
+ input_table[handler->minor >> 5] = handler;
+
+/*
+ * Add the handler.
+ */
+
+ handler->next = input_handler;
+ input_handler = handler;
+
+/*
+ * Notify it about all existing devices.
+ */
+
+ while (dev) {
+ if ((handle = handler->connect(handler, dev)))
+ input_link_handle(handle);
+ dev = dev->next;
+ }
+}
+
+void input_unregister_handler(struct input_handler *handler)
+{
+ struct input_handler **handlerptr = &input_handler;
+ struct input_handle *handle = handler->handle;
+ struct input_handle *hnext;
+
+/*
+ * Tell the handler to disconnect from all devices it keeps open.
+ */
+
+ while (handle) {
+ hnext = handle->hnext;
+ input_unlink_handle(handle);
+ handler->disconnect(handle);
+ handle = hnext;
+ }
+
+/*
+ * Remove it.
+ */
+
+ while (*handlerptr && (*handlerptr != handler))
+ handlerptr = &((*handlerptr)->next);
+
+ *handlerptr = (*handlerptr)->next;
+
+/*
+ * Remove minors.
+ */
+
+ if (handler->fops != NULL)
+ input_table[handler->minor >> 5] = NULL;
+}
+
+static int input_open_file(struct inode *inode, struct file *file)
+{
+ struct input_handler *handler = input_table[MINOR(inode->i_rdev) >> 5];
+ struct file_operations *old_fops, *new_fops = NULL;
+ int err;
+
+ /* No load-on-demand here? */
+ if (!handler || !(new_fops = fops_get(handler->fops)))
+ return -ENODEV;
+
+ /*
+ * That's _really_ odd. Usually NULL ->open means "nothing special",
+ * not "no device". Oh, well...
+ */
+ if (!new_fops->open) {
+ fops_put(new_fops);
+ return -ENODEV;
+ }
+ old_fops = file->f_op;
+ file->f_op = new_fops;
+ err = new_fops->open(inode, file);
+ if (err) {
+ fops_put(file->f_op);
+ file->f_op = fops_get(old_fops);
+ }
+ fops_put(old_fops);
+ return err;
+}
+
+static struct file_operations input_fops = {
+ owner: THIS_MODULE,
+ open: input_open_file,
+};
+
+devfs_handle_t input_register_minor(char *name, int minor, int minor_base)
+{
+ char devfs_name[16];
+ sprintf(devfs_name, name, minor);
+ return devfs_register(input_devfs_handle, devfs_name, DEVFS_FL_DEFAULT, INPUT_MAJOR, minor + minor_base,
+ S_IFCHR | S_IRUGO | S_IWUSR, &input_fops, NULL);
+}
+
+void input_unregister_minor(devfs_handle_t handle)
+{
+ devfs_unregister(handle);
+}
+
+static int __init input_init(void)
+{
+ if (devfs_register_chrdev(INPUT_MAJOR, "input", &input_fops)) {
+ printk(KERN_ERR "input: unable to register char major %d", INPUT_MAJOR);
+ return -EBUSY;
+ }
+ input_devfs_handle = devfs_mk_dir(NULL, "input", NULL);
+ return 0;
+}
+
+static void __exit input_exit(void)
+{
+ devfs_unregister(input_devfs_handle);
+ if (devfs_unregister_chrdev(INPUT_MAJOR, "input"))
+ printk(KERN_ERR "input: can't unregister char major %d", INPUT_MAJOR);
+}
+
+module_init(input_init);
+module_exit(input_exit);
diff --git a/drivers/input/joydev.c b/drivers/input/joydev.c
new file mode 100644
index 000000000..90c4dbcef
--- /dev/null
+++ b/drivers/input/joydev.c
@@ -0,0 +1,497 @@
+/*
+ * $Id: joydev.c,v 1.13 2000/08/14 21:05:26 vojtech Exp $
+ *
+ * Copyright (c) 1999-2000 Vojtech Pavlik
+ * Copyright (c) 1999 Colin Van Dyke
+ *
+ * Joystick device driver for the input driver suite.
+ *
+ * Sponsored by SuSE and Intel
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Should you need to contact me, the author, you can do so either by
+ * e-mail - mail your message to <vojtech@suse.cz>, or by paper mail:
+ * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic
+ */
+
+#include <asm/io.h>
+#include <asm/system.h>
+#include <asm/segment.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/joystick.h>
+#include <linux/input.h>
+#include <linux/kernel.h>
+#include <linux/major.h>
+#include <linux/malloc.h>
+#include <linux/mm.h>
+#include <linux/miscdevice.h>
+#include <linux/module.h>
+#include <linux/poll.h>
+#include <linux/init.h>
+#include <linux/smp_lock.h>
+
+#define JOYDEV_MINOR_BASE 0
+#define JOYDEV_MINORS 32
+#define JOYDEV_BUFFER_SIZE 64
+
+struct joydev {
+ int exist;
+ int open;
+ int minor;
+ struct input_handle handle;
+ wait_queue_head_t wait;
+ devfs_handle_t devfs;
+ struct joydev *next;
+ struct joydev_list *list;
+ struct js_corr corr[ABS_MAX];
+ struct JS_DATA_SAVE_TYPE glue;
+ int nabs;
+ int nkey;
+ __u16 keymap[KEY_MAX - BTN_MISC];
+ __u16 keypam[KEY_MAX - BTN_MISC];
+ __u8 absmap[ABS_MAX];
+ __u8 abspam[ABS_MAX];
+ __s16 abs[ABS_MAX];
+};
+
+struct joydev_list {
+ struct js_event buffer[JOYDEV_BUFFER_SIZE];
+ int head;
+ int tail;
+ int startup;
+ struct fasync_struct *fasync;
+ struct joydev *joydev;
+ struct joydev_list *next;
+};
+
+static struct joydev *joydev_table[JOYDEV_MINORS];
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@suse.cz>");
+MODULE_DESCRIPTION("Joystick device driver");
+MODULE_SUPPORTED_DEVICE("input/js");
+
+static int joydev_correct(int value, struct js_corr *corr)
+{
+ switch (corr->type) {
+ case JS_CORR_NONE:
+ break;
+ case JS_CORR_BROKEN:
+ value = value > corr->coef[0] ? (value < corr->coef[1] ? 0 :
+ ((corr->coef[3] * (value - corr->coef[1])) >> 14)) :
+ ((corr->coef[2] * (value - corr->coef[0])) >> 14);
+ break;
+ default:
+ return 0;
+ }
+
+ if (value < -32767) return -32767;
+ if (value > 32767) return 32767;
+
+ return value;
+}
+
+static void joydev_event(struct input_handle *handle, unsigned int type, unsigned int code, int value)
+{
+ struct joydev *joydev = handle->private;
+ struct joydev_list *list = joydev->list;
+ struct js_event event;
+
+ switch (type) {
+
+ case EV_KEY:
+ if (code < BTN_MISC || value == 2) return;
+ event.type = JS_EVENT_BUTTON;
+ event.number = joydev->keymap[code - BTN_MISC];
+ event.value = value;
+ break;
+
+ case EV_ABS:
+ event.type = JS_EVENT_AXIS;
+ event.number = joydev->absmap[code];
+ event.value = joydev_correct(value, joydev->corr + event.number);
+ if (event.value == joydev->abs[event.number]) return;
+ joydev->abs[event.number] = event.value;
+ break;
+
+ default:
+ return;
+ }
+
+ event.time = jiffies * (1000 / HZ);
+
+ while (list) {
+
+ memcpy(list->buffer + list->head, &event, sizeof(struct js_event));
+
+ if (list->startup == joydev->nabs + joydev->nkey)
+ if (list->tail == (list->head = (list->head + 1) & (JOYDEV_BUFFER_SIZE - 1)))
+ list->startup = 0;
+
+ kill_fasync(&list->fasync, SIGIO, POLL_IN);
+
+ list = list->next;
+ }
+
+ wake_up_interruptible(&joydev->wait);
+}
+
+static int joydev_fasync(int fd, struct file *file, int on)
+{
+ int retval;
+ struct joydev_list *list = file->private_data;
+ retval = fasync_helper(fd, file, on, &list->fasync);
+ return retval < 0 ? retval : 0;
+}
+
+static int joydev_release(struct inode * inode, struct file * file)
+{
+ struct joydev_list *list = file->private_data;
+ struct joydev_list **listptr;
+
+ lock_kernel();
+ listptr = &list->joydev->list;
+ joydev_fasync(-1, file, 0);
+
+ while (*listptr && (*listptr != list))
+ listptr = &((*listptr)->next);
+ *listptr = (*listptr)->next;
+
+ if (!--list->joydev->open) {
+ if (list->joydev->exist) {
+ input_close_device(&list->joydev->handle);
+ } else {
+ input_unregister_minor(list->joydev->devfs);
+ joydev_table[list->joydev->minor] = NULL;
+ kfree(list->joydev);
+ }
+ }
+
+ kfree(list);
+ unlock_kernel();
+
+ return 0;
+}
+
+static int joydev_open(struct inode *inode, struct file *file)
+{
+ struct joydev_list *list;
+ int i = MINOR(inode->i_rdev) - JOYDEV_MINOR_BASE;
+
+ if (i > JOYDEV_MINORS || !joydev_table[i])
+ return -ENODEV;
+
+ if (!(list = kmalloc(sizeof(struct joydev_list), GFP_KERNEL)))
+ return -ENOMEM;
+ memset(list, 0, sizeof(struct joydev_list));
+
+ list->joydev = joydev_table[i];
+ list->next = joydev_table[i]->list;
+ joydev_table[i]->list = list;
+
+ file->private_data = list;
+
+ if (!list->joydev->open++)
+ if (list->joydev->exist)
+ input_open_device(&list->joydev->handle);
+
+ return 0;
+}
+
+static ssize_t joydev_write(struct file * file, const char * buffer, size_t count, loff_t *ppos)
+{
+ return -EINVAL;
+}
+
+static ssize_t joydev_read(struct file *file, char *buf, size_t count, loff_t *ppos)
+{
+ DECLARE_WAITQUEUE(wait, current);
+ struct joydev_list *list = file->private_data;
+ struct joydev *joydev = list->joydev;
+ struct input_dev *input = joydev->handle.dev;
+ int retval = 0;
+
+ if (count < sizeof(struct js_event))
+ return -EINVAL;
+
+ if (count == sizeof(struct JS_DATA_TYPE)) {
+
+ struct JS_DATA_TYPE data;
+
+ data.buttons = ((joydev->nkey > 0 && test_bit(joydev->keypam[0], input->key)) ? 1 : 0) |
+ ((joydev->nkey > 1 && test_bit(joydev->keypam[1], input->key)) ? 2 : 0);
+ data.x = (joydev->abs[0] / 256 + 128) >> joydev->glue.JS_CORR.x;
+ data.y = (joydev->abs[1] / 256 + 128) >> joydev->glue.JS_CORR.y;
+
+ if (copy_to_user(buf, &data, sizeof(struct JS_DATA_TYPE)))
+ return -EFAULT;
+
+ list->startup = 0;
+ list->tail = list->head;
+
+ return sizeof(struct JS_DATA_TYPE);
+ }
+
+ if (list->head == list->tail && list->startup == joydev->nabs + joydev->nkey) {
+
+ add_wait_queue(&list->joydev->wait, &wait);
+ current->state = TASK_INTERRUPTIBLE;
+
+ while (list->head == list->tail) {
+
+ if (file->f_flags & O_NONBLOCK) {
+ retval = -EAGAIN;
+ break;
+ }
+ if (signal_pending(current)) {
+ retval = -ERESTARTSYS;
+ break;
+ }
+
+ schedule();
+ }
+
+ current->state = TASK_RUNNING;
+ remove_wait_queue(&list->joydev->wait, &wait);
+ }
+
+ if (retval)
+ return retval;
+
+ while (list->startup < joydev->nabs + joydev->nkey && retval + sizeof(struct js_event) <= count) {
+
+ struct js_event event;
+
+ event.time = jiffies * (1000/HZ);
+
+ if (list->startup < joydev->nkey) {
+ event.type = JS_EVENT_BUTTON | JS_EVENT_INIT;
+ event.number = list->startup;
+ event.value = !!test_bit(joydev->keypam[event.number], input->key);
+ } else {
+ event.type = JS_EVENT_AXIS | JS_EVENT_INIT;
+ event.number = list->startup - joydev->nkey;
+ event.value = joydev->abs[event.number];
+ }
+
+ if (copy_to_user(buf + retval, &event, sizeof(struct js_event)))
+ return -EFAULT;
+
+ list->startup++;
+ retval += sizeof(struct js_event);
+ }
+
+ while (list->head != list->tail && retval + sizeof(struct js_event) <= count) {
+
+ if (copy_to_user(buf + retval, list->buffer + list->tail, sizeof(struct js_event)))
+ return -EFAULT;
+
+ list->tail = (list->tail + 1) & (JOYDEV_BUFFER_SIZE - 1);
+ retval += sizeof(struct js_event);
+ }
+
+ return retval;
+}
+
+/* No kernel lock - fine */
+static unsigned int joydev_poll(struct file *file, poll_table *wait)
+{
+ struct joydev_list *list = file->private_data;
+ poll_wait(file, &list->joydev->wait, wait);
+ if (list->head != list->tail || list->startup < list->joydev->nabs + list->joydev->nkey)
+ return POLLIN | POLLRDNORM;
+ return 0;
+}
+
+static int joydev_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
+{
+ struct joydev_list *list = file->private_data;
+ struct joydev *joydev = list->joydev;
+ struct input_dev *dev = joydev->handle.dev;
+
+
+ switch (cmd) {
+
+ case JS_SET_CAL:
+ return copy_from_user(&joydev->glue.JS_CORR, (struct JS_DATA_TYPE *) arg,
+ sizeof(struct JS_DATA_TYPE)) ? -EFAULT : 0;
+ case JS_GET_CAL:
+ return copy_to_user((struct JS_DATA_TYPE *) arg, &joydev->glue.JS_CORR,
+ sizeof(struct JS_DATA_TYPE)) ? -EFAULT : 0;
+ case JS_SET_TIMEOUT:
+ return get_user(joydev->glue.JS_TIMEOUT, (int *) arg);
+ case JS_GET_TIMEOUT:
+ return put_user(joydev->glue.JS_TIMEOUT, (int *) arg);
+ case JS_SET_TIMELIMIT:
+ return get_user(joydev->glue.JS_TIMELIMIT, (long *) arg);
+ case JS_GET_TIMELIMIT:
+ return put_user(joydev->glue.JS_TIMELIMIT, (long *) arg);
+ case JS_SET_ALL:
+ return copy_from_user(&joydev->glue, (struct JS_DATA_SAVE_TYPE *) arg,
+ sizeof(struct JS_DATA_SAVE_TYPE)) ? -EFAULT : 0;
+ case JS_GET_ALL:
+ return copy_to_user((struct JS_DATA_SAVE_TYPE *) arg, &joydev->glue,
+ sizeof(struct JS_DATA_SAVE_TYPE)) ? -EFAULT : 0;
+
+ case JSIOCGVERSION:
+ return put_user(JS_VERSION, (__u32 *) arg);
+ case JSIOCGAXES:
+ return put_user(joydev->nabs, (__u8 *) arg);
+ case JSIOCGBUTTONS:
+ return put_user(joydev->nkey, (__u8 *) arg);
+ case JSIOCSCORR:
+ return copy_from_user(joydev->corr, (struct js_corr *) arg,
+ sizeof(struct js_corr) * joydev->nabs) ? -EFAULT : 0;
+ case JSIOCGCORR:
+ return copy_to_user((struct js_corr *) arg, joydev->corr,
+ sizeof(struct js_corr) * joydev->nabs) ? -EFAULT : 0;
+ default:
+ if ((cmd & ~(_IOC_SIZEMASK << _IOC_SIZESHIFT)) == JSIOCGNAME(0)) {
+ int len;
+ if (!dev->name) return 0;
+ len = strlen(dev->name) + 1;
+ if (len > _IOC_SIZE(cmd)) len = _IOC_SIZE(cmd);
+ if (copy_to_user((char *) arg, dev->name, len)) return -EFAULT;
+ return len;
+ }
+ }
+ return -EINVAL;
+}
+
+static struct file_operations joydev_fops = {
+ owner: THIS_MODULE,
+ read: joydev_read,
+ write: joydev_write,
+ poll: joydev_poll,
+ open: joydev_open,
+ release: joydev_release,
+ ioctl: joydev_ioctl,
+ fasync: joydev_fasync,
+};
+
+static struct input_handle *joydev_connect(struct input_handler *handler, struct input_dev *dev)
+{
+ struct joydev *joydev;
+ int i, j, minor;
+
+ if (!(test_bit(EV_KEY, dev->evbit) && test_bit(EV_ABS, dev->evbit) &&
+ (test_bit(ABS_X, dev->absbit) || test_bit(ABS_Y, dev->absbit)) &&
+ (test_bit(BTN_TRIGGER, dev->keybit) || test_bit(BTN_A, dev->keybit)
+ || test_bit(BTN_1, dev->keybit)))) return NULL;
+
+ for (minor = 0; minor < JOYDEV_MINORS && joydev_table[minor]; minor++);
+ if (joydev_table[minor]) {
+ printk(KERN_ERR "joydev: no more free joydev devices\n");
+ return NULL;
+ }
+
+ if (!(joydev = kmalloc(sizeof(struct joydev), GFP_KERNEL)))
+ return NULL;
+ memset(joydev, 0, sizeof(struct joydev));
+
+ init_waitqueue_head(&joydev->wait);
+
+ joydev->minor = minor;
+ joydev_table[minor] = joydev;
+
+ joydev->handle.dev = dev;
+ joydev->handle.handler = handler;
+ joydev->handle.private = joydev;
+
+ joydev->exist = 1;
+
+ for (i = 0; i < ABS_MAX; i++)
+ if (test_bit(i, dev->absbit)) {
+ joydev->absmap[i] = joydev->nabs;
+ joydev->abspam[joydev->nabs] = i;
+ joydev->nabs++;
+ }
+
+ for (i = BTN_JOYSTICK - BTN_MISC; i < KEY_MAX - BTN_MISC; i++)
+ if (test_bit(i + BTN_MISC, dev->keybit)) {
+ joydev->keymap[i] = joydev->nkey;
+ joydev->keypam[joydev->nkey] = i + BTN_MISC;
+ joydev->nkey++;
+ }
+
+ for (i = 0; i < BTN_JOYSTICK - BTN_MISC; i++)
+ if (test_bit(i + BTN_MISC, dev->keybit)) {
+ joydev->keymap[i] = joydev->nkey;
+ joydev->keypam[joydev->nkey] = i + BTN_MISC;
+ joydev->nkey++;
+ }
+
+ for (i = 0; i < joydev->nabs; i++) {
+ j = joydev->abspam[i];
+ if (dev->absmax[j] == dev->absmin[j]) {
+ joydev->corr[i].type = JS_CORR_NONE;
+ continue;
+ }
+ joydev->corr[i].type = JS_CORR_BROKEN;
+ joydev->corr[i].prec = dev->absfuzz[j];
+ joydev->corr[i].coef[0] = (dev->absmax[j] + dev->absmin[j]) / 2 - dev->absflat[j];
+ joydev->corr[i].coef[1] = (dev->absmax[j] + dev->absmin[j]) / 2 + dev->absflat[j];
+ joydev->corr[i].coef[2] = (1 << 29) / ((dev->absmax[j] - dev->absmin[j]) / 2 - 2 * dev->absflat[j]);
+ joydev->corr[i].coef[3] = (1 << 29) / ((dev->absmax[j] - dev->absmin[j]) / 2 - 2 * dev->absflat[j]);
+
+ joydev->abs[i] = joydev_correct(dev->abs[j], joydev->corr + i);
+ }
+
+ joydev->devfs = input_register_minor("js%d", minor, JOYDEV_MINOR_BASE);
+
+ printk(KERN_INFO "js%d: Joystick device for input%d\n", minor, dev->number);
+
+ return &joydev->handle;
+}
+
+static void joydev_disconnect(struct input_handle *handle)
+{
+ struct joydev *joydev = handle->private;
+
+ joydev->exist = 0;
+
+ if (joydev->open) {
+ input_close_device(handle);
+ } else {
+ input_unregister_minor(joydev->devfs);
+ joydev_table[joydev->minor] = NULL;
+ kfree(joydev);
+ }
+}
+
+static struct input_handler joydev_handler = {
+ event: joydev_event,
+ connect: joydev_connect,
+ disconnect: joydev_disconnect,
+ fops: &joydev_fops,
+ minor: JOYDEV_MINOR_BASE,
+};
+
+static int __init joydev_init(void)
+{
+ input_register_handler(&joydev_handler);
+ return 0;
+}
+
+static void __exit joydev_exit(void)
+{
+ input_unregister_handler(&joydev_handler);
+}
+
+module_init(joydev_init);
+module_exit(joydev_exit);
diff --git a/drivers/input/keybdev.c b/drivers/input/keybdev.c
new file mode 100644
index 000000000..95fce5e80
--- /dev/null
+++ b/drivers/input/keybdev.c
@@ -0,0 +1,200 @@
+/*
+ * $Id: keybdev.c,v 1.3 2000/05/28 17:31:36 vojtech Exp $
+ *
+ * Copyright (c) 1999-2000 Vojtech Pavlik
+ *
+ * Input driver to keyboard driver binding.
+ *
+ * Sponsored by SuSE
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Should you need to contact me, the author, you can do so either by
+ * e-mail - mail your message to <vojtech@suse.cz>, or by paper mail:
+ * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic
+ */
+
+#include <linux/config.h>
+#include <linux/kbd_ll.h>
+#include <linux/input.h>
+#include <linux/malloc.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/kbd_kern.h>
+
+#if defined(CONFIG_X86) || defined(CONFIG_IA64) || defined(__alpha__) || defined(__mips__)
+
+static int x86_sysrq_alt = 0;
+
+static unsigned short x86_keycodes[256] =
+ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
+ 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
+ 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
+ 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
+ 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79,
+ 80, 81, 82, 83, 43, 85, 86, 87, 88,115,119,120,121,375,123, 90,
+ 284,285,309,298,312, 91,327,328,329,331,333,335,336,337,338,339,
+ 367,294,293,286,350, 92,334,512,116,377,109,111,373,347,348,349,
+ 360, 93, 94, 95, 98,376,100,101,357,316,354,304,289,102,351,355,
+ 103,104,105,275,281,272,306,106,274,107,288,364,358,363,362,361,
+ 291,108,381,290,287,292,279,305,280, 99,112,257,258,113,270,114,
+ 118,117,125,374,379,259,260,261,262,263,264,265,266,267,268,269,
+ 271,273,276,277,278,282,283,295,296,297,299,300,301,302,303,307,
+ 308,310,313,314,315,317,318,319,320,321,322,323,324,325,326,330,
+ 332,340,341,342,343,344,345,346,356,359,365,368,369,370,371,372 };
+
+static int emulate_raw(unsigned int keycode, int down)
+{
+ if (keycode > 255 || !x86_keycodes[keycode])
+ return -1;
+
+ if (keycode == KEY_PAUSE) {
+ handle_scancode(0xe1, 1);
+ handle_scancode(0x1d, down);
+ handle_scancode(0x45, down);
+ return 0;
+ }
+
+ if (keycode == KEY_SYSRQ && x86_sysrq_alt) {
+ handle_scancode(0x54, down);
+ return 0;
+ }
+
+ if (x86_keycodes[keycode] & 0x100)
+ handle_scancode(0xe0, 1);
+
+ handle_scancode(x86_keycodes[keycode] & 0x7f, down);
+
+ if (keycode == KEY_SYSRQ) {
+ handle_scancode(0xe0, 1);
+ handle_scancode(0x37, down);
+ }
+
+ if (keycode == KEY_LEFTALT || keycode == KEY_RIGHTALT)
+ x86_sysrq_alt = down;
+
+ return 0;
+}
+
+#elif defined(CONFIG_ADB_KEYBOARD)
+
+static unsigned char mac_keycodes[128] =
+ { 0, 53, 18, 19, 20, 21, 23, 22, 26, 28, 25, 29, 27, 24, 51, 48,
+ 12, 13, 14, 15, 17, 16, 32, 34, 31, 35, 33, 30, 36, 54,128, 1,
+ 2, 3, 5, 4, 38, 40, 37, 41, 39, 50, 56, 42, 6, 7, 8, 9,
+ 11, 45, 46, 43, 47, 44,123, 67, 58, 49, 57,122,120, 99,118, 96,
+ 97, 98,100,101,109, 71,107, 89, 91, 92, 78, 86, 87, 88, 69, 83,
+ 84, 85, 82, 65, 42, 0, 10,103,111, 0, 0, 0, 0, 0, 0, 0,
+ 76,125, 75,105,124, 0,115, 62,116, 59, 60,119, 61,121,114,117,
+ 0, 0, 0, 0,127, 81, 0,113, 0, 0, 0, 0, 0, 55, 55 };
+
+static int emulate_raw(unsigned int keycode, int down)
+{
+ if (keycode > 127 || !mac_keycodes[keycode])
+ return -1;
+
+ handle_scancode(mac_keycodes[keycode] & 0x7f, down);
+
+ return 0;
+}
+
+#endif
+
+static struct input_handler keybdev_handler;
+
+void keybdev_ledfunc(unsigned int led)
+{
+ struct input_handle *handle;
+
+ for (handle = keybdev_handler.handle; handle; handle = handle->hnext) {
+
+ input_event(handle->dev, EV_LED, LED_SCROLLL, !!(led & 0x01));
+ input_event(handle->dev, EV_LED, LED_NUML, !!(led & 0x02));
+ input_event(handle->dev, EV_LED, LED_CAPSL, !!(led & 0x04));
+
+ }
+}
+
+void keybdev_event(struct input_handle *handle, unsigned int type, unsigned int code, int down)
+{
+ if (type != EV_KEY) return;
+
+ if (emulate_raw(code, down))
+ printk(KERN_WARNING "keyboard.c: can't emulate rawmode for keycode %d\n", code);
+
+ tasklet_schedule(&keyboard_tasklet);
+}
+
+static struct input_handle *keybdev_connect(struct input_handler *handler, struct input_dev *dev)
+{
+ struct input_handle *handle;
+ int i;
+
+ if (!test_bit(EV_KEY, dev->evbit))
+ return NULL;
+
+ for (i = KEY_RESERVED; i < BTN_MISC; i++)
+ if (test_bit(i, dev->keybit)) break;
+
+ if (i == BTN_MISC)
+ return NULL;
+
+ if (!(handle = kmalloc(sizeof(struct input_handle), GFP_KERNEL)))
+ return NULL;
+ memset(handle, 0, sizeof(struct input_handle));
+
+ handle->dev = dev;
+ handle->handler = handler;
+
+ input_open_device(handle);
+
+ printk(KERN_INFO "keybdev.c: Adding keyboard: input%d\n", dev->number);
+
+ return handle;
+}
+
+static void keybdev_disconnect(struct input_handle *handle)
+{
+ printk(KERN_INFO "keybdev.c: Removing keyboard: input%d\n", handle->dev->number);
+ input_close_device(handle);
+ kfree(handle);
+}
+
+static struct input_handler keybdev_handler = {
+ event: keybdev_event,
+ connect: keybdev_connect,
+ disconnect: keybdev_disconnect,
+};
+
+static int __init keybdev_init(void)
+{
+ input_register_handler(&keybdev_handler);
+ kbd_ledfunc = keybdev_ledfunc;
+ return 0;
+}
+
+static void __exit keybdev_exit(void)
+{
+ kbd_ledfunc = NULL;
+ input_unregister_handler(&keybdev_handler);
+}
+
+module_init(keybdev_init);
+module_exit(keybdev_exit);
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@suse.cz>");
+MODULE_DESCRIPTION("Input driver to keyboard driver binding");
diff --git a/drivers/input/mousedev.c b/drivers/input/mousedev.c
new file mode 100644
index 000000000..1aa85e227
--- /dev/null
+++ b/drivers/input/mousedev.c
@@ -0,0 +1,489 @@
+/*
+ * $Id: mousedev.c,v 1.15 2000/08/14 21:05:26 vojtech Exp $
+ *
+ * Copyright (c) 1999-2000 Vojtech Pavlik
+ *
+ * Input driver to PS/2 or ImPS/2 device driver module.
+ *
+ * Sponsored by SuSE
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Should you need to contact me, the author, you can do so either by
+ * e-mail - mail your message to <vojtech@suse.cz>, or by paper mail:
+ * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic
+ */
+
+#define MOUSEDEV_MINOR_BASE 32
+#define MOUSEDEV_MINORS 32
+#define MOUSEDEV_MIX 31
+
+#include <linux/malloc.h>
+#include <linux/poll.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/config.h>
+#include <linux/smp_lock.h>
+
+#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
+
+struct mousedev {
+ int exist;
+ int open;
+ int minor;
+ wait_queue_head_t wait;
+ struct mousedev_list *list;
+ struct input_handle handle;
+ devfs_handle_t devfs;
+};
+
+struct mousedev_list {
+ struct fasync_struct *fasync;
+ struct mousedev *mousedev;
+ struct mousedev_list *next;
+ int dx, dy, dz, oldx, oldy;
+ signed char ps2[6];
+ unsigned long buttons;
+ unsigned char ready, buffer, bufsiz;
+ unsigned char mode, genseq, impseq;
+};
+
+#define MOUSEDEV_GENIUS_LEN 5
+#define MOUSEDEV_IMPS_LEN 6
+
+static unsigned char mousedev_genius_seq[] = { 0xe8, 3, 0xe6, 0xe6, 0xe6 };
+static unsigned char mousedev_imps_seq[] = { 0xf3, 200, 0xf3, 100, 0xf3, 80 };
+
+static struct input_handler mousedev_handler;
+
+static struct mousedev *mousedev_table[MOUSEDEV_MINORS];
+static struct mousedev mousedev_mix;
+
+static void mousedev_event(struct input_handle *handle, unsigned int type, unsigned int code, int value)
+{
+ struct mousedev *mousedevs[3] = { handle->private, &mousedev_mix, NULL };
+ struct mousedev **mousedev = mousedevs;
+ struct mousedev_list *list;
+ int index, size;
+
+ while (*mousedev) {
+ list = (*mousedev)->list;
+ while (list) {
+ switch (type) {
+ case EV_ABS:
+ if (test_bit(BTN_TRIGGER, handle->dev->keybit))
+ break;
+ switch (code) {
+ case ABS_X:
+ size = handle->dev->absmax[ABS_X] - handle->dev->absmin[ABS_X];
+ list->dx += (value * CONFIG_INPUT_MOUSEDEV_SCREEN_X - list->oldx) / size;
+ list->oldx += list->dx * size;
+ break;
+ case ABS_Y:
+ size = handle->dev->absmax[ABS_Y] - handle->dev->absmin[ABS_Y];
+ list->dy -= (value * CONFIG_INPUT_MOUSEDEV_SCREEN_Y - list->oldy) / size;
+ list->oldy -= list->dy * size;
+ break;
+ }
+ break;
+
+ case EV_REL:
+ switch (code) {
+ case REL_X: list->dx += value; break;
+ case REL_Y: list->dy -= value; break;
+ case REL_WHEEL: if (list->mode) list->dz -= value; break;
+ }
+ break;
+
+ case EV_KEY:
+ switch (code) {
+ case BTN_0:
+ case BTN_TOUCH:
+ case BTN_LEFT: index = 0; break;
+ case BTN_4:
+ case BTN_EXTRA: if (list->mode > 1) { index = 4; break; }
+ case BTN_STYLUS:
+ case BTN_1:
+ case BTN_RIGHT: index = 1; break;
+ case BTN_3:
+ case BTN_SIDE: if (list->mode > 1) { index = 3; break; }
+ case BTN_2:
+ case BTN_STYLUS2:
+ case BTN_MIDDLE: index = 2; break;
+ default: return;
+ }
+ switch (value) {
+ case 0: clear_bit(index, &list->buttons); break;
+ case 1: set_bit(index, &list->buttons); break;
+ case 2: return;
+ }
+ break;
+ }
+
+ list->ready = 1;
+
+ kill_fasync(&list->fasync, SIGIO, POLL_IN);
+
+ list = list->next;
+ }
+
+ wake_up_interruptible(&((*mousedev)->wait));
+ mousedev++;
+ }
+}
+
+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 int mousedev_release(struct inode * inode, struct file * file)
+{
+ struct mousedev_list *list = file->private_data;
+ struct mousedev_list **listptr;
+
+ lock_kernel();
+ listptr = &list->mousedev->list;
+ mousedev_fasync(-1, file, 0);
+
+ while (*listptr && (*listptr != list))
+ listptr = &((*listptr)->next);
+ *listptr = (*listptr)->next;
+
+ if (!--list->mousedev->open) {
+ if (list->mousedev->minor == MOUSEDEV_MIX) {
+ struct input_handle *handle = mousedev_handler.handle;
+ while (handle) {
+ struct mousedev *mousedev = handle->private;
+ if (!mousedev->open) {
+ if (mousedev->exist) {
+ input_close_device(&mousedev->handle);
+ } else {
+ input_unregister_minor(mousedev->devfs);
+ mousedev_table[mousedev->minor] = NULL;
+ kfree(mousedev);
+ }
+ }
+ handle = handle->hnext;
+ }
+ } else {
+ if (!mousedev_mix.open) {
+ if (list->mousedev->exist) {
+ input_close_device(&list->mousedev->handle);
+ } else {
+ input_unregister_minor(list->mousedev->devfs);
+ mousedev_table[list->mousedev->minor] = NULL;
+ kfree(list->mousedev);
+ }
+ }
+ }
+ }
+
+ kfree(list);
+ unlock_kernel();
+
+ return 0;
+}
+
+static int mousedev_open(struct inode * inode, struct file * file)
+{
+ struct mousedev_list *list;
+ int i = MINOR(inode->i_rdev) - MOUSEDEV_MINOR_BASE;
+
+ if (i > MOUSEDEV_MINORS || !mousedev_table[i])
+ return -ENODEV;
+
+ if (!(list = kmalloc(sizeof(struct mousedev_list), GFP_KERNEL)))
+ return -ENOMEM;
+ memset(list, 0, sizeof(struct mousedev_list));
+
+ list->mousedev = mousedev_table[i];
+ list->next = mousedev_table[i]->list;
+ mousedev_table[i]->list = list;
+ file->private_data = list;
+
+ if (!list->mousedev->open++) {
+ if (list->mousedev->minor == MOUSEDEV_MIX) {
+ struct input_handle *handle = mousedev_handler.handle;
+ while (handle) {
+ struct mousedev *mousedev = handle->private;
+ if (!mousedev->open)
+ if (mousedev->exist)
+ input_open_device(handle);
+ handle = handle->hnext;
+ }
+ } else {
+ if (!mousedev_mix.open)
+ if (list->mousedev->exist)
+ input_open_device(&list->mousedev->handle);
+ }
+ }
+
+ return 0;
+}
+
+static void mousedev_packet(struct mousedev_list *list, unsigned char off)
+{
+ list->ps2[off] = 0x08 | ((list->dx < 0) << 4) | ((list->dy < 0) << 5) | (list->buttons & 0x07);
+ list->ps2[off + 1] = (list->dx > 127 ? 127 : (list->dx < -127 ? -127 : list->dx));
+ list->ps2[off + 2] = (list->dy > 127 ? 127 : (list->dy < -127 ? -127 : list->dy));
+ list->dx -= list->ps2[off + 1];
+ list->dy -= list->ps2[off + 2];
+ list->bufsiz = off + 3;
+
+ if (list->mode > 1)
+ list->ps2[off] |= ((list->buttons & 0x18) << 3);
+
+ if (list->mode) {
+ list->ps2[off + 3] = (list->dz > 127 ? 127 : (list->dz < -127 ? -127 : list->dz));
+ list->bufsiz++;
+ list->dz -= list->ps2[off + 3];
+ }
+ if (!list->dx && !list->dy && (!list->mode || !list->dz)) list->ready = 0;
+ list->buffer = list->bufsiz;
+}
+
+
+static ssize_t mousedev_write(struct file * file, const char * buffer, size_t count, loff_t *ppos)
+{
+ struct mousedev_list *list = file->private_data;
+ unsigned char c;
+ int i;
+
+ for (i = 0; i < count; i++) {
+
+ c = buffer[i];
+
+ if (c == mousedev_genius_seq[list->genseq]) {
+ if (++list->genseq == MOUSEDEV_GENIUS_LEN) {
+ list->genseq = 0;
+ list->ready = 1;
+ list->mode = 2;
+ }
+ } else list->genseq = 0;
+
+ if (c == mousedev_imps_seq[list->impseq]) {
+ if (++list->impseq == MOUSEDEV_IMPS_LEN) {
+ list->impseq = 0;
+ list->ready = 1;
+ list->mode = 1;
+ }
+ } else list->impseq = 0;
+
+ list->ps2[0] = 0xfa;
+ list->bufsiz = 1;
+
+ switch (c) {
+
+ case 0xeb: /* Poll */
+ mousedev_packet(list, 1);
+ break;
+
+ case 0xf2: /* Get ID */
+ list->ps2[1] = (list->mode == 1) ? 3 : 0;
+ list->bufsiz = 2;
+ break;
+
+ case 0xe9: /* Get info */
+ if (list->mode == 2) {
+ list->ps2[1] = 0x00; list->ps2[2] = 0x33; list->ps2[3] = 0x55;
+ } else {
+ list->ps2[1] = 0x60; list->ps2[2] = 3; list->ps2[3] = 200;
+ }
+ list->bufsiz = 4;
+ 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 * buffer, size_t count, loff_t *ppos)
+{
+ DECLARE_WAITQUEUE(wait, current);
+ struct mousedev_list *list = file->private_data;
+ int retval = 0;
+
+ if (!list->ready && !list->buffer) {
+
+ add_wait_queue(&list->mousedev->wait, &wait);
+ current->state = TASK_INTERRUPTIBLE;
+
+ while (!list->ready) {
+
+ if (file->f_flags & O_NONBLOCK) {
+ retval = -EAGAIN;
+ break;
+ }
+ if (signal_pending(current)) {
+ retval = -ERESTARTSYS;
+ break;
+ }
+
+ schedule();
+ }
+
+ current->state = TASK_RUNNING;
+ remove_wait_queue(&list->mousedev->wait, &wait);
+ }
+
+ if (retval)
+ return retval;
+
+ if (!list->buffer)
+ mousedev_packet(list, 0);
+
+ if (count > list->buffer)
+ count = list->buffer;
+
+ if (copy_to_user(buffer, list->ps2 + list->bufsiz - list->buffer, count))
+ return -EFAULT;
+
+ list->buffer -= count;
+
+ 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);
+ if (list->ready || list->buffer)
+ return POLLIN | POLLRDNORM;
+ return 0;
+}
+
+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)
+{
+ struct mousedev *mousedev;
+ int minor = 0;
+
+ if (!test_bit(EV_KEY, dev->evbit) ||
+ (!test_bit(BTN_LEFT, dev->keybit) && !test_bit(BTN_TOUCH, dev->keybit)))
+ return NULL;
+
+ if ((!test_bit(EV_REL, dev->evbit) || !test_bit(REL_X, dev->relbit)) &&
+ (!test_bit(EV_ABS, dev->evbit) || !test_bit(ABS_X, dev->absbit)))
+ return NULL;
+
+ for (minor = 0; minor < MOUSEDEV_MINORS && mousedev_table[minor]; minor++);
+ if (mousedev_table[minor]) {
+ printk(KERN_ERR "mousedev: no more free mousedev devices\n");
+ return NULL;
+ }
+
+ if (!(mousedev = kmalloc(sizeof(struct mousedev), GFP_KERNEL)))
+ return NULL;
+ memset(mousedev, 0, sizeof(struct mousedev));
+ init_waitqueue_head(&mousedev->wait);
+
+ mousedev->exist = 1;
+ mousedev->minor = minor;
+ mousedev_table[minor] = mousedev;
+
+ mousedev->handle.dev = dev;
+ mousedev->handle.handler = handler;
+ mousedev->handle.private = mousedev;
+
+ mousedev->devfs = input_register_minor("mouse%d", minor, MOUSEDEV_MINOR_BASE);
+
+ if (mousedev_mix.open)
+ input_open_device(&mousedev->handle);
+
+ printk(KERN_INFO "mouse%d: PS/2 mouse device for input%d\n", minor, dev->number);
+
+ return &mousedev->handle;
+}
+
+static void mousedev_disconnect(struct input_handle *handle)
+{
+ struct mousedev *mousedev = handle->private;
+
+ mousedev->exist = 0;
+
+ if (mousedev->open) {
+ input_close_device(handle);
+ } else {
+ if (mousedev_mix.open)
+ input_close_device(handle);
+ input_unregister_minor(mousedev->devfs);
+ mousedev_table[mousedev->minor] = NULL;
+ kfree(mousedev);
+ }
+}
+
+static struct input_handler mousedev_handler = {
+ event: mousedev_event,
+ connect: mousedev_connect,
+ disconnect: mousedev_disconnect,
+ fops: &mousedev_fops,
+ minor: MOUSEDEV_MINOR_BASE,
+};
+
+static int __init mousedev_init(void)
+{
+ input_register_handler(&mousedev_handler);
+
+ memset(&mousedev_mix, 0, sizeof(struct mousedev));
+ init_waitqueue_head(&mousedev_mix.wait);
+ mousedev_table[MOUSEDEV_MIX] = &mousedev_mix;
+ mousedev_mix.exist = 1;
+ mousedev_mix.minor = MOUSEDEV_MIX;
+ mousedev_mix.devfs = input_register_minor("mice", MOUSEDEV_MIX, MOUSEDEV_MINOR_BASE);
+
+ printk(KERN_INFO "mice: PS/2 mouse device common for all mice\n");
+
+ return 0;
+}
+
+static void __exit mousedev_exit(void)
+{
+ input_unregister_minor(mousedev_mix.devfs);
+ input_unregister_handler(&mousedev_handler);
+}
+
+module_init(mousedev_init);
+module_exit(mousedev_exit);
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@suse.cz>");
+MODULE_DESCRIPTION("Input driver to PS/2 or ImPS/2 device driver");