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/input.c | |
parent | fb9c690a18b3d66925a65b17441c37fa14d4370b (diff) |
Merge with 2.4.0-test7.
Diffstat (limited to 'drivers/input/input.c')
-rw-r--r-- | drivers/input/input.c | 426 |
1 files changed, 426 insertions, 0 deletions
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); |