diff options
author | Ralf Baechle <ralf@linux-mips.org> | 2000-08-28 22:00:09 +0000 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 2000-08-28 22:00:09 +0000 |
commit | 1a1d77dd589de5a567fa95e36aa6999c704ceca4 (patch) | |
tree | 141e31f89f18b9fe0831f31852e0435ceaccafc5 /drivers/input | |
parent | fb9c690a18b3d66925a65b17441c37fa14d4370b (diff) |
Merge with 2.4.0-test7.
Diffstat (limited to 'drivers/input')
-rw-r--r-- | drivers/input/Config.in | 20 | ||||
-rw-r--r-- | drivers/input/Makefile | 51 | ||||
-rw-r--r-- | drivers/input/evdev.c | 356 | ||||
-rw-r--r-- | drivers/input/input.c | 426 | ||||
-rw-r--r-- | drivers/input/joydev.c | 497 | ||||
-rw-r--r-- | drivers/input/keybdev.c | 200 | ||||
-rw-r--r-- | drivers/input/mousedev.c | 489 |
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"); |