From 57e6b724c08697d8332a63b60d3a60ee5e74e219 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Wed, 19 Jul 2006 01:39:56 -0400 Subject: Input: rename input.ko into input-core.ko This will allow building input core module from several files which is needed for the reworked force feedback support. Signed-off-by: Dmitry Torokhov --- drivers/input/Makefile | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers/input') diff --git a/drivers/input/Makefile b/drivers/input/Makefile index 1a6ff4982f3..e539a309df8 100644 --- a/drivers/input/Makefile +++ b/drivers/input/Makefile @@ -4,7 +4,9 @@ # Each configuration option enables a list of files. -obj-$(CONFIG_INPUT) += input.o +obj-$(CONFIG_INPUT) += input-core.o +input-core-objs := input.o + obj-$(CONFIG_INPUT_MOUSEDEV) += mousedev.o obj-$(CONFIG_INPUT_JOYDEV) += joydev.o obj-$(CONFIG_INPUT_EVDEV) += evdev.o -- cgit v1.2.3 From 806d41b756fecc1b13584e2b806b76d8934b1679 Mon Sep 17 00:00:00 2001 From: Anssi Hannula Date: Wed, 19 Jul 2006 01:40:14 -0400 Subject: Input: move fixp-arith.h to drivers/input Move fixp-arith.h from drivers/usb/input to drivers/input, as the part of force feedback support that requires trigonometric functions is being moved there. Signed-off-by: Anssi Hannula Signed-off-by: Dmitry Torokhov --- drivers/input/fixp-arith.h | 87 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 87 insertions(+) create mode 100644 drivers/input/fixp-arith.h (limited to 'drivers/input') diff --git a/drivers/input/fixp-arith.h b/drivers/input/fixp-arith.h new file mode 100644 index 00000000000..ed3d2da0c48 --- /dev/null +++ b/drivers/input/fixp-arith.h @@ -0,0 +1,87 @@ +#ifndef _FIXP_ARITH_H +#define _FIXP_ARITH_H + +/* + * Simplistic fixed-point arithmetics. + * Hmm, I'm probably duplicating some code :( + * + * Copyright (c) 2002 Johann Deneux + */ + +/* + * 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 by + * e-mail - mail your message to + */ + +#include + +/* The type representing fixed-point values */ +typedef s16 fixp_t; + +#define FRAC_N 8 +#define FRAC_MASK ((1< 123.0 */ +static inline fixp_t fixp_new(s16 a) +{ + return a< -1.0 + 0x8000 -> 1.0 + 0x0000 -> 0.0 +*/ +static inline fixp_t fixp_new16(s16 a) +{ + return ((s32)a)>>(16-FRAC_N); +} + +static inline fixp_t fixp_cos(unsigned int degrees) +{ + int quadrant = (degrees / 90) & 3; + unsigned int i = degrees % 90; + + if (quadrant == 1 || quadrant == 3) + i = 90 - i; + + i >>= 1; + + return (quadrant == 1 || quadrant == 2)? -cos_table[i] : cos_table[i]; +} + +static inline fixp_t fixp_sin(unsigned int degrees) +{ + return -fixp_cos(degrees + 90); +} + +static inline fixp_t fixp_mult(fixp_t a, fixp_t b) +{ + return ((s32)(a*b))>>FRAC_N; +} + +#endif -- cgit v1.2.3 From 509ca1a9383601fdc5612d3d3ba5b981f6eb6c8b Mon Sep 17 00:00:00 2001 From: Anssi Hannula Date: Wed, 19 Jul 2006 01:40:22 -0400 Subject: Input: implement new force feedback interface Implement a new force feedback interface, in which all non-driver-specific operations are separated to a common module. This includes handling effect type validations, locking, etc. The effects are now file descriptor specific instead of the previous strange half-process half-fd specific behaviour. The effect memory of devices is not emptied if the root user opens and closes the device while another user is using effects. This is a minor change and most likely no force feedback aware programs are affected by this negatively. Otherwise the userspace interface is left unaltered. Signed-off-by: Anssi Hannula Signed-off-by: Dmitry Torokhov --- drivers/input/Makefile | 2 +- drivers/input/evdev.c | 32 ++--- drivers/input/ff-core.c | 367 ++++++++++++++++++++++++++++++++++++++++++++++++ drivers/input/input.c | 6 + 4 files changed, 389 insertions(+), 18 deletions(-) create mode 100644 drivers/input/ff-core.c (limited to 'drivers/input') diff --git a/drivers/input/Makefile b/drivers/input/Makefile index e539a309df8..abdc9d43570 100644 --- a/drivers/input/Makefile +++ b/drivers/input/Makefile @@ -5,7 +5,7 @@ # Each configuration option enables a list of files. obj-$(CONFIG_INPUT) += input-core.o -input-core-objs := input.o +input-core-objs := input.o ff-core.o obj-$(CONFIG_INPUT_MOUSEDEV) += mousedev.o obj-$(CONFIG_INPUT_JOYDEV) += joydev.o diff --git a/drivers/input/evdev.c b/drivers/input/evdev.c index 4bf48188cc9..12c7ab876c3 100644 --- a/drivers/input/evdev.c +++ b/drivers/input/evdev.c @@ -391,8 +391,10 @@ static long evdev_ioctl_handler(struct file *file, unsigned int cmd, struct evdev *evdev = list->evdev; struct input_dev *dev = evdev->handle.dev; struct input_absinfo abs; + struct ff_effect effect; int __user *ip = (int __user *)p; int i, t, u, v; + int error; if (!evdev->exist) return -ENODEV; @@ -460,27 +462,22 @@ static long evdev_ioctl_handler(struct file *file, unsigned int cmd, return 0; case EVIOCSFF: - if (dev->upload_effect) { - struct ff_effect effect; - int err; - - if (copy_from_user(&effect, p, sizeof(effect))) - return -EFAULT; - err = dev->upload_effect(dev, &effect); - if (put_user(effect.id, &(((struct ff_effect __user *)p)->id))) - return -EFAULT; - return err; - } else - return -ENOSYS; + if (copy_from_user(&effect, p, sizeof(effect))) + return -EFAULT; - case EVIOCRMFF: - if (!dev->erase_effect) - return -ENOSYS; + error = input_ff_upload(dev, &effect, file); - return dev->erase_effect(dev, (int)(unsigned long) p); + if (put_user(effect.id, &(((struct ff_effect __user *)p)->id))) + return -EFAULT; + + return error; + + case EVIOCRMFF: + return input_ff_erase(dev, (int)(unsigned long) p, file); case EVIOCGEFFECTS: - if (put_user(dev->ff_effects_max, ip)) + i = test_bit(EV_FF, dev->evbit) ? dev->ff->max_effects : 0; + if (put_user(i, ip)) return -EFAULT; return 0; @@ -669,6 +666,7 @@ static void evdev_disconnect(struct input_handle *handle) evdev->exist = 0; if (evdev->open) { + input_flush_device(handle, NULL); input_close_device(handle); wake_up_interruptible(&evdev->wait); list_for_each_entry(list, &evdev->list, node) diff --git a/drivers/input/ff-core.c b/drivers/input/ff-core.c new file mode 100644 index 00000000000..35656cadc91 --- /dev/null +++ b/drivers/input/ff-core.c @@ -0,0 +1,367 @@ +/* + * Force feedback support for Linux input subsystem + * + * Copyright (c) 2006 Anssi Hannula + * Copyright (c) 2006 Dmitry Torokhov + */ + +/* + * 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 + */ + +/* #define DEBUG */ + +#define debug(format, arg...) pr_debug("ff-core: " format "\n", ## arg) + +#include +#include +#include + +/* + * Check that the effect_id is a valid effect and whether the user + * is the owner + */ +static int check_effect_access(struct ff_device *ff, int effect_id, + struct file *file) +{ + if (effect_id < 0 || effect_id >= ff->max_effects || + !ff->effect_owners[effect_id]) + return -EINVAL; + + if (file && ff->effect_owners[effect_id] != file) + return -EACCES; + + return 0; +} + +/* + * Checks whether 2 effects can be combined together + */ +static inline int check_effects_compatible(struct ff_effect *e1, + struct ff_effect *e2) +{ + return e1->type == e2->type && + (e1->type != FF_PERIODIC || + e1->u.periodic.waveform == e2->u.periodic.waveform); +} + +/* + * Convert an effect into compatible one + */ +static int compat_effect(struct ff_device *ff, struct ff_effect *effect) +{ + int magnitude; + + switch (effect->type) { + case FF_RUMBLE: + if (!test_bit(FF_PERIODIC, ff->ffbit)) + return -EINVAL; + + /* + * calculate manginude of sine wave as average of rumble's + * 2/3 of strong magnitude and 1/3 of weak magnitude + */ + magnitude = effect->u.rumble.strong_magnitude / 3 + + effect->u.rumble.weak_magnitude / 6; + + effect->type = FF_PERIODIC; + effect->u.periodic.waveform = FF_SINE; + effect->u.periodic.period = 50; + effect->u.periodic.magnitude = max(magnitude, 0x7fff); + effect->u.periodic.offset = 0; + effect->u.periodic.phase = 0; + effect->u.periodic.envelope.attack_length = 0; + effect->u.periodic.envelope.attack_level = 0; + effect->u.periodic.envelope.fade_length = 0; + effect->u.periodic.envelope.fade_level = 0; + + return 0; + + default: + /* Let driver handle conversion */ + return 0; + } +} + +/** + * input_ff_upload() - upload effect into force-feedback device + * @dev: input device + * @effect: effect to be uploaded + * @file: owner of the effect + */ +int input_ff_upload(struct input_dev *dev, struct ff_effect *effect, + struct file *file) +{ + struct ff_device *ff = dev->ff; + struct ff_effect *old; + int ret = 0; + int id; + + if (!test_bit(EV_FF, dev->evbit)) + return -ENOSYS; + + if (effect->type < FF_EFFECT_MIN || effect->type > FF_EFFECT_MAX || + !test_bit(effect->type, dev->ffbit)) { + debug("invalid or not supported effect type in upload"); + return -EINVAL; + } + + if (effect->type == FF_PERIODIC && + (effect->u.periodic.waveform < FF_WAVEFORM_MIN || + effect->u.periodic.waveform > FF_WAVEFORM_MAX || + !test_bit(effect->u.periodic.waveform, dev->ffbit))) { + debug("invalid or not supported wave form in upload"); + return -EINVAL; + } + + if (!test_bit(effect->type, ff->ffbit)) { + ret = compat_effect(ff, effect); + if (ret) + return ret; + } + + mutex_lock(&ff->mutex); + + if (effect->id == -1) { + for (id = 0; id < ff->max_effects; id++) + if (!ff->effect_owners[id]) + break; + + if (id >= ff->max_effects) { + ret = -ENOSPC; + goto out; + } + + effect->id = id; + old = NULL; + + } else { + id = effect->id; + + ret = check_effect_access(ff, id, file); + if (ret) + goto out; + + old = &ff->effects[id]; + + if (!check_effects_compatible(effect, old)) { + ret = -EINVAL; + goto out; + } + } + + ret = ff->upload(dev, effect, old); + if (ret) + goto out; + + ff->effects[id] = *effect; + ff->effect_owners[id] = file; + + out: + mutex_unlock(&ff->mutex); + return ret; +} +EXPORT_SYMBOL_GPL(input_ff_upload); + +/* + * Erases the effect if the requester is also the effect owner. The mutex + * should already be locked before calling this function. + */ +static int erase_effect(struct input_dev *dev, int effect_id, + struct file *file) +{ + struct ff_device *ff = dev->ff; + int error; + + error = check_effect_access(ff, effect_id, file); + if (error) + return error; + + ff->playback(dev, effect_id, 0); + + if (ff->erase) { + error = ff->erase(dev, effect_id); + if (error) + return error; + } + + ff->effect_owners[effect_id] = NULL; + + return 0; +} + +/** + * input_ff_erase - erase an effect from device + * @dev: input device to erase effect from + * @effect_id: id of the ffect to be erased + * @file: purported owner of the request + * + * This function erases a force-feedback effect from specified device. + * The effect will only be erased if it was uploaded through the same + * file handle that is requesting erase. + */ +int input_ff_erase(struct input_dev *dev, int effect_id, struct file *file) +{ + struct ff_device *ff = dev->ff; + int ret; + + if (!test_bit(EV_FF, dev->evbit)) + return -ENOSYS; + + mutex_lock(&ff->mutex); + ret = erase_effect(dev, effect_id, file); + mutex_unlock(&ff->mutex); + + return ret; +} +EXPORT_SYMBOL_GPL(input_ff_erase); + +/* + * flush_effects - erase all effects owned by a file handle + */ +static int flush_effects(struct input_dev *dev, struct file *file) +{ + struct ff_device *ff = dev->ff; + int i; + + debug("flushing now"); + + mutex_lock(&ff->mutex); + + for (i = 0; i < ff->max_effects; i++) + erase_effect(dev, i, file); + + mutex_unlock(&ff->mutex); + + return 0; +} + +/** + * input_ff_event() - generic handler for force-feedback events + * @dev: input device to send the effect to + * @type: event type (anything but EV_FF is ignored) + * @code: event code + * @value: event value + */ +int input_ff_event(struct input_dev *dev, unsigned int type, + unsigned int code, int value) +{ + struct ff_device *ff = dev->ff; + + if (type != EV_FF) + return 0; + + mutex_lock(&ff->mutex); + + switch (code) { + case FF_GAIN: + if (!test_bit(FF_GAIN, dev->ffbit) || value > 0xffff) + break; + + ff->set_gain(dev, value); + break; + + case FF_AUTOCENTER: + if (!test_bit(FF_AUTOCENTER, dev->ffbit) || value > 0xffff) + break; + + ff->set_autocenter(dev, value); + break; + + default: + ff->playback(dev, code, value); + break; + } + + mutex_unlock(&ff->mutex); + return 0; +} +EXPORT_SYMBOL_GPL(input_ff_event); + +/** + * input_ff_create() - create force-feedback device + * @dev: input device supporting force-feedback + * @max_effects: maximum number of effects supported by the device + * + * This function allocates all necessary memory for a force feedback + * portion of an input device and installs all default handlers. + * @dev->ffbit should be already set up before calling this function. + * Once ff device is created you need to setup its upload, erase, + * playback and other handlers before registering input device + */ +int input_ff_create(struct input_dev *dev, int max_effects) +{ + struct ff_device *ff; + int i; + + if (!max_effects) { + printk(KERN_ERR + "ff-core: cannot allocate device without any effects\n"); + return -EINVAL; + } + + ff = kzalloc(sizeof(struct ff_device) + + max_effects * sizeof(struct file *), GFP_KERNEL); + if (!ff) + return -ENOMEM; + + ff->effects = kcalloc(max_effects, sizeof(struct ff_effect), + GFP_KERNEL); + if (!ff->effects) { + kfree(ff); + return -ENOMEM; + } + + ff->max_effects = max_effects; + mutex_init(&ff->mutex); + + dev->ff = ff; + dev->flush = flush_effects; + dev->event = input_ff_event; + set_bit(EV_FF, dev->evbit); + + /* Copy "true" bits into ff device bitmap */ + for (i = 0; i <= FF_MAX; i++) + if (test_bit(i, dev->ffbit)) + set_bit(i, ff->ffbit); + + /* we can emulate RUMBLE with periodic effects */ + if (test_bit(FF_PERIODIC, ff->ffbit)) + set_bit(FF_RUMBLE, dev->ffbit); + + return 0; +} +EXPORT_SYMBOL_GPL(input_ff_create); + +/** + * input_ff_free() - frees force feedback portion of input device + * @dev: input device supporintg force feedback + * + * This function is only needed in error path as input core will + * automatically free force feedback structures when device is + * destroyed. + */ +void input_ff_destroy(struct input_dev *dev) +{ + clear_bit(EV_FF, dev->evbit); + if (dev->ff) { + if (dev->ff->destroy) + dev->ff->destroy(dev->ff); + kfree(dev->ff->private); + kfree(dev->ff); + dev->ff = NULL; + } +} +EXPORT_SYMBOL_GPL(input_ff_destroy); diff --git a/drivers/input/input.c b/drivers/input/input.c index 9cb4b9a54f0..1fc0517e942 100644 --- a/drivers/input/input.c +++ b/drivers/input/input.c @@ -176,6 +176,10 @@ void input_event(struct input_dev *dev, unsigned int type, unsigned int code, in break; case EV_FF: + + if (value < 0) + return; + if (dev->event) dev->event(dev, type, code, value); break; @@ -762,7 +766,9 @@ static void input_dev_release(struct class_device *class_dev) { struct input_dev *dev = to_input_dev(class_dev); + input_ff_destroy(dev); kfree(dev); + module_put(THIS_MODULE); } -- cgit v1.2.3 From 7d928a2b14eede1f333db7b7b684c57f7fa7f456 Mon Sep 17 00:00:00 2001 From: Anssi Hannula Date: Wed, 19 Jul 2006 01:40:30 -0400 Subject: Input: unified force feedback support for memoryless devices Consolidate core implementing memoryless devices in one module; added support for gain and envelopes and periodic => rumble conversion. Signed-off-by: Anssi Hannula Signed-off-by: Dmitry Torokhov --- drivers/input/Kconfig | 14 ++ drivers/input/Makefile | 2 + drivers/input/ff-memless.c | 515 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 531 insertions(+) create mode 100644 drivers/input/ff-memless.c (limited to 'drivers/input') diff --git a/drivers/input/Kconfig b/drivers/input/Kconfig index 58223b5d842..96232313b1b 100644 --- a/drivers/input/Kconfig +++ b/drivers/input/Kconfig @@ -24,6 +24,20 @@ config INPUT if INPUT +config INPUT_FF_MEMLESS + tristate "Support for memoryless force-feedback devices" + default n + ---help--- + Say Y here if you have memoryless force-feedback input device + such as Logitech WingMan Force 3D, ThrustMaster FireStorm Dual + Power 2, or similar. You will also need to enable hardware-specific + driver. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called ff-memless. + comment "Userland interfaces" config INPUT_MOUSEDEV diff --git a/drivers/input/Makefile b/drivers/input/Makefile index abdc9d43570..a005b1df5f1 100644 --- a/drivers/input/Makefile +++ b/drivers/input/Makefile @@ -7,6 +7,8 @@ obj-$(CONFIG_INPUT) += input-core.o input-core-objs := input.o ff-core.o +obj-$(CONFIG_INPUT_FF_MEMLESS) += ff-memless.o + obj-$(CONFIG_INPUT_MOUSEDEV) += mousedev.o obj-$(CONFIG_INPUT_JOYDEV) += joydev.o obj-$(CONFIG_INPUT_EVDEV) += evdev.o diff --git a/drivers/input/ff-memless.c b/drivers/input/ff-memless.c new file mode 100644 index 00000000000..cd8b7297e6d --- /dev/null +++ b/drivers/input/ff-memless.c @@ -0,0 +1,515 @@ +/* + * Force feedback support for memoryless devices + * + * Copyright (c) 2006 Anssi Hannula + * Copyright (c) 2006 Dmitry Torokhov + */ + +/* + * 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 + */ + +/* #define DEBUG */ + +#define debug(format, arg...) pr_debug("ff-memless: " format "\n", ## arg) + +#include +#include +#include +#include +#include + +#include "fixp-arith.h" + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Anssi Hannula "); +MODULE_DESCRIPTION("Force feedback support for memoryless devices"); + +/* Number of effects handled with memoryless devices */ +#define FF_MEMLESS_EFFECTS 16 + +/* Envelope update interval in ms */ +#define FF_ENVELOPE_INTERVAL 50 + +#define FF_EFFECT_STARTED 0 +#define FF_EFFECT_PLAYING 1 +#define FF_EFFECT_ABORTING 2 + +struct ml_effect_state { + struct ff_effect *effect; + unsigned long flags; /* effect state (STARTED, PLAYING, etc) */ + int count; /* loop count of the effect */ + unsigned long play_at; /* start time */ + unsigned long stop_at; /* stop time */ + unsigned long adj_at; /* last time the effect was sent */ +}; + +struct ml_device { + void *private; + struct ml_effect_state states[FF_MEMLESS_EFFECTS]; + int gain; + struct timer_list timer; + spinlock_t timer_lock; + struct input_dev *dev; + + int (*play_effect)(struct input_dev *dev, void *data, + struct ff_effect *effect); +}; + +static const struct ff_envelope *get_envelope(const struct ff_effect *effect) +{ + static const struct ff_envelope empty_envelope; + + switch (effect->type) { + case FF_PERIODIC: + return &effect->u.periodic.envelope; + case FF_CONSTANT: + return &effect->u.constant.envelope; + default: + return &empty_envelope; + } +} + +/* + * Check for the next time envelope requires an update on memoryless devices + */ +static unsigned long calculate_next_time(struct ml_effect_state *state) +{ + const struct ff_envelope *envelope = get_envelope(state->effect); + unsigned long attack_stop, fade_start, next_fade; + + if (envelope->attack_length) { + attack_stop = state->play_at + + msecs_to_jiffies(envelope->attack_length); + if (time_before(state->adj_at, attack_stop)) + return state->adj_at + + msecs_to_jiffies(FF_ENVELOPE_INTERVAL); + } + + if (state->effect->replay.length) { + if (envelope->fade_length) { + /* check when fading should start */ + fade_start = state->stop_at - + msecs_to_jiffies(envelope->fade_length); + + if (time_before(state->adj_at, fade_start)) + return fade_start; + + /* already fading, advance to next checkpoint */ + next_fade = state->adj_at + + msecs_to_jiffies(FF_ENVELOPE_INTERVAL); + if (time_before(next_fade, state->stop_at)) + return next_fade; + } + + return state->stop_at; + } + + return state->play_at; +} + +static void ml_schedule_timer(struct ml_device *ml) +{ + struct ml_effect_state *state; + unsigned long now = jiffies; + unsigned long earliest = 0; + unsigned long next_at; + int events = 0; + int i; + + debug("calculating next timer"); + + for (i = 0; i < FF_MEMLESS_EFFECTS; i++) { + + state = &ml->states[i]; + + if (!test_bit(FF_EFFECT_STARTED, &state->flags)) + continue; + + if (test_bit(FF_EFFECT_PLAYING, &state->flags)) + next_at = calculate_next_time(state); + else + next_at = state->play_at; + + if (time_before_eq(now, next_at) && + (++events == 1 || time_before(next_at, earliest))) + earliest = next_at; + } + + if (!events) { + debug("no actions"); + del_timer(&ml->timer); + } else { + debug("timer set"); + mod_timer(&ml->timer, earliest); + } +} + +/* + * Apply an envelope to a value + */ +static int apply_envelope(struct ml_effect_state *state, int value, + struct ff_envelope *envelope) +{ + struct ff_effect *effect = state->effect; + unsigned long now = jiffies; + int time_from_level; + int time_of_envelope; + int envelope_level; + int difference; + + if (envelope->attack_length && + time_before(now, + state->play_at + msecs_to_jiffies(envelope->attack_length))) { + debug("value = 0x%x, attack_level = 0x%x", value, + envelope->attack_level); + time_from_level = jiffies_to_msecs(now - state->play_at); + time_of_envelope = envelope->attack_length; + envelope_level = min_t(__s16, envelope->attack_level, 0x7fff); + + } else if (envelope->fade_length && effect->replay.length && + time_after(now, + state->stop_at - msecs_to_jiffies(envelope->fade_length)) && + time_before(now, state->stop_at)) { + time_from_level = jiffies_to_msecs(state->stop_at - now); + time_of_envelope = envelope->fade_length; + envelope_level = min_t(__s16, envelope->fade_level, 0x7fff); + } else + return value; + + difference = abs(value) - envelope_level; + + debug("difference = %d", difference); + debug("time_from_level = 0x%x", time_from_level); + debug("time_of_envelope = 0x%x", time_of_envelope); + + difference = difference * time_from_level / time_of_envelope; + + debug("difference = %d", difference); + + return value < 0 ? + -(difference + envelope_level) : (difference + envelope_level); +} + +/* + * Return the type the effect has to be converted into (memless devices) + */ +static int get_compatible_type(struct ff_device *ff, int effect_type) +{ + + if (test_bit(effect_type, ff->ffbit)) + return effect_type; + + if (effect_type == FF_PERIODIC && test_bit(FF_RUMBLE, ff->ffbit)) + return FF_RUMBLE; + + printk(KERN_ERR + "ff-memless: invalid type in get_compatible_type()\n"); + + return 0; +} + +/* + * Combine two effects and apply gain. + */ +static void ml_combine_effects(struct ff_effect *effect, + struct ml_effect_state *state, + int gain) +{ + struct ff_effect *new = state->effect; + unsigned int strong, weak, i; + int x, y; + fixp_t level; + + switch (new->type) { + case FF_CONSTANT: + i = new->direction * 360 / 0xffff; + level = fixp_new16(apply_envelope(state, + new->u.constant.level, + &new->u.constant.envelope)); + x = fixp_mult(fixp_sin(i), level) * gain / 0xffff; + y = fixp_mult(-fixp_cos(i), level) * gain / 0xffff; + /* + * here we abuse ff_ramp to hold x and y of constant force + * If in future any driver wants something else than x and y + * in s8, this should be changed to something more generic + */ + effect->u.ramp.start_level = + max(min(effect->u.ramp.start_level + x, 0x7f), -0x80); + effect->u.ramp.end_level = + max(min(effect->u.ramp.end_level + y, 0x7f), -0x80); + break; + + case FF_RUMBLE: + strong = new->u.rumble.strong_magnitude * gain / 0xffff; + weak = new->u.rumble.weak_magnitude * gain / 0xffff; + effect->u.rumble.strong_magnitude = + min(strong + effect->u.rumble.strong_magnitude, + 0xffffU); + effect->u.rumble.weak_magnitude = + min(weak + effect->u.rumble.weak_magnitude, 0xffffU); + break; + + case FF_PERIODIC: + i = apply_envelope(state, abs(new->u.periodic.magnitude), + &new->u.periodic.envelope); + + /* here we also scale it 0x7fff => 0xffff */ + i = i * gain / 0x7fff; + + effect->u.rumble.strong_magnitude = + min(i + effect->u.rumble.strong_magnitude, 0xffffU); + effect->u.rumble.weak_magnitude = + min(i + effect->u.rumble.weak_magnitude, 0xffffU); + break; + + default: + printk(KERN_ERR "ff-memless: invalid type in ml_combine_effects()\n"); + break; + } + +} + + +/* + * Because memoryless devices have only one effect per effect type active + * at one time we have to combine multiple effects into one + */ +static int ml_get_combo_effect(struct ml_device *ml, + unsigned long *effect_handled, + struct ff_effect *combo_effect) +{ + struct ff_effect *effect; + struct ml_effect_state *state; + int effect_type; + int i; + + memset(combo_effect, 0, sizeof(struct ff_effect)); + + for (i = 0; i < FF_MEMLESS_EFFECTS; i++) { + if (__test_and_set_bit(i, effect_handled)) + continue; + + state = &ml->states[i]; + effect = state->effect; + + if (!test_bit(FF_EFFECT_STARTED, &state->flags)) + continue; + + if (time_before(jiffies, state->play_at)) + continue; + + /* + * here we have started effects that are either + * currently playing (and may need be aborted) + * or need to start playing. + */ + effect_type = get_compatible_type(ml->dev->ff, effect->type); + if (combo_effect->type != effect_type) { + if (combo_effect->type != 0) { + __clear_bit(i, effect_handled); + continue; + } + combo_effect->type = effect_type; + } + + if (__test_and_clear_bit(FF_EFFECT_ABORTING, &state->flags)) { + __clear_bit(FF_EFFECT_PLAYING, &state->flags); + __clear_bit(FF_EFFECT_STARTED, &state->flags); + } else if (effect->replay.length && + time_after_eq(jiffies, state->stop_at)) { + + __clear_bit(FF_EFFECT_PLAYING, &state->flags); + + if (--state->count <= 0) { + __clear_bit(FF_EFFECT_STARTED, &state->flags); + } else { + state->play_at = jiffies + + msecs_to_jiffies(effect->replay.delay); + state->stop_at = state->play_at + + msecs_to_jiffies(effect->replay.length); + } + } else { + __set_bit(FF_EFFECT_PLAYING, &state->flags); + state->adj_at = jiffies; + ml_combine_effects(combo_effect, state, ml->gain); + } + } + + return combo_effect->type != 0; +} + +static void ml_play_effects(struct ml_device *ml) +{ + struct ff_effect effect; + DECLARE_BITMAP(handled_bm, FF_MEMLESS_EFFECTS); + + memset(handled_bm, 0, sizeof(handled_bm)); + + while (ml_get_combo_effect(ml, handled_bm, &effect)) + ml->play_effect(ml->dev, ml->private, &effect); + + ml_schedule_timer(ml); +} + +static void ml_effect_timer(unsigned long timer_data) +{ + struct input_dev *dev = (struct input_dev *)timer_data; + struct ml_device *ml = dev->ff->private; + + debug("timer: updating effects"); + + spin_lock(&ml->timer_lock); + ml_play_effects(ml); + spin_unlock(&ml->timer_lock); +} + +static void ml_ff_set_gain(struct input_dev *dev, u16 gain) +{ + struct ml_device *ml = dev->ff->private; + int i; + + spin_lock_bh(&ml->timer_lock); + + ml->gain = gain; + + for (i = 0; i < FF_MEMLESS_EFFECTS; i++) + __clear_bit(FF_EFFECT_PLAYING, &ml->states[i].flags); + + ml_play_effects(ml); + + spin_unlock_bh(&ml->timer_lock); +} + +static int ml_ff_playback(struct input_dev *dev, int effect_id, int value) +{ + struct ml_device *ml = dev->ff->private; + struct ml_effect_state *state = &ml->states[effect_id]; + + spin_lock_bh(&ml->timer_lock); + + if (value > 0) { + debug("initiated play"); + + __set_bit(FF_EFFECT_STARTED, &state->flags); + state->count = value; + state->play_at = jiffies + + msecs_to_jiffies(state->effect->replay.delay); + state->stop_at = state->play_at + + msecs_to_jiffies(state->effect->replay.length); + state->adj_at = state->play_at; + + ml_schedule_timer(ml); + + } else { + debug("initiated stop"); + + if (test_bit(FF_EFFECT_PLAYING, &state->flags)) + __set_bit(FF_EFFECT_ABORTING, &state->flags); + else + __clear_bit(FF_EFFECT_STARTED, &state->flags); + + ml_play_effects(ml); + } + + spin_unlock_bh(&ml->timer_lock); + + return 0; +} + +static int ml_ff_upload(struct input_dev *dev, + struct ff_effect *effect, struct ff_effect *old) +{ + struct ml_device *ml = dev->ff->private; + struct ml_effect_state *state = &ml->states[effect->id]; + + spin_lock_bh(&ml->timer_lock); + + if (test_bit(FF_EFFECT_STARTED, &state->flags)) { + __clear_bit(FF_EFFECT_PLAYING, &state->flags); + state->play_at = jiffies + + msecs_to_jiffies(state->effect->replay.delay); + state->stop_at = state->play_at + + msecs_to_jiffies(state->effect->replay.length); + state->adj_at = state->play_at; + ml_schedule_timer(ml); + } + + spin_unlock_bh(&ml->timer_lock); + + return 0; +} + +static void ml_ff_destroy(struct ff_device *ff) +{ + struct ml_device *ml = ff->private; + + kfree(ml->private); +} + +/** + * input_ff_create_memless() - create memoryless FF device + * @dev: input device supporting force-feedback + * @data: driver-specific data to be passed into @play_effect + * @play_effect: driver-specific method for playing FF effect + */ +int input_ff_create_memless(struct input_dev *dev, void *data, + int (*play_effect)(struct input_dev *, void *, struct ff_effect *)) +{ + struct ml_device *ml; + struct ff_device *ff; + int error; + int i; + + ml = kzalloc(sizeof(struct ml_device), GFP_KERNEL); + if (!ml) + return -ENOMEM; + + ml->dev = dev; + ml->private = data; + ml->play_effect = play_effect; + ml->gain = 0xffff; + spin_lock_init(&ml->timer_lock); + setup_timer(&ml->timer, ml_effect_timer, (unsigned long)dev); + + set_bit(FF_GAIN, dev->ffbit); + + error = input_ff_create(dev, FF_MEMLESS_EFFECTS); + if (error) { + kfree(ml); + return error; + } + + ff = dev->ff; + ff->private = ml; + ff->upload = ml_ff_upload; + ff->playback = ml_ff_playback; + ff->set_gain = ml_ff_set_gain; + ff->destroy = ml_ff_destroy; + + /* we can emulate periodic effects with RUMBLE */ + if (test_bit(FF_RUMBLE, ff->ffbit)) { + set_bit(FF_PERIODIC, dev->ffbit); + set_bit(FF_SINE, dev->ffbit); + set_bit(FF_TRIANGLE, dev->ffbit); + set_bit(FF_SQUARE, dev->ffbit); + } + + for (i = 0; i < FF_MEMLESS_EFFECTS; i++) + ml->states[i].effect = &ff->effects[i]; + + return 0; +} +EXPORT_SYMBOL_GPL(input_ff_create_memless); -- cgit v1.2.3 From f6a01c85965c9e6fa8fb893c1fa5db16130d0ccb Mon Sep 17 00:00:00 2001 From: Anssi Hannula Date: Wed, 19 Jul 2006 01:40:39 -0400 Subject: Input: iforce - switch to the new FF interface Signed-off-by: Anssi Hannula Signed-off-by: Dmitry Torokhov --- drivers/input/joystick/iforce/iforce-ff.c | 118 ++++++------- drivers/input/joystick/iforce/iforce-main.c | 226 +++++++++---------------- drivers/input/joystick/iforce/iforce-packets.c | 15 +- drivers/input/joystick/iforce/iforce.h | 26 +-- 4 files changed, 147 insertions(+), 238 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/joystick/iforce/iforce-ff.c b/drivers/input/joystick/iforce/iforce-ff.c index 50c90765aee..8fb0c19cc60 100644 --- a/drivers/input/joystick/iforce/iforce-ff.c +++ b/drivers/input/joystick/iforce/iforce-ff.c @@ -165,19 +165,19 @@ static int make_condition_modifier(struct iforce* iforce, data[0] = LO(mod_chunk->start); data[1] = HI(mod_chunk->start); - data[2] = (100*rk)>>15; /* Dangerous: the sign is extended by gcc on plateforms providing an arith shift */ - data[3] = (100*lk)>>15; /* This code is incorrect on cpus lacking arith shift */ + data[2] = (100 * rk) >> 15; /* Dangerous: the sign is extended by gcc on plateforms providing an arith shift */ + data[3] = (100 * lk) >> 15; /* This code is incorrect on cpus lacking arith shift */ - center = (500*center)>>15; + center = (500 * center) >> 15; data[4] = LO(center); data[5] = HI(center); - db = (1000*db)>>16; + db = (1000 * db) >> 16; data[6] = LO(db); data[7] = HI(db); - data[8] = (100*rsat)>>16; - data[9] = (100*lsat)>>16; + data[8] = (100 * rsat) >> 16; + data[9] = (100 * lsat) >> 16; iforce_send_packet(iforce, FF_CMD_CONDITION, data); iforce_dump_packet("condition", FF_CMD_CONDITION, data); @@ -188,6 +188,7 @@ static int make_condition_modifier(struct iforce* iforce, static unsigned char find_button(struct iforce *iforce, signed short button) { int i; + for (i = 1; iforce->type->btn[i] >= 0; i++) if (iforce->type->btn[i] == button) return i + 1; @@ -198,19 +199,17 @@ static unsigned char find_button(struct iforce *iforce, signed short button) * Analyse the changes in an effect, and tell if we need to send an condition * parameter packet */ -static int need_condition_modifier(struct iforce* iforce, struct ff_effect* new) +static int need_condition_modifier(struct ff_effect *old, struct ff_effect *new) { - int id = new->id; - struct ff_effect* old = &iforce->core_effects[id].effect; - int ret=0; + int ret = 0; int i; if (new->type != FF_SPRING && new->type != FF_FRICTION) { printk(KERN_WARNING "iforce.c: bad effect type in need_condition_modifier\n"); - return FALSE; + return 0; } - for(i=0; i<2; i++) { + for (i = 0; i < 2; i++) { ret |= old->u.condition[i].right_saturation != new->u.condition[i].right_saturation || old->u.condition[i].left_saturation != new->u.condition[i].left_saturation || old->u.condition[i].right_coeff != new->u.condition[i].right_coeff @@ -225,35 +224,29 @@ static int need_condition_modifier(struct iforce* iforce, struct ff_effect* new) * Analyse the changes in an effect, and tell if we need to send a magnitude * parameter packet */ -static int need_magnitude_modifier(struct iforce* iforce, struct ff_effect* effect) +static int need_magnitude_modifier(struct ff_effect *old, struct ff_effect *effect) { - int id = effect->id; - struct ff_effect* old = &iforce->core_effects[id].effect; - if (effect->type != FF_CONSTANT) { printk(KERN_WARNING "iforce.c: bad effect type in need_envelope_modifier\n"); - return FALSE; + return 0; } - return (old->u.constant.level != effect->u.constant.level); + return old->u.constant.level != effect->u.constant.level; } /* * Analyse the changes in an effect, and tell if we need to send an envelope * parameter packet */ -static int need_envelope_modifier(struct iforce* iforce, struct ff_effect* effect) +static int need_envelope_modifier(struct ff_effect *old, struct ff_effect *effect) { - int id = effect->id; - struct ff_effect* old = &iforce->core_effects[id].effect; - switch (effect->type) { case FF_CONSTANT: if (old->u.constant.envelope.attack_length != effect->u.constant.envelope.attack_length || old->u.constant.envelope.attack_level != effect->u.constant.envelope.attack_level || old->u.constant.envelope.fade_length != effect->u.constant.envelope.fade_length || old->u.constant.envelope.fade_level != effect->u.constant.envelope.fade_level) - return TRUE; + return 1; break; case FF_PERIODIC: @@ -261,30 +254,26 @@ static int need_envelope_modifier(struct iforce* iforce, struct ff_effect* effec || old->u.periodic.envelope.attack_level != effect->u.periodic.envelope.attack_level || old->u.periodic.envelope.fade_length != effect->u.periodic.envelope.fade_length || old->u.periodic.envelope.fade_level != effect->u.periodic.envelope.fade_level) - return TRUE; + return 1; break; default: printk(KERN_WARNING "iforce.c: bad effect type in need_envelope_modifier\n"); } - return FALSE; + return 0; } /* * Analyse the changes in an effect, and tell if we need to send a periodic * parameter effect */ -static int need_period_modifier(struct iforce* iforce, struct ff_effect* new) +static int need_period_modifier(struct ff_effect *old, struct ff_effect *new) { - int id = new->id; - struct ff_effect* old = &iforce->core_effects[id].effect; - if (new->type != FF_PERIODIC) { - printk(KERN_WARNING "iforce.c: bad effect type in need_periodic_modifier\n"); - return FALSE; + printk(KERN_WARNING "iforce.c: bad effect type in need_period_modifier\n"); + return 0; } - return (old->u.periodic.period != new->u.periodic.period || old->u.periodic.magnitude != new->u.periodic.magnitude || old->u.periodic.offset != new->u.periodic.offset @@ -295,19 +284,16 @@ static int need_period_modifier(struct iforce* iforce, struct ff_effect* new) * Analyse the changes in an effect, and tell if we need to send an effect * packet */ -static int need_core(struct iforce* iforce, struct ff_effect* new) +static int need_core(struct ff_effect *old, struct ff_effect *new) { - int id = new->id; - struct ff_effect* old = &iforce->core_effects[id].effect; - if (old->direction != new->direction || old->trigger.button != new->trigger.button || old->trigger.interval != new->trigger.interval || old->replay.length != new->replay.length || old->replay.delay != new->replay.delay) - return TRUE; + return 1; - return FALSE; + return 0; } /* * Send the part common to all effects to the device @@ -360,7 +346,7 @@ static int make_core(struct iforce* iforce, u16 id, u16 mod_id1, u16 mod_id2, * Upload a periodic effect to the device * See also iforce_upload_constant. */ -int iforce_upload_periodic(struct iforce* iforce, struct ff_effect* effect, int is_update) +int iforce_upload_periodic(struct iforce *iforce, struct ff_effect *effect, struct ff_effect *old) { u8 wave_code; int core_id = effect->id; @@ -371,23 +357,25 @@ int iforce_upload_periodic(struct iforce* iforce, struct ff_effect* effect, int int param2_err = 1; int core_err = 0; - if (!is_update || need_period_modifier(iforce, effect)) { + if (!old || need_period_modifier(old, effect)) { param1_err = make_period_modifier(iforce, mod1_chunk, - is_update, + old != NULL, effect->u.periodic.magnitude, effect->u.periodic.offset, effect->u.periodic.period, effect->u.periodic.phase); - if (param1_err) return param1_err; + if (param1_err) + return param1_err; set_bit(FF_MOD1_IS_USED, core_effect->flags); } - if (!is_update || need_envelope_modifier(iforce, effect)) { + if (!old || need_envelope_modifier(old, effect)) { param2_err = make_envelope_modifier(iforce, mod2_chunk, - is_update, + old !=NULL, effect->u.periodic.envelope.attack_length, effect->u.periodic.envelope.attack_level, effect->u.periodic.envelope.fade_length, effect->u.periodic.envelope.fade_level); - if (param2_err) return param2_err; + if (param2_err) + return param2_err; set_bit(FF_MOD2_IS_USED, core_effect->flags); } @@ -400,7 +388,7 @@ int iforce_upload_periodic(struct iforce* iforce, struct ff_effect* effect, int default: wave_code = 0x20; break; } - if (!is_update || need_core(iforce, effect)) { + if (!old || need_core(old, effect)) { core_err = make_core(iforce, effect->id, mod1_chunk->start, mod2_chunk->start, @@ -429,7 +417,7 @@ int iforce_upload_periodic(struct iforce* iforce, struct ff_effect* effect, int * 0 Ok, effect created or updated * 1 effect did not change since last upload, and no packet was therefore sent */ -int iforce_upload_constant(struct iforce* iforce, struct ff_effect* effect, int is_update) +int iforce_upload_constant(struct iforce *iforce, struct ff_effect *effect, struct ff_effect *old) { int core_id = effect->id; struct iforce_core_effect* core_effect = iforce->core_effects + core_id; @@ -439,26 +427,28 @@ int iforce_upload_constant(struct iforce* iforce, struct ff_effect* effect, int int param2_err = 1; int core_err = 0; - if (!is_update || need_magnitude_modifier(iforce, effect)) { + if (!old || need_magnitude_modifier(old, effect)) { param1_err = make_magnitude_modifier(iforce, mod1_chunk, - is_update, + old != NULL, effect->u.constant.level); - if (param1_err) return param1_err; + if (param1_err) + return param1_err; set_bit(FF_MOD1_IS_USED, core_effect->flags); } - if (!is_update || need_envelope_modifier(iforce, effect)) { + if (!old || need_envelope_modifier(old, effect)) { param2_err = make_envelope_modifier(iforce, mod2_chunk, - is_update, + old != NULL, effect->u.constant.envelope.attack_length, effect->u.constant.envelope.attack_level, effect->u.constant.envelope.fade_length, effect->u.constant.envelope.fade_level); - if (param2_err) return param2_err; + if (param2_err) + return param2_err; set_bit(FF_MOD2_IS_USED, core_effect->flags); } - if (!is_update || need_core(iforce, effect)) { + if (!old || need_core(old, effect)) { core_err = make_core(iforce, effect->id, mod1_chunk->start, mod2_chunk->start, @@ -483,7 +473,7 @@ int iforce_upload_constant(struct iforce* iforce, struct ff_effect* effect, int /* * Upload an condition effect. Those are for example friction, inertia, springs... */ -int iforce_upload_condition(struct iforce* iforce, struct ff_effect* effect, int is_update) +int iforce_upload_condition(struct iforce *iforce, struct ff_effect *effect, struct ff_effect *old) { int core_id = effect->id; struct iforce_core_effect* core_effect = iforce->core_effects + core_id; @@ -494,37 +484,39 @@ int iforce_upload_condition(struct iforce* iforce, struct ff_effect* effect, int int core_err = 0; switch (effect->type) { - case FF_SPRING: type = 0x40; break; - case FF_DAMPER: type = 0x41; break; + case FF_SPRING: type = 0x40; break; + case FF_DAMPER: type = 0x41; break; default: return -1; } - if (!is_update || need_condition_modifier(iforce, effect)) { + if (!old || need_condition_modifier(old, effect)) { param_err = make_condition_modifier(iforce, mod1_chunk, - is_update, + old != NULL, effect->u.condition[0].right_saturation, effect->u.condition[0].left_saturation, effect->u.condition[0].right_coeff, effect->u.condition[0].left_coeff, effect->u.condition[0].deadband, effect->u.condition[0].center); - if (param_err) return param_err; + if (param_err) + return param_err; set_bit(FF_MOD1_IS_USED, core_effect->flags); param_err = make_condition_modifier(iforce, mod2_chunk, - is_update, + old != NULL, effect->u.condition[1].right_saturation, effect->u.condition[1].left_saturation, effect->u.condition[1].right_coeff, effect->u.condition[1].left_coeff, effect->u.condition[1].deadband, effect->u.condition[1].center); - if (param_err) return param_err; + if (param_err) + return param_err; set_bit(FF_MOD2_IS_USED, core_effect->flags); } - if (!is_update || need_core(iforce, effect)) { + if (!old || need_core(old, effect)) { core_err = make_core(iforce, effect->id, mod1_chunk->start, mod2_chunk->start, type, 0xc0, diff --git a/drivers/input/joystick/iforce/iforce-main.c b/drivers/input/joystick/iforce/iforce-main.c index b4914e7231f..24c684bc633 100644 --- a/drivers/input/joystick/iforce/iforce-main.c +++ b/drivers/input/joystick/iforce/iforce-main.c @@ -83,103 +83,57 @@ static struct iforce_device iforce_device[] = { { 0x0000, 0x0000, "Unknown I-Force Device [%04x:%04x]", btn_joystick, abs_joystick, ff_iforce } }; - - -static int iforce_input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value) +static int iforce_playback(struct input_dev *dev, int effect_id, int value) { struct iforce* iforce = dev->private; - unsigned char data[3]; - - if (type != EV_FF) - return -1; - - switch (code) { - - case FF_GAIN: - - data[0] = value >> 9; - iforce_send_packet(iforce, FF_CMD_GAIN, data); - - return 0; - - case FF_AUTOCENTER: + struct iforce_core_effect *core_effect = &iforce->core_effects[effect_id]; - data[0] = 0x03; - data[1] = value >> 9; - iforce_send_packet(iforce, FF_CMD_AUTOCENTER, data); + if (value > 0) + set_bit(FF_CORE_SHOULD_PLAY, core_effect->flags); + else + clear_bit(FF_CORE_SHOULD_PLAY, core_effect->flags); - data[0] = 0x04; - data[1] = 0x01; - iforce_send_packet(iforce, FF_CMD_AUTOCENTER, data); + iforce_control_playback(iforce, effect_id, value); + return 0; +} - return 0; +static void iforce_set_gain(struct input_dev *dev, u16 gain) +{ + struct iforce* iforce = dev->private; + unsigned char data[3]; - default: /* Play or stop an effect */ + data[0] = gain >> 9; + iforce_send_packet(iforce, FF_CMD_GAIN, data); +} - if (!CHECK_OWNERSHIP(code, iforce)) { - return -1; - } - if (value > 0) { - set_bit(FF_CORE_SHOULD_PLAY, iforce->core_effects[code].flags); - } - else { - clear_bit(FF_CORE_SHOULD_PLAY, iforce->core_effects[code].flags); - } +static void iforce_set_autocenter(struct input_dev *dev, u16 magnitude) +{ + struct iforce* iforce = dev->private; + unsigned char data[3]; - iforce_control_playback(iforce, code, value); - return 0; - } + data[0] = 0x03; + data[1] = magnitude >> 9; + iforce_send_packet(iforce, FF_CMD_AUTOCENTER, data); - return -1; + data[0] = 0x04; + data[1] = 0x01; + iforce_send_packet(iforce, FF_CMD_AUTOCENTER, data); } /* * Function called when an ioctl is performed on the event dev entry. * It uploads an effect to the device */ -static int iforce_upload_effect(struct input_dev *dev, struct ff_effect *effect) +static int iforce_upload_effect(struct input_dev *dev, struct ff_effect *effect, struct ff_effect *old) { struct iforce* iforce = dev->private; - int id; + struct iforce_core_effect *core_effect = &iforce->core_effects[effect->id]; int ret; - int is_update; - -/* Check this effect type is supported by this device */ - if (!test_bit(effect->type, iforce->dev->ffbit)) - return -EINVAL; - -/* - * If we want to create a new effect, get a free id - */ - if (effect->id == -1) { - - for (id = 0; id < FF_EFFECTS_MAX; ++id) - if (!test_and_set_bit(FF_CORE_IS_USED, iforce->core_effects[id].flags)) - break; - - if (id == FF_EFFECTS_MAX || id >= iforce->dev->ff_effects_max) - return -ENOMEM; - - effect->id = id; - iforce->core_effects[id].owner = current->pid; - iforce->core_effects[id].flags[0] = (1 << FF_CORE_IS_USED); /* Only IS_USED bit must be set */ - - is_update = FALSE; - } - else { - /* We want to update an effect */ - if (!CHECK_OWNERSHIP(effect->id, iforce)) - return -EACCES; - - /* Parameter type cannot be updated */ - if (effect->type != iforce->core_effects[effect->id].effect.type) - return -EINVAL; + if (__test_and_set_bit(FF_CORE_IS_USED, core_effect->flags)) { /* Check the effect is not already being updated */ - if (test_bit(FF_CORE_UPDATE, iforce->core_effects[effect->id].flags)) + if (test_bit(FF_CORE_UPDATE, core_effect->flags)) return -EAGAIN; - - is_update = TRUE; } /* @@ -188,28 +142,28 @@ static int iforce_upload_effect(struct input_dev *dev, struct ff_effect *effect) switch (effect->type) { case FF_PERIODIC: - ret = iforce_upload_periodic(iforce, effect, is_update); + ret = iforce_upload_periodic(iforce, effect, old); break; case FF_CONSTANT: - ret = iforce_upload_constant(iforce, effect, is_update); + ret = iforce_upload_constant(iforce, effect, old); break; case FF_SPRING: case FF_DAMPER: - ret = iforce_upload_condition(iforce, effect, is_update); + ret = iforce_upload_condition(iforce, effect, old); break; default: return -EINVAL; } + if (ret == 0) { /* A packet was sent, forbid new updates until we are notified * that the packet was updated */ - set_bit(FF_CORE_UPDATE, iforce->core_effects[effect->id].flags); + set_bit(FF_CORE_UPDATE, core_effect->flags); } - iforce->core_effects[effect->id].effect = *effect; return ret; } @@ -219,20 +173,9 @@ static int iforce_upload_effect(struct input_dev *dev, struct ff_effect *effect) */ static int iforce_erase_effect(struct input_dev *dev, int effect_id) { - struct iforce* iforce = dev->private; + struct iforce *iforce = dev->private; + struct iforce_core_effect *core_effect = &iforce->core_effects[effect_id]; int err = 0; - struct iforce_core_effect* core_effect; - - if (effect_id < 0 || effect_id >= FF_EFFECTS_MAX) - return -EINVAL; - - core_effect = &iforce->core_effects[effect_id]; - - /* Check who is trying to erase this effect */ - if (core_effect->owner != current->pid) { - printk(KERN_WARNING "iforce-main.c: %d tried to erase an effect belonging to %d\n", current->pid, core_effect->owner); - return -EACCES; - } if (test_bit(FF_MOD1_IS_USED, core_effect->flags)) err = release_resource(&core_effect->mod1_chunk); @@ -240,7 +183,7 @@ static int iforce_erase_effect(struct input_dev *dev, int effect_id) if (!err && test_bit(FF_MOD2_IS_USED, core_effect->flags)) err = release_resource(&core_effect->mod2_chunk); - /*TODO: remember to change that if more FF_MOD* bits are added */ + /* TODO: remember to change that if more FF_MOD* bits are added */ core_effect->flags[0] = 0; return err; @@ -260,52 +203,31 @@ static int iforce_open(struct input_dev *dev) #endif } - /* Enable force feedback */ - iforce_send_packet(iforce, FF_CMD_ENABLE, "\004"); + if (test_bit(EV_FF, dev->evbit)) { + /* Enable force feedback */ + iforce_send_packet(iforce, FF_CMD_ENABLE, "\004"); + } return 0; } -static int iforce_flush(struct input_dev *dev, struct file *file) +static void iforce_release(struct input_dev *dev) { struct iforce *iforce = dev->private; int i; - /* Erase all effects this process owns */ - for (i=0; iff_effects_max; ++i) { - - if (test_bit(FF_CORE_IS_USED, iforce->core_effects[i].flags) && - current->pid == iforce->core_effects[i].owner) { - - /* Stop effect */ - input_report_ff(dev, i, 0); - - /* Free ressources assigned to effect */ - if (iforce_erase_effect(dev, i)) { - printk(KERN_WARNING "iforce_flush: erase effect %d failed\n", i); + if (test_bit(EV_FF, dev->evbit)) { + /* Check: no effects should be present in memory */ + for (i = 0; i < dev->ff->max_effects; i++) { + if (test_bit(FF_CORE_IS_USED, iforce->core_effects[i].flags)) { + printk(KERN_WARNING "iforce_release: Device still owns effects\n"); + break; } } + /* Disable force feedback playback */ + iforce_send_packet(iforce, FF_CMD_ENABLE, "\001"); } - return 0; -} - -static void iforce_release(struct input_dev *dev) -{ - struct iforce *iforce = dev->private; - int i; - - /* Check: no effect should be present in memory */ - for (i=0; iff_effects_max; ++i) { - if (test_bit(FF_CORE_IS_USED, iforce->core_effects[i].flags)) - break; - } - if (iff_effects_max) { - printk(KERN_WARNING "iforce_release: Device still owns effects\n"); - } - - /* Disable force feedback playback */ - iforce_send_packet(iforce, FF_CMD_ENABLE, "\001"); switch (iforce->bus) { #ifdef CONFIG_JOYSTICK_IFORCE_USB @@ -342,8 +264,10 @@ void iforce_delete_device(struct iforce *iforce) int iforce_init_device(struct iforce *iforce) { struct input_dev *input_dev; + struct ff_device *ff; unsigned char c[] = "CEOV"; - int i; + int i, error; + int ff_effects = 0; input_dev = input_allocate_device(); if (!input_dev) @@ -378,11 +302,6 @@ int iforce_init_device(struct iforce *iforce) input_dev->name = "Unknown I-Force device"; input_dev->open = iforce_open; input_dev->close = iforce_release; - input_dev->flush = iforce_flush; - input_dev->event = iforce_input_event; - input_dev->upload_effect = iforce_upload_effect; - input_dev->erase_effect = iforce_erase_effect; - input_dev->ff_effects_max = 10; /* * On-device memory allocation. @@ -430,15 +349,15 @@ int iforce_init_device(struct iforce *iforce) printk(KERN_WARNING "iforce-main.c: Device does not respond to id packet B\n"); if (!iforce_get_id_packet(iforce, "N")) - iforce->dev->ff_effects_max = iforce->edata[1]; + ff_effects = iforce->edata[1]; else printk(KERN_WARNING "iforce-main.c: Device does not respond to id packet N\n"); /* Check if the device can store more effects than the driver can really handle */ - if (iforce->dev->ff_effects_max > FF_EFFECTS_MAX) { - printk(KERN_WARNING "input??: Device can handle %d effects, but N_EFFECTS_MAX is set to %d in iforce.h\n", - iforce->dev->ff_effects_max, FF_EFFECTS_MAX); - iforce->dev->ff_effects_max = FF_EFFECTS_MAX; + if (ff_effects > IFORCE_EFFECTS_MAX) { + printk(KERN_WARNING "iforce: Limiting number of effects to %d (device reports %d)\n", + IFORCE_EFFECTS_MAX, ff_effects); + ff_effects = IFORCE_EFFECTS_MAX; } /* @@ -472,12 +391,10 @@ int iforce_init_device(struct iforce *iforce) * Set input device bitfields and ranges. */ - input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_ABS) | BIT(EV_FF) | BIT(EV_FF_STATUS); + input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_ABS) | BIT(EV_FF_STATUS); - for (i = 0; iforce->type->btn[i] >= 0; i++) { - signed short t = iforce->type->btn[i]; - set_bit(t, input_dev->keybit); - } + for (i = 0; iforce->type->btn[i] >= 0; i++) + set_bit(iforce->type->btn[i], input_dev->keybit); set_bit(BTN_DEAD, input_dev->keybit); for (i = 0; iforce->type->abs[i] >= 0; i++) { @@ -516,9 +433,24 @@ int iforce_init_device(struct iforce *iforce) } } - for (i = 0; iforce->type->ff[i] >= 0; i++) - set_bit(iforce->type->ff[i], input_dev->ffbit); + if (ff_effects) { + for (i = 0; iforce->type->ff[i] >= 0; i++) + set_bit(iforce->type->ff[i], input_dev->ffbit); + + error = input_ff_create(input_dev, ff_effects); + if (error) { + input_free_device(input_dev); + return error; + } + + ff = input_dev->ff; + ff->upload = iforce_upload_effect; + ff->erase = iforce_erase_effect; + ff->set_gain = iforce_set_gain; + ff->set_autocenter = iforce_set_autocenter; + ff->playback = iforce_playback; + } /* * Register input device. */ diff --git a/drivers/input/joystick/iforce/iforce-packets.c b/drivers/input/joystick/iforce/iforce-packets.c index 76cb1f88f4e..8632d47a7fb 100644 --- a/drivers/input/joystick/iforce/iforce-packets.c +++ b/drivers/input/joystick/iforce/iforce-packets.c @@ -140,7 +140,10 @@ static int mark_core_as_ready(struct iforce *iforce, unsigned short addr) { int i; - for (i = 0; i < iforce->dev->ff_effects_max; ++i) { + if (!iforce->dev->ff) + return 0; + + for (i = 0; i < iforce->dev->ff->max_effects; ++i) { if (test_bit(FF_CORE_IS_USED, iforce->core_effects[i].flags) && (iforce->core_effects[i].mod1_chunk.start == addr || iforce->core_effects[i].mod2_chunk.start == addr)) { @@ -229,19 +232,17 @@ void iforce_process_packet(struct iforce *iforce, u16 cmd, unsigned char *data, i = data[1] & 0x7f; if (data[1] & 0x80) { if (!test_and_set_bit(FF_CORE_IS_PLAYED, iforce->core_effects[i].flags)) { - /* Report play event */ - input_report_ff_status(dev, i, FF_STATUS_PLAYING); + /* Report play event */ + input_report_ff_status(dev, i, FF_STATUS_PLAYING); } - } - else if (test_and_clear_bit(FF_CORE_IS_PLAYED, iforce->core_effects[i].flags)) { + } else if (test_and_clear_bit(FF_CORE_IS_PLAYED, iforce->core_effects[i].flags)) { /* Report stop event */ input_report_ff_status(dev, i, FF_STATUS_STOPPED); } if (LO(cmd) > 3) { int j; - for (j=3; j= 0 && \ - test_bit(FF_CORE_IS_USED, (iforce)->core_effects[(i)].flags) && \ - (current->pid == 0 || \ - (iforce)->core_effects[(i)].owner == current->pid)) - struct iforce_core_effect { /* Information about where modifiers are stored in the device's memory */ struct resource mod1_chunk; struct resource mod2_chunk; unsigned long flags[NBITS(FF_MODCORE_MAX)]; - pid_t owner; - /* Used to keep track of parameters of an effect. They are needed - * to know what parts of an effect changed in an update operation. - * We try to send only parameter packets if possible, as sending - * effect parameter requires the effect to be stoped and restarted - */ - struct ff_effect effect; }; #define FF_CMD_EFFECT 0x010e @@ -145,7 +129,7 @@ struct iforce { /* Force Feedback */ wait_queue_head_t wait; struct resource device_memory; - struct iforce_core_effect core_effects[FF_EFFECTS_MAX]; + struct iforce_core_effect core_effects[IFORCE_EFFECTS_MAX]; struct mutex mem_mutex; }; @@ -182,9 +166,9 @@ void iforce_dump_packet(char *msg, u16 cmd, unsigned char *data) ; int iforce_get_id_packet(struct iforce *iforce, char *packet); /* iforce-ff.c */ -int iforce_upload_periodic(struct iforce*, struct ff_effect*, int is_update); -int iforce_upload_constant(struct iforce*, struct ff_effect*, int is_update); -int iforce_upload_condition(struct iforce*, struct ff_effect*, int is_update); +int iforce_upload_periodic(struct iforce *, struct ff_effect *, struct ff_effect *); +int iforce_upload_constant(struct iforce *, struct ff_effect *, struct ff_effect *); +int iforce_upload_condition(struct iforce *, struct ff_effect *, struct ff_effect *); /* Public variables */ extern struct serio_driver iforce_serio_drv; -- cgit v1.2.3 From ff462551235d8d7d843a005950bc90924fcedede Mon Sep 17 00:00:00 2001 From: Anssi Hannula Date: Wed, 19 Jul 2006 01:41:09 -0400 Subject: Input: uinput - switch to the new FF interface The userspace interface of the force feedback part is changed and documentation in uinput.h is updated accordingly. MODULE_VERSION is also incremented to reflect the revision. Signed-off-by: Anssi Hannula Signed-off-by: Dmitry Torokhov --- drivers/input/misc/uinput.c | 67 ++++++++++++++++++++++++++++++++++----------- 1 file changed, 51 insertions(+), 16 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/misc/uinput.c b/drivers/input/misc/uinput.c index d723e9ad7c4..9516439b7c7 100644 --- a/drivers/input/misc/uinput.c +++ b/drivers/input/misc/uinput.c @@ -20,6 +20,9 @@ * Author: Aristeu Sergio Rozanski Filho * * Changes/Revisions: + * 0.3 09/04/2006 (Anssi Hannula ) + * - updated ff support for the changes in kernel interface + * - added MODULE_VERSION * 0.2 16/10/2004 (Micah Dowty ) * - added force feedback support * - added UI_SET_PHYS @@ -107,18 +110,31 @@ static int uinput_request_submit(struct input_dev *dev, struct uinput_request *r return request->retval; } -static int uinput_dev_upload_effect(struct input_dev *dev, struct ff_effect *effect) +static void uinput_dev_set_gain(struct input_dev *dev, u16 gain) +{ + uinput_dev_event(dev, EV_FF, FF_GAIN, gain); +} + +static void uinput_dev_set_autocenter(struct input_dev *dev, u16 magnitude) +{ + uinput_dev_event(dev, EV_FF, FF_AUTOCENTER, magnitude); +} + +static int uinput_dev_playback(struct input_dev *dev, int effect_id, int value) +{ + return uinput_dev_event(dev, EV_FF, effect_id, value); +} + +static int uinput_dev_upload_effect(struct input_dev *dev, struct ff_effect *effect, struct ff_effect *old) { struct uinput_request request; int retval; - if (!test_bit(EV_FF, dev->evbit)) - return -ENOSYS; - request.id = -1; init_completion(&request.done); request.code = UI_FF_UPLOAD; - request.u.effect = effect; + request.u.upload.effect = effect; + request.u.upload.old = old; retval = uinput_request_reserve_slot(dev->private, &request); if (!retval) @@ -168,6 +184,7 @@ static void uinput_destroy_device(struct uinput_device *udev) static int uinput_create_device(struct uinput_device *udev) { + struct input_dev *dev = udev->dev; int error; if (udev->state != UIST_SETUP_COMPLETE) { @@ -175,15 +192,29 @@ static int uinput_create_device(struct uinput_device *udev) return -EINVAL; } - error = input_register_device(udev->dev); - if (error) { - uinput_destroy_device(udev); - return error; + if (udev->ff_effects_max) { + error = input_ff_create(dev, udev->ff_effects_max); + if (error) + goto fail1; + + dev->ff->upload = uinput_dev_upload_effect; + dev->ff->erase = uinput_dev_erase_effect; + dev->ff->playback = uinput_dev_playback; + dev->ff->set_gain = uinput_dev_set_gain; + dev->ff->set_autocenter = uinput_dev_set_autocenter; } + error = input_register_device(udev->dev); + if (error) + goto fail2; + udev->state = UIST_CREATED; return 0; + + fail2: input_ff_destroy(dev); + fail1: uinput_destroy_device(udev); + return error; } static int uinput_open(struct inode *inode, struct file *file) @@ -243,8 +274,6 @@ static int uinput_allocate_device(struct uinput_device *udev) return -ENOMEM; udev->dev->event = uinput_dev_event; - udev->dev->upload_effect = uinput_dev_upload_effect; - udev->dev->erase_effect = uinput_dev_erase_effect; udev->dev->private = udev; return 0; @@ -278,6 +307,8 @@ static int uinput_setup_device(struct uinput_device *udev, const char __user *bu goto exit; } + udev->ff_effects_max = user_dev->ff_effects_max; + size = strnlen(user_dev->name, UINPUT_MAX_NAME_SIZE) + 1; if (!size) { retval = -EINVAL; @@ -296,7 +327,6 @@ static int uinput_setup_device(struct uinput_device *udev, const char __user *bu dev->id.vendor = user_dev->id.vendor; dev->id.product = user_dev->id.product; dev->id.version = user_dev->id.version; - dev->ff_effects_max = user_dev->ff_effects_max; size = sizeof(int) * (ABS_MAX + 1); memcpy(dev->absmax, user_dev->absmax, size); @@ -525,12 +555,17 @@ static long uinput_ioctl(struct file *file, unsigned int cmd, unsigned long arg) break; } req = uinput_request_find(udev, ff_up.request_id); - if (!(req && req->code == UI_FF_UPLOAD && req->u.effect)) { + if (!(req && req->code == UI_FF_UPLOAD && req->u.upload.effect)) { retval = -EINVAL; break; } ff_up.retval = 0; - memcpy(&ff_up.effect, req->u.effect, sizeof(struct ff_effect)); + memcpy(&ff_up.effect, req->u.upload.effect, sizeof(struct ff_effect)); + if (req->u.upload.old) + memcpy(&ff_up.old, req->u.upload.old, sizeof(struct ff_effect)); + else + memset(&ff_up.old, 0, sizeof(struct ff_effect)); + if (copy_to_user(p, &ff_up, sizeof(ff_up))) { retval = -EFAULT; break; @@ -561,12 +596,11 @@ static long uinput_ioctl(struct file *file, unsigned int cmd, unsigned long arg) break; } req = uinput_request_find(udev, ff_up.request_id); - if (!(req && req->code == UI_FF_UPLOAD && req->u.effect)) { + if (!(req && req->code == UI_FF_UPLOAD && req->u.upload.effect)) { retval = -EINVAL; break; } req->retval = ff_up.retval; - memcpy(req->u.effect, &ff_up.effect, sizeof(struct ff_effect)); uinput_request_done(udev, req); break; @@ -622,6 +656,7 @@ static void __exit uinput_exit(void) MODULE_AUTHOR("Aristeu Sergio Rozanski Filho"); MODULE_DESCRIPTION("User level driver support for input subsystem"); MODULE_LICENSE("GPL"); +MODULE_VERSION("0.3"); module_init(uinput_init); module_exit(uinput_exit); -- cgit v1.2.3 From 6b50d8b862284929314e9ff09e5b1cce2c43d32b Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Sat, 5 Aug 2006 00:27:00 -0400 Subject: Input: elo - handle input_register_device() failures Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/elo.c | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/touchscreen/elo.c b/drivers/input/touchscreen/elo.c index c86a2eb310f..b7ac016219d 100644 --- a/drivers/input/touchscreen/elo.c +++ b/drivers/input/touchscreen/elo.c @@ -187,11 +187,13 @@ static irqreturn_t elo_interrupt(struct serio *serio, static void elo_disconnect(struct serio *serio) { - struct elo* elo = serio_get_drvdata(serio); + struct elo *elo = serio_get_drvdata(serio); + input_get_device(elo->dev); input_unregister_device(elo->dev); serio_close(serio); serio_set_drvdata(serio, NULL); + input_put_device(elo->dev); kfree(elo); } @@ -211,7 +213,7 @@ static int elo_connect(struct serio *serio, struct serio_driver *drv) input_dev = input_allocate_device(); if (!elo || !input_dev) { err = -ENOMEM; - goto fail; + goto fail1; } elo->serio = serio; @@ -257,13 +259,17 @@ static int elo_connect(struct serio *serio, struct serio_driver *drv) err = serio_open(serio, drv); if (err) - goto fail; + goto fail2; + + err = input_register_device(elo->dev); + if (err) + goto fail3; - input_register_device(elo->dev); return 0; - fail: serio_set_drvdata(serio, NULL); - input_free_device(input_dev); + fail3: serio_close(serio); + fail2: serio_set_drvdata(serio, NULL); + fail1: input_free_device(input_dev); kfree(elo); return err; } -- cgit v1.2.3 From 1ce316efb55a1497d07d518853e60a4356abceb6 Mon Sep 17 00:00:00 2001 From: Shaun Jackman Date: Sat, 5 Aug 2006 00:27:59 -0400 Subject: Input: elo - fix checksum calculation Fix 10-byte protocol checksum calculation and do not discard packet early unless it is missing lead in byte. Signed-off-by: Shaun Jackman Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/elo.c | 55 ++++++++++++++++++++++------------------- 1 file changed, 29 insertions(+), 26 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/touchscreen/elo.c b/drivers/input/touchscreen/elo.c index b7ac016219d..fe6e1a42867 100644 --- a/drivers/input/touchscreen/elo.c +++ b/drivers/input/touchscreen/elo.c @@ -34,7 +34,11 @@ MODULE_LICENSE("GPL"); * Definitions & global arrays. */ -#define ELO_MAX_LENGTH 10 +#define ELO_MAX_LENGTH 10 + +#define ELO10_LEAD_BYTE 'U' + +#define ELO10_TOUCH_PACKET 'T' /* * Per-touchscreen data. @@ -50,44 +54,43 @@ struct elo { char phys[32]; }; -static void elo_process_data_10(struct elo* elo, unsigned char data, struct pt_regs *regs) +static void elo_process_data_10(struct elo *elo, unsigned char data, struct pt_regs *regs) { struct input_dev *dev = elo->dev; - elo->csum += elo->data[elo->idx] = data; - + elo->data[elo->idx] = data; switch (elo->idx++) { - case 0: - if (data != 'U') { - elo->idx = 0; - elo->csum = 0; - } - break; - - case 1: - if (data != 'T') { + elo->csum = 0xaa; + if (data != ELO10_LEAD_BYTE) { + pr_debug("elo: unsynchronized data: 0x%02x\n", data); elo->idx = 0; - elo->csum = 0; } break; case 9: - if (elo->csum) { - input_regs(dev, regs); - input_report_abs(dev, ABS_X, (elo->data[4] << 8) | elo->data[3]); - input_report_abs(dev, ABS_Y, (elo->data[6] << 8) | elo->data[5]); - input_report_abs(dev, ABS_PRESSURE, (elo->data[8] << 8) | elo->data[7]); - input_report_key(dev, BTN_TOUCH, elo->data[8] || elo->data[7]); - input_sync(dev); - } elo->idx = 0; - elo->csum = 0; + if (data != elo->csum) { + pr_debug("elo: bad checksum: 0x%02x, expected 0x%02x\n", + data, elo->csum); + break; + } + if (elo->data[1] != ELO10_TOUCH_PACKET) { + pr_debug(elo: "unexpected packet: 0x%02x\n", elo->data[1]); + break; + } + input_regs(dev, regs); + input_report_abs(dev, ABS_X, (elo->data[4] << 8) | elo->data[3]); + input_report_abs(dev, ABS_Y, (elo->data[6] << 8) | elo->data[5]); + input_report_abs(dev, ABS_PRESSURE, (elo->data[8] << 8) | elo->data[7]); + input_report_key(dev, BTN_TOUCH, elo->data[8] || elo->data[7]); + input_sync(dev); break; } + elo->csum += data; } -static void elo_process_data_6(struct elo* elo, unsigned char data, struct pt_regs *regs) +static void elo_process_data_6(struct elo *elo, unsigned char data, struct pt_regs *regs) { struct input_dev *dev = elo->dev; @@ -135,7 +138,7 @@ static void elo_process_data_6(struct elo* elo, unsigned char data, struct pt_re } } -static void elo_process_data_3(struct elo* elo, unsigned char data, struct pt_regs *regs) +static void elo_process_data_3(struct elo *elo, unsigned char data, struct pt_regs *regs) { struct input_dev *dev = elo->dev; @@ -161,7 +164,7 @@ static void elo_process_data_3(struct elo* elo, unsigned char data, struct pt_re static irqreturn_t elo_interrupt(struct serio *serio, unsigned char data, unsigned int flags, struct pt_regs *regs) { - struct elo* elo = serio_get_drvdata(serio); + struct elo *elo = serio_get_drvdata(serio); switch(elo->id) { case 0: -- cgit v1.2.3 From fae3006e4b42eafbed4af714e93cf6c2b92ff793 Mon Sep 17 00:00:00 2001 From: Shaun Jackman Date: Sat, 5 Aug 2006 00:29:49 -0400 Subject: Input: elo - add support for non-pressure-sensitive touchscreens - Use the touch status bit rather than the pressure bits to distinguish a BTN_TOUCH event. Non-pressure-sensitive touchscreens always report full pressure - Report ABS_PRESSURE information only if the touchscreen supports it Signed-off-by: Shaun Jackman Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/elo.c | 124 ++++++++++++++++++++++++++++++++++------ 1 file changed, 107 insertions(+), 17 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/touchscreen/elo.c b/drivers/input/touchscreen/elo.c index fe6e1a42867..ab565335ee4 100644 --- a/drivers/input/touchscreen/elo.c +++ b/drivers/input/touchscreen/elo.c @@ -23,6 +23,7 @@ #include #include #include +#include #define DRIVER_DESC "Elo serial touchscreen driver" @@ -36,9 +37,17 @@ MODULE_LICENSE("GPL"); #define ELO_MAX_LENGTH 10 +#define ELO10_PACKET_LEN 8 +#define ELO10_TOUCH 0x03 +#define ELO10_PRESSURE 0x80 + #define ELO10_LEAD_BYTE 'U' +#define ELO10_ID_CMD 'i' + #define ELO10_TOUCH_PACKET 'T' +#define ELO10_ACK_PACKET 'A' +#define ELI10_ID_PACKET 'I' /* * Per-touchscreen data. @@ -47,10 +56,14 @@ MODULE_LICENSE("GPL"); struct elo { struct input_dev *dev; struct serio *serio; + struct mutex cmd_mutex; + struct completion cmd_done; int id; int idx; + unsigned char expected_packet; unsigned char csum; unsigned char data[ELO_MAX_LENGTH]; + unsigned char response[ELO10_PACKET_LEN]; char phys[32]; }; @@ -75,16 +88,29 @@ static void elo_process_data_10(struct elo *elo, unsigned char data, struct pt_r data, elo->csum); break; } - if (elo->data[1] != ELO10_TOUCH_PACKET) { - pr_debug(elo: "unexpected packet: 0x%02x\n", elo->data[1]); + if (elo->data[1] != elo->expected_packet) { + if (elo->data[1] != ELO10_TOUCH_PACKET) + pr_debug("elo: unexpected packet: 0x%02x\n", + elo->data[1]); break; } - input_regs(dev, regs); - input_report_abs(dev, ABS_X, (elo->data[4] << 8) | elo->data[3]); - input_report_abs(dev, ABS_Y, (elo->data[6] << 8) | elo->data[5]); - input_report_abs(dev, ABS_PRESSURE, (elo->data[8] << 8) | elo->data[7]); - input_report_key(dev, BTN_TOUCH, elo->data[8] || elo->data[7]); - input_sync(dev); + if (likely(elo->data[1] == ELO10_TOUCH_PACKET)) { + input_regs(dev, regs); + input_report_abs(dev, ABS_X, (elo->data[4] << 8) | elo->data[3]); + input_report_abs(dev, ABS_Y, (elo->data[6] << 8) | elo->data[5]); + if (elo->data[2] & ELO10_PRESSURE) + input_report_abs(dev, ABS_PRESSURE, + (elo->data[8] << 8) | elo->data[7]); + input_report_key(dev, BTN_TOUCH, elo->data[2] & ELO10_TOUCH); + input_sync(dev); + } else if (elo->data[1] == ELO10_ACK_PACKET) { + if (elo->data[2] == '0') + elo->expected_packet = ELO10_TOUCH_PACKET; + complete(&elo->cmd_done); + } else { + memcpy(elo->response, &elo->data[1], ELO10_PACKET_LEN); + elo->expected_packet = ELO10_ACK_PACKET; + } break; } elo->csum += data; @@ -184,6 +210,68 @@ static irqreturn_t elo_interrupt(struct serio *serio, return IRQ_HANDLED; } +static int elo_command_10(struct elo *elo, unsigned char *packet) +{ + int rc = -1; + int i; + unsigned char csum = 0xaa + ELO10_LEAD_BYTE; + + mutex_lock(&elo->cmd_mutex); + + serio_pause_rx(elo->serio); + elo->expected_packet = toupper(packet[0]); + init_completion(&elo->cmd_done); + serio_continue_rx(elo->serio); + + if (serio_write(elo->serio, ELO10_LEAD_BYTE)) + goto out; + + for (i = 0; i < ELO10_PACKET_LEN; i++) { + csum += packet[i]; + if (serio_write(elo->serio, packet[i])) + goto out; + } + + if (serio_write(elo->serio, csum)) + goto out; + + wait_for_completion_timeout(&elo->cmd_done, HZ); + + if (elo->expected_packet == ELO10_TOUCH_PACKET) { + /* We are back in reporting mode, the command was ACKed */ + memcpy(packet, elo->response, ELO10_PACKET_LEN); + rc = 0; + } + + out: + mutex_unlock(&elo->cmd_mutex); + return rc; +} + +static int elo_setup_10(struct elo *elo) +{ + static const char *elo_types[] = { "Accu", "Dura", "Intelli", "Carroll" }; + struct input_dev *dev = elo->dev; + unsigned char packet[ELO10_PACKET_LEN] = { ELO10_ID_CMD }; + + if (elo_command_10(elo, packet)) + return -1; + + dev->id.version = (packet[5] << 8) | packet[4]; + + input_set_abs_params(dev, ABS_X, 96, 4000, 0, 0); + input_set_abs_params(dev, ABS_Y, 96, 4000, 0, 0); + if (packet[3] & ELO10_PRESSURE) + input_set_abs_params(dev, ABS_PRESSURE, 0, 255, 0, 0); + + printk(KERN_INFO "elo: %sTouch touchscreen, fw: %02x.%02x, " + "features: %x02x, controller: 0x%02x\n", + elo_types[(packet[1] -'0') & 0x03], + packet[5], packet[4], packet[3], packet[7]); + + return 0; +} + /* * elo_disconnect() is the opposite of elo_connect() */ @@ -222,6 +310,9 @@ static int elo_connect(struct serio *serio, struct serio_driver *drv) elo->serio = serio; elo->id = serio->id.id; elo->dev = input_dev; + elo->expected_packet = ELO10_TOUCH_PACKET; + mutex_init(&elo->cmd_mutex); + init_completion(&elo->cmd_done); snprintf(elo->phys, sizeof(elo->phys), "%s/input0", serio->phys); input_dev->private = elo; @@ -236,12 +327,17 @@ static int elo_connect(struct serio *serio, struct serio_driver *drv) input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_ABS); input_dev->keybit[LONG(BTN_TOUCH)] = BIT(BTN_TOUCH); + serio_set_drvdata(serio, elo); + err = serio_open(serio, drv); + if (err) + goto fail2; + switch (elo->id) { case 0: /* 10-byte protocol */ - input_set_abs_params(input_dev, ABS_X, 96, 4000, 0, 0); - input_set_abs_params(input_dev, ABS_Y, 96, 4000, 0, 0); - input_set_abs_params(input_dev, ABS_PRESSURE, 0, 255, 0, 0); + if (elo_setup_10(elo)) + goto fail3; + break; case 1: /* 6-byte protocol */ @@ -258,12 +354,6 @@ static int elo_connect(struct serio *serio, struct serio_driver *drv) break; } - serio_set_drvdata(serio, elo); - - err = serio_open(serio, drv); - if (err) - goto fail2; - err = input_register_device(elo->dev); if (err) goto fail3; -- cgit v1.2.3 From ee4799997950e81437ef9055a4b104099e3272c4 Mon Sep 17 00:00:00 2001 From: Rick Koch Date: Sat, 5 Aug 2006 00:32:18 -0400 Subject: Input: add driver for Penmount serial touchscreens Signed-off-by: Rick Koch Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/Kconfig | 12 +++ drivers/input/touchscreen/Makefile | 1 + drivers/input/touchscreen/penmount.c | 185 +++++++++++++++++++++++++++++++++++ 3 files changed, 198 insertions(+) create mode 100644 drivers/input/touchscreen/penmount.c (limited to 'drivers/input') diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index b1b14f8d4dd..fa038126d50 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -108,4 +108,16 @@ config TOUCHSCREEN_HP600 To compile this driver as a module, choose M here: the module will be called hp680_ts_input. +config TOUCHSCREEN_PENMOUNT + tristate "Penmount serial touchscreen" + select SERIO + help + Say Y here if you have a Penmount serial touchscreen connected to + your system. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called penmount. + endif diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index 5e5557c4312..72e7be8097a 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -12,3 +12,4 @@ obj-$(CONFIG_TOUCHSCREEN_ELO) += elo.o obj-$(CONFIG_TOUCHSCREEN_MTOUCH) += mtouch.o obj-$(CONFIG_TOUCHSCREEN_MK712) += mk712.o obj-$(CONFIG_TOUCHSCREEN_HP600) += hp680_ts_input.o +obj-$(CONFIG_TOUCHSCREEN_PENMOUNT) += penmount.o diff --git a/drivers/input/touchscreen/penmount.c b/drivers/input/touchscreen/penmount.c new file mode 100644 index 00000000000..f7370109d43 --- /dev/null +++ b/drivers/input/touchscreen/penmount.c @@ -0,0 +1,185 @@ +/* + * Penmount serial touchscreen driver + * + * Copyright (c) 2006 Rick Koch + * + * Based on ELO driver (drivers/input/touchscreen/elo.c) + * Copyright (c) 2004 Vojtech Pavlik + */ + +/* + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include + +#define DRIVER_DESC "Penmount serial touchscreen driver" + +MODULE_AUTHOR("Rick Koch "); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); + +/* + * Definitions & global arrays. + */ + +#define PM_MAX_LENGTH 5 + +/* + * Per-touchscreen data. + */ + +struct pm { + struct input_dev *dev; + struct serio *serio; + int idx; + unsigned char data[PM_MAX_LENGTH]; + char phys[32]; +}; + +static irqreturn_t pm_interrupt(struct serio *serio, + unsigned char data, unsigned int flags, struct pt_regs *regs) +{ + struct pm *pm = serio_get_drvdata(serio); + struct input_dev *dev = pm->dev; + + pm->data[pm->idx] = data; + + if (pm->data[0] & 0x80) { + if (PM_MAX_LENGTH == ++pm->idx) { + input_regs(dev, regs); + input_report_abs(dev, ABS_X, pm->data[2] * 128 + pm->data[1]); + input_report_abs(dev, ABS_Y, pm->data[4] * 128 + pm->data[3]); + input_report_key(dev, BTN_TOUCH, !!(pm->data[0] & 0x40)); + input_sync(dev); + pm->idx = 0; + } + } + + return IRQ_HANDLED; +} + +/* + * pm_disconnect() is the opposite of pm_connect() + */ + +static void pm_disconnect(struct serio *serio) +{ + struct pm *pm = serio_get_drvdata(serio); + + input_get_device(pm->dev); + input_unregister_device(pm->dev); + serio_close(serio); + serio_set_drvdata(serio, NULL); + input_put_device(pm->dev); + kfree(pm); +} + +/* + * pm_connect() is the routine that is called when someone adds a + * new serio device that supports Gunze protocol and registers it as + * an input device. + */ + +static int pm_connect(struct serio *serio, struct serio_driver *drv) +{ + struct pm *pm; + struct input_dev *input_dev; + int err; + + pm = kzalloc(sizeof(struct pm), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!pm || !input_dev) { + err = -ENOMEM; + goto fail1; + } + + pm->serio = serio; + pm->dev = input_dev; + snprintf(pm->phys, sizeof(pm->phys), "%s/input0", serio->phys); + + input_dev->private = pm; + input_dev->name = "Penmount Serial TouchScreen"; + input_dev->phys = pm->phys; + input_dev->id.bustype = BUS_RS232; + input_dev->id.vendor = SERIO_PENMOUNT; + input_dev->id.product = 0; + input_dev->id.version = 0x0100; + input_dev->cdev.dev = &serio->dev; + + input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_ABS); + input_dev->keybit[LONG(BTN_TOUCH)] = BIT(BTN_TOUCH); + input_set_abs_params(pm->dev, ABS_X, 0, 0x3ff, 0, 0); + input_set_abs_params(pm->dev, ABS_Y, 0, 0x3ff, 0, 0); + + serio_set_drvdata(serio, pm); + + err = serio_open(serio, drv); + if (err) + goto fail2; + + err = input_register_device(pm->dev); + if (err) + goto fail3; + + return 0; + + fail3: serio_close(serio); + fail2: serio_set_drvdata(serio, NULL); + fail1: input_free_device(input_dev); + kfree(pm); + return err; +} + +/* + * The serio driver structure. + */ + +static struct serio_device_id pm_serio_ids[] = { + { + .type = SERIO_RS232, + .proto = SERIO_PENMOUNT, + .id = SERIO_ANY, + .extra = SERIO_ANY, + }, + { 0 } +}; + +MODULE_DEVICE_TABLE(serio, pm_serio_ids); + +static struct serio_driver pm_drv = { + .driver = { + .name = "penmountlpc", + }, + .description = DRIVER_DESC, + .id_table = pm_serio_ids, + .interrupt = pm_interrupt, + .connect = pm_connect, + .disconnect = pm_disconnect, +}; + +/* + * The functions for inserting/removing us as a module. + */ + +static int __init pm_init(void) +{ + serio_register_driver(&pm_drv); + return 0; +} + +static void __exit pm_exit(void) +{ + serio_unregister_driver(&pm_drv); +} + +module_init(pm_init); +module_exit(pm_exit); -- cgit v1.2.3 From 4003dff41e65ad338a60dde90019bffcb5531fb6 Mon Sep 17 00:00:00 2001 From: Rick Koch Date: Sat, 5 Aug 2006 00:32:24 -0400 Subject: Input: add driver for Touchright serial touchscreens Signed-off-by: Rick Koch Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/Kconfig | 12 ++ drivers/input/touchscreen/Makefile | 1 + drivers/input/touchscreen/touchright.c | 196 +++++++++++++++++++++++++++++++++ 3 files changed, 209 insertions(+) create mode 100644 drivers/input/touchscreen/touchright.c (limited to 'drivers/input') diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index fa038126d50..216db122937 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -120,4 +120,16 @@ config TOUCHSCREEN_PENMOUNT To compile this driver as a module, choose M here: the module will be called penmount. +config TOUCHSCREEN_TOUCHRIGHT + tristate "Touchright serial touchscreen" + select SERIO + help + Say Y here if you have a Touchright serial touchscreen connected to + your system. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called touchright. + endif diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index 72e7be8097a..deda25e3680 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -13,3 +13,4 @@ obj-$(CONFIG_TOUCHSCREEN_MTOUCH) += mtouch.o obj-$(CONFIG_TOUCHSCREEN_MK712) += mk712.o obj-$(CONFIG_TOUCHSCREEN_HP600) += hp680_ts_input.o obj-$(CONFIG_TOUCHSCREEN_PENMOUNT) += penmount.o +obj-$(CONFIG_TOUCHSCREEN_TOUCHRIGHT) += touchright.o diff --git a/drivers/input/touchscreen/touchright.c b/drivers/input/touchscreen/touchright.c new file mode 100644 index 00000000000..1c89fa53865 --- /dev/null +++ b/drivers/input/touchscreen/touchright.c @@ -0,0 +1,196 @@ +/* + * Touchright serial touchscreen driver + * + * Copyright (c) 2006 Rick Koch + * + * Based on MicroTouch driver (drivers/input/touchscreen/mtouch.c) + * Copyright (c) 2004 Vojtech Pavlik + * and Dan Streetman + */ + +/* + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include + +#define DRIVER_DESC "Touchright serial touchscreen driver" + +MODULE_AUTHOR("Rick Koch "); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); + +/* + * Definitions & global arrays. + */ + +#define TR_FORMAT_TOUCH_BIT 0x01 +#define TR_FORMAT_STATUS_BYTE 0x40 +#define TR_FORMAT_STATUS_MASK ~TR_FORMAT_TOUCH_BIT + +#define TR_LENGTH 5 + +#define TR_MIN_XC 0 +#define TR_MAX_XC 0x1ff +#define TR_MIN_YC 0 +#define TR_MAX_YC 0x1ff + +/* + * Per-touchscreen data. + */ + +struct tr { + struct input_dev *dev; + struct serio *serio; + int idx; + unsigned char data[TR_LENGTH]; + char phys[32]; +}; + +static irqreturn_t tr_interrupt(struct serio *serio, + unsigned char data, unsigned int flags, struct pt_regs *regs) +{ + struct tr *tr = serio_get_drvdata(serio); + struct input_dev *dev = tr->dev; + + tr->data[tr->idx] = data; + + if ((tr->data[0] & TR_FORMAT_STATUS_MASK) == TR_FORMAT_STATUS_BYTE) { + if (++tr->idx == TR_LENGTH) { + input_regs(dev, regs); + input_report_abs(dev, ABS_X, + (tr->data[1] << 5) | (tr->data[2] >> 1)); + input_report_abs(dev, ABS_Y, + (tr->data[3] << 5) | (tr->data[4] >> 1)); + input_report_key(dev, BTN_TOUCH, + tr->data[0] & TR_FORMAT_TOUCH_BIT); + input_sync(dev); + tr->idx = 0; + } + } + + return IRQ_HANDLED; +} + +/* + * tr_disconnect() is the opposite of tr_connect() + */ + +static void tr_disconnect(struct serio *serio) +{ + struct tr *tr = serio_get_drvdata(serio); + + input_get_device(tr->dev); + input_unregister_device(tr->dev); + serio_close(serio); + serio_set_drvdata(serio, NULL); + input_put_device(tr->dev); + kfree(tr); +} + +/* + * tr_connect() is the routine that is called when someone adds a + * new serio device that supports the Touchright protocol and registers it as + * an input device. + */ + +static int tr_connect(struct serio *serio, struct serio_driver *drv) +{ + struct tr *tr; + struct input_dev *input_dev; + int err; + + tr = kzalloc(sizeof(struct tr), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!tr || !input_dev) { + err = -ENOMEM; + goto fail1; + } + + tr->serio = serio; + tr->dev = input_dev; + snprintf(tr->phys, sizeof(tr->phys), "%s/input0", serio->phys); + + input_dev->private = tr; + input_dev->name = "Touchright Serial TouchScreen"; + input_dev->phys = tr->phys; + input_dev->id.bustype = BUS_RS232; + input_dev->id.vendor = SERIO_TOUCHRIGHT; + input_dev->id.product = 0; + input_dev->id.version = 0x0100; + input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_ABS); + input_dev->keybit[LONG(BTN_TOUCH)] = BIT(BTN_TOUCH); + input_set_abs_params(tr->dev, ABS_X, TR_MIN_XC, TR_MAX_XC, 0, 0); + input_set_abs_params(tr->dev, ABS_Y, TR_MIN_YC, TR_MAX_YC, 0, 0); + + serio_set_drvdata(serio, tr); + + err = serio_open(serio, drv); + if (err) + goto fail2; + + err = input_register_device(tr->dev); + if (err) + goto fail3; + + return 0; + + fail3: serio_close(serio); + fail2: serio_set_drvdata(serio, NULL); + fail1: input_free_device(input_dev); + kfree(tr); + return err; +} + +/* + * The serio driver structure. + */ + +static struct serio_device_id tr_serio_ids[] = { + { + .type = SERIO_RS232, + .proto = SERIO_TOUCHRIGHT, + .id = SERIO_ANY, + .extra = SERIO_ANY, + }, + { 0 } +}; + +MODULE_DEVICE_TABLE(serio, tr_serio_ids); + +static struct serio_driver tr_drv = { + .driver = { + .name = "touchright", + }, + .description = DRIVER_DESC, + .id_table = tr_serio_ids, + .interrupt = tr_interrupt, + .connect = tr_connect, + .disconnect = tr_disconnect, +}; + +/* + * The functions for inserting/removing us as a module. + */ + +static int __init tr_init(void) +{ + serio_register_driver(&tr_drv); + return 0; +} + +static void __exit tr_exit(void) +{ + serio_unregister_driver(&tr_drv); +} + +module_init(tr_init); +module_exit(tr_exit); -- cgit v1.2.3 From 11ea3173d5f2de71d037ef58ac43395795fed2bc Mon Sep 17 00:00:00 2001 From: Rick Koch Date: Sat, 5 Aug 2006 00:32:30 -0400 Subject: Input: add driver for Touchwin serial touchscreens Signed-off-by: Rick Koch Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/Kconfig | 12 +++ drivers/input/touchscreen/Makefile | 1 + drivers/input/touchscreen/touchwin.c | 203 +++++++++++++++++++++++++++++++++++ 3 files changed, 216 insertions(+) create mode 100644 drivers/input/touchscreen/touchwin.c (limited to 'drivers/input') diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index 216db122937..9418bbe4707 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -132,4 +132,16 @@ config TOUCHSCREEN_TOUCHRIGHT To compile this driver as a module, choose M here: the module will be called touchright. +config TOUCHSCREEN_TOUCHWIN + tristate "Touchwin serial touchscreen" + select SERIO + help + Say Y here if you have a Touchwin serial touchscreen connected to + your system. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called touchwin. + endif diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index deda25e3680..1abb8f10d60 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -14,3 +14,4 @@ obj-$(CONFIG_TOUCHSCREEN_MK712) += mk712.o obj-$(CONFIG_TOUCHSCREEN_HP600) += hp680_ts_input.o obj-$(CONFIG_TOUCHSCREEN_PENMOUNT) += penmount.o obj-$(CONFIG_TOUCHSCREEN_TOUCHRIGHT) += touchright.o +obj-$(CONFIG_TOUCHSCREEN_TOUCHWIN) += touchwin.o diff --git a/drivers/input/touchscreen/touchwin.c b/drivers/input/touchscreen/touchwin.c new file mode 100644 index 00000000000..a7b4c755958 --- /dev/null +++ b/drivers/input/touchscreen/touchwin.c @@ -0,0 +1,203 @@ +/* + * Touchwindow serial touchscreen driver + * + * Copyright (c) 2006 Rick Koch + * + * Based on MicroTouch driver (drivers/input/touchscreen/mtouch.c) + * Copyright (c) 2004 Vojtech Pavlik + * and Dan Streetman + */ + +/* + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation. + */ + +/* + * 2005/02/19 Rick Koch: + * The Touchwindow I used is made by Edmark Corp. and + * constantly outputs a stream of 0's unless it is touched. + * It then outputs 3 bytes: X, Y, and a copy of Y. + */ + +#include +#include +#include +#include +#include +#include +#include + +#define DRIVER_DESC "Touchwindow serial touchscreen driver" + +MODULE_AUTHOR("Rick Koch "); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); + +/* + * Definitions & global arrays. + */ + +#define TW_LENGTH 3 + +#define TW_MIN_XC 0 +#define TW_MAX_XC 0xff +#define TW_MIN_YC 0 +#define TW_MAX_YC 0xff + +/* + * Per-touchscreen data. + */ + +struct tw { + struct input_dev *dev; + struct serio *serio; + int idx; + int touched; + unsigned char data[TW_LENGTH]; + char phys[32]; +}; + +static irqreturn_t tw_interrupt(struct serio *serio, + unsigned char data, unsigned int flags, struct pt_regs *regs) +{ + struct tw *tw = serio_get_drvdata(serio); + struct input_dev *dev = tw->dev; + + if (data) { /* touch */ + tw->touched = 1; + tw->data[tw->idx++] = data; + /* verify length and that the two Y's are the same */ + if (tw->idx == TW_LENGTH && tw->data[1] == tw->data[2]) { + input_regs(dev, regs); + input_report_abs(dev, ABS_X, tw->data[0]); + input_report_abs(dev, ABS_Y, tw->data[1]); + input_report_key(dev, BTN_TOUCH, 1); + input_sync(dev); + tw->idx = 0; + } + } else if (tw->touched) { /* untouch */ + input_report_key(dev, BTN_TOUCH, 0); + input_sync(dev); + tw->idx = 0; + tw->touched = 0; + } + + return IRQ_HANDLED; +} + +/* + * tw_disconnect() is the opposite of tw_connect() + */ + +static void tw_disconnect(struct serio *serio) +{ + struct tw *tw = serio_get_drvdata(serio); + + input_get_device(tw->dev); + input_unregister_device(tw->dev); + serio_close(serio); + serio_set_drvdata(serio, NULL); + input_put_device(tw->dev); + kfree(tw); +} + +/* + * tw_connect() is the routine that is called when someone adds a + * new serio device that supports the Touchwin protocol and registers it as + * an input device. + */ + +static int tw_connect(struct serio *serio, struct serio_driver *drv) +{ + struct tw *tw; + struct input_dev *input_dev; + int err; + + tw = kzalloc(sizeof(struct tw), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!tw || !input_dev) { + err = -ENOMEM; + goto fail1; + } + + tw->serio = serio; + tw->dev = input_dev; + snprintf(tw->phys, sizeof(tw->phys), "%s/input0", serio->phys); + + input_dev->private = tw; + input_dev->name = "Touchwindow Serial TouchScreen"; + input_dev->phys = tw->phys; + input_dev->id.bustype = BUS_RS232; + input_dev->id.vendor = SERIO_TOUCHWIN; + input_dev->id.product = 0; + input_dev->id.version = 0x0100; + input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_ABS); + input_dev->keybit[LONG(BTN_TOUCH)] = BIT(BTN_TOUCH); + input_set_abs_params(tw->dev, ABS_X, TW_MIN_XC, TW_MAX_XC, 0, 0); + input_set_abs_params(tw->dev, ABS_Y, TW_MIN_YC, TW_MAX_YC, 0, 0); + + serio_set_drvdata(serio, tw); + + err = serio_open(serio, drv); + if (err) + goto fail2; + + err = input_register_device(tw->dev); + if (err) + goto fail3; + + return 0; + + fail3: serio_close(serio); + fail2: serio_set_drvdata(serio, NULL); + fail1: input_free_device(input_dev); + kfree(tw); + return err; +} + +/* + * The serio driver structure. + */ + +static struct serio_device_id tw_serio_ids[] = { + { + .type = SERIO_RS232, + .proto = SERIO_TOUCHWIN, + .id = SERIO_ANY, + .extra = SERIO_ANY, + }, + { 0 } +}; + +MODULE_DEVICE_TABLE(serio, tw_serio_ids); + +static struct serio_driver tw_drv = { + .driver = { + .name = "touchwin", + }, + .description = DRIVER_DESC, + .id_table = tw_serio_ids, + .interrupt = tw_interrupt, + .connect = tw_connect, + .disconnect = tw_disconnect, +}; + +/* + * The functions for inserting/removing us as a module. + */ + +static int __init tw_init(void) +{ + serio_register_driver(&tw_drv); + return 0; +} + +static void __exit tw_exit(void) +{ + serio_unregister_driver(&tw_drv); +} + +module_init(tw_init); +module_exit(tw_exit); -- cgit v1.2.3 From e3882bb56e31dbb3950e51c1ba914ef2f445650c Mon Sep 17 00:00:00 2001 From: Helge Deller Date: Tue, 8 Aug 2006 23:37:18 -0400 Subject: Input: logips2pp - add sugnature 56 (Cordless MouseMan Wheel), cleanup Signed-off-by: Helge Deller Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/logips2pp.c | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/mouse/logips2pp.c b/drivers/input/mouse/logips2pp.c index 2f0d2884081..eb03f3a3f8a 100644 --- a/drivers/input/mouse/logips2pp.c +++ b/drivers/input/mouse/logips2pp.c @@ -30,9 +30,9 @@ #define PS2PP_NAV_BTN 0x20 struct ps2pp_info { - const int model; - unsigned const int kind; - unsigned const int features; + u8 model; + u8 kind; + u16 features; }; /* @@ -199,9 +199,9 @@ static void ps2pp_disconnect(struct psmouse *psmouse) device_remove_file(&psmouse->ps2dev.serio->dev, &psmouse_attr_smartscroll.dattr); } -static struct ps2pp_info *get_model_info(unsigned char model) +static const struct ps2pp_info *get_model_info(unsigned char model) { - static struct ps2pp_info ps2pp_list[] = { + static const struct ps2pp_info ps2pp_list[] = { { 12, 0, PS2PP_SIDE_BTN}, { 13, 0, 0 }, { 15, PS2PP_KIND_MX, /* MX1000 */ @@ -215,6 +215,7 @@ static struct ps2pp_info *get_model_info(unsigned char model) { 51, 0, 0 }, { 52, PS2PP_KIND_WHEEL, PS2PP_SIDE_BTN | PS2PP_WHEEL }, { 53, PS2PP_KIND_WHEEL, PS2PP_WHEEL }, + { 56, PS2PP_KIND_WHEEL, PS2PP_SIDE_BTN | PS2PP_WHEEL }, /* Cordless MouseMan Wheel */ { 61, PS2PP_KIND_MX, /* MX700 */ PS2PP_WHEEL | PS2PP_SIDE_BTN | PS2PP_TASK_BTN | PS2PP_EXTRA_BTN | PS2PP_NAV_BTN }, @@ -245,12 +246,11 @@ static struct ps2pp_info *get_model_info(unsigned char model) PS2PP_EXTRA_BTN | PS2PP_NAV_BTN }, { 114, PS2PP_KIND_MX, /* MX310 */ PS2PP_WHEEL | PS2PP_SIDE_BTN | - PS2PP_TASK_BTN | PS2PP_EXTRA_BTN }, - { } + PS2PP_TASK_BTN | PS2PP_EXTRA_BTN } }; int i; - for (i = 0; ps2pp_list[i].model; i++) + for (i = 0; i < ARRAY_SIZE(ps2pp_list); i++) if (model == ps2pp_list[i].model) return &ps2pp_list[i]; @@ -262,7 +262,8 @@ static struct ps2pp_info *get_model_info(unsigned char model) * Set up input device's properties based on the detected mouse model. */ -static void ps2pp_set_model_properties(struct psmouse *psmouse, struct ps2pp_info *model_info, +static void ps2pp_set_model_properties(struct psmouse *psmouse, + const struct ps2pp_info *model_info, int using_ps2pp) { struct input_dev *input_dev = psmouse->dev; @@ -328,7 +329,7 @@ int ps2pp_init(struct psmouse *psmouse, int set_properties) struct ps2dev *ps2dev = &psmouse->ps2dev; unsigned char param[4]; unsigned char model, buttons; - struct ps2pp_info *model_info; + const struct ps2pp_info *model_info; int use_ps2pp = 0; param[0] = 0; @@ -350,7 +351,7 @@ int ps2pp_init(struct psmouse *psmouse, int set_properties) /* * Do Logitech PS2++ / PS2T++ magic init. */ - if (model == 97) { /* Touch Pad 3 */ + if (model_info->kind == PS2PP_KIND_TP3) { /* Touch Pad 3 */ /* Unprotect RAM */ param[0] = 0x11; param[1] = 0x04; param[2] = 0x68; -- cgit v1.2.3 From e2df452b8adca9d3e3195ff7d91be375342964d7 Mon Sep 17 00:00:00 2001 From: Cjacker Huang Date: Tue, 8 Aug 2006 23:38:22 -0400 Subject: Input: i8042 - add Amoi to the MUX blacklist ALPS touchpad on Amoi laptops (Amoi is a vendor in China) is not detected when keyboard controller is in MUX mode, add to blacklist. Signed-off-by: Andrew Morton Signed-off-by: Dmitry Torokhov --- drivers/input/serio/i8042-x86ia64io.h | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'drivers/input') diff --git a/drivers/input/serio/i8042-x86ia64io.h b/drivers/input/serio/i8042-x86ia64io.h index f606e96bc2f..2232ab71fb4 100644 --- a/drivers/input/serio/i8042-x86ia64io.h +++ b/drivers/input/serio/i8042-x86ia64io.h @@ -180,6 +180,13 @@ static struct dmi_system_id __initdata i8042_dmi_nomux_table[] = { DMI_MATCH(DMI_PRODUCT_NAME, "VGN-FS115B"), }, }, + { + .ident = "Amoi M636/A737", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Amoi Electronics CO.,LTD."), + DMI_MATCH(DMI_PRODUCT_NAME, "M636/A737 platform"), + }, + }, { } }; -- cgit v1.2.3 From e38de678f6b19be3e46a678ec4deeaa7fa0fc140 Mon Sep 17 00:00:00 2001 From: Helge Deller Date: Sun, 10 Sep 2006 21:54:39 -0400 Subject: Input: constify psmouse driver Signed-off-by: Helge Deller Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/alps.c | 8 ++++---- drivers/input/mouse/alps.h | 2 +- drivers/input/mouse/lifebook.c | 8 +++++--- drivers/input/mouse/psmouse-base.c | 42 ++++++++++++++++++++++---------------- drivers/input/mouse/sermouse.c | 2 +- drivers/input/mouse/synaptics.c | 10 ++++----- 6 files changed, 40 insertions(+), 32 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/mouse/alps.c b/drivers/input/mouse/alps.c index 070d75330af..450b68a619f 100644 --- a/drivers/input/mouse/alps.c +++ b/drivers/input/mouse/alps.c @@ -36,7 +36,7 @@ #define ALPS_PASS 0x20 #define ALPS_FW_BK_2 0x40 -static struct alps_model_info alps_model_data[] = { +static const struct alps_model_info alps_model_data[] = { { { 0x33, 0x02, 0x0a }, 0x88, 0xf8, ALPS_OLDPROTO }, /* UMAX-530T */ { { 0x53, 0x02, 0x0a }, 0xf8, 0xf8, 0 }, { { 0x53, 0x02, 0x14 }, 0xf8, 0xf8, 0 }, @@ -209,10 +209,10 @@ static psmouse_ret_t alps_process_byte(struct psmouse *psmouse, struct pt_regs * return PSMOUSE_GOOD_DATA; } -static struct alps_model_info *alps_get_model(struct psmouse *psmouse, int *version) +static const struct alps_model_info *alps_get_model(struct psmouse *psmouse, int *version) { struct ps2dev *ps2dev = &psmouse->ps2dev; - unsigned char rates[] = { 0, 10, 20, 40, 60, 80, 100, 200 }; + static const unsigned char rates[] = { 0, 10, 20, 40, 60, 80, 100, 200 }; unsigned char param[4]; int i; @@ -504,7 +504,7 @@ init_fail: int alps_detect(struct psmouse *psmouse, int set_properties) { int version; - struct alps_model_info *model; + const struct alps_model_info *model; if (!(model = alps_get_model(psmouse, &version))) return -1; diff --git a/drivers/input/mouse/alps.h b/drivers/input/mouse/alps.h index e428f8d5d12..69db7325a49 100644 --- a/drivers/input/mouse/alps.h +++ b/drivers/input/mouse/alps.h @@ -25,7 +25,7 @@ struct alps_data { struct input_dev *dev2; /* Relative device */ char name[32]; /* Name */ char phys[32]; /* Phys */ - struct alps_model_info *i; /* Info */ + const struct alps_model_info *i;/* Info */ int prev_fin; /* Finger bit from previous packet */ }; diff --git a/drivers/input/mouse/lifebook.c b/drivers/input/mouse/lifebook.c index c14395ba798..5e9d2506751 100644 --- a/drivers/input/mouse/lifebook.c +++ b/drivers/input/mouse/lifebook.c @@ -115,13 +115,15 @@ static int lifebook_absolute_mode(struct psmouse *psmouse) static void lifebook_set_resolution(struct psmouse *psmouse, unsigned int resolution) { - unsigned char params[] = { 0, 1, 2, 2, 3 }; + static const unsigned char params[] = { 0, 1, 2, 2, 3 }; + unsigned char p; if (resolution == 0 || resolution > 400) resolution = 400; - ps2_command(&psmouse->ps2dev, ¶ms[resolution / 100], PSMOUSE_CMD_SETRES); - psmouse->resolution = 50 << params[resolution / 100]; + p = params[resolution / 100]; + ps2_command(&psmouse->ps2dev, &p, PSMOUSE_CMD_SETRES); + psmouse->resolution = 50 << p; } static void lifebook_disconnect(struct psmouse *psmouse) diff --git a/drivers/input/mouse/psmouse-base.c b/drivers/input/mouse/psmouse-base.c index 8bc9f51ae6c..ec0119459bc 100644 --- a/drivers/input/mouse/psmouse-base.c +++ b/drivers/input/mouse/psmouse-base.c @@ -112,8 +112,8 @@ static struct workqueue_struct *kpsmoused_wq; struct psmouse_protocol { enum psmouse_type type; - char *name; - char *alias; + const char *name; + const char *alias; int maxproto; int (*detect)(struct psmouse *, int); int (*init)(struct psmouse *); @@ -514,15 +514,17 @@ static int thinking_detect(struct psmouse *psmouse, int set_properties) { struct ps2dev *ps2dev = &psmouse->ps2dev; unsigned char param[2]; - unsigned char seq[] = { 20, 60, 40, 20, 20, 60, 40, 20, 20, 0 }; + static const unsigned char seq[] = { 20, 60, 40, 20, 20, 60, 40, 20, 20 }; int i; param[0] = 10; ps2_command(ps2dev, param, PSMOUSE_CMD_SETRATE); param[0] = 0; ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES); - for (i = 0; seq[i]; i++) - ps2_command(ps2dev, seq + i, PSMOUSE_CMD_SETRATE); + for (i = 0; i < ARRAY_SIZE(seq); i++) { + param[0] = seq[i]; + ps2_command(ps2dev, param, PSMOUSE_CMD_SETRATE); + } ps2_command(ps2dev, param, PSMOUSE_CMD_GETID); if (param[0] != 2) @@ -659,7 +661,7 @@ static int psmouse_extensions(struct psmouse *psmouse, return PSMOUSE_PS2; } -static struct psmouse_protocol psmouse_protocols[] = { +static const struct psmouse_protocol psmouse_protocols[] = { { .type = PSMOUSE_PS2, .name = "PS/2", @@ -733,7 +735,7 @@ static struct psmouse_protocol psmouse_protocols[] = { }, }; -static struct psmouse_protocol *psmouse_protocol_by_type(enum psmouse_type type) +static const struct psmouse_protocol *psmouse_protocol_by_type(enum psmouse_type type) { int i; @@ -745,9 +747,9 @@ static struct psmouse_protocol *psmouse_protocol_by_type(enum psmouse_type type) return &psmouse_protocols[0]; } -static struct psmouse_protocol *psmouse_protocol_by_name(const char *name, size_t len) +static const struct psmouse_protocol *psmouse_protocol_by_name(const char *name, size_t len) { - struct psmouse_protocol *p; + const struct psmouse_protocol *p; int i; for (i = 0; i < ARRAY_SIZE(psmouse_protocols); i++) { @@ -802,13 +804,15 @@ static int psmouse_probe(struct psmouse *psmouse) void psmouse_set_resolution(struct psmouse *psmouse, unsigned int resolution) { - unsigned char params[] = { 0, 1, 2, 2, 3 }; + static const unsigned char params[] = { 0, 1, 2, 2, 3 }; + unsigned char p; if (resolution == 0 || resolution > 200) resolution = 200; - ps2_command(&psmouse->ps2dev, ¶ms[resolution / 50], PSMOUSE_CMD_SETRES); - psmouse->resolution = 25 << params[resolution / 50]; + p = params[resolution / 50]; + ps2_command(&psmouse->ps2dev, &p, PSMOUSE_CMD_SETRES); + psmouse->resolution = 25 << p; } /* @@ -817,12 +821,14 @@ void psmouse_set_resolution(struct psmouse *psmouse, unsigned int resolution) static void psmouse_set_rate(struct psmouse *psmouse, unsigned int rate) { - unsigned char rates[] = { 200, 100, 80, 60, 40, 20, 10, 0 }; + static const unsigned char rates[] = { 200, 100, 80, 60, 40, 20, 10, 0 }; + unsigned char r; int i = 0; while (rates[i] > rate) i++; - ps2_command(&psmouse->ps2dev, &rates[i], PSMOUSE_CMD_SETRATE); - psmouse->rate = rates[i]; + r = rates[i]; + ps2_command(&psmouse->ps2dev, &r, PSMOUSE_CMD_SETRATE); + psmouse->rate = r; } /* @@ -1038,7 +1044,7 @@ static void psmouse_disconnect(struct serio *serio) mutex_unlock(&psmouse_mutex); } -static int psmouse_switch_protocol(struct psmouse *psmouse, struct psmouse_protocol *proto) +static int psmouse_switch_protocol(struct psmouse *psmouse, const struct psmouse_protocol *proto) { struct input_dev *input_dev = psmouse->dev; @@ -1369,7 +1375,7 @@ static ssize_t psmouse_attr_set_protocol(struct psmouse *psmouse, void *data, co struct serio *serio = psmouse->ps2dev.serio; struct psmouse *parent = NULL; struct input_dev *new_dev; - struct psmouse_protocol *proto; + const struct psmouse_protocol *proto; int retry = 0; if (!(proto = psmouse_protocol_by_name(buf, count))) @@ -1466,7 +1472,7 @@ static ssize_t psmouse_attr_set_resolution(struct psmouse *psmouse, void *data, static int psmouse_set_maxproto(const char *val, struct kernel_param *kp) { - struct psmouse_protocol *proto; + const struct psmouse_protocol *proto; if (!val) return -EINVAL; diff --git a/drivers/input/mouse/sermouse.c b/drivers/input/mouse/sermouse.c index 0023501a5b6..680b3235388 100644 --- a/drivers/input/mouse/sermouse.c +++ b/drivers/input/mouse/sermouse.c @@ -42,7 +42,7 @@ MODULE_AUTHOR("Vojtech Pavlik "); MODULE_DESCRIPTION(DRIVER_DESC); MODULE_LICENSE("GPL"); -static char *sermouse_protocols[] = { "None", "Mouse Systems Mouse", "Sun Mouse", "Microsoft Mouse", +static const char *sermouse_protocols[] = { "None", "Mouse Systems Mouse", "Sun Mouse", "Microsoft Mouse", "Logitech M+ Mouse", "Microsoft MZ Mouse", "Logitech MZ+ Mouse", "Logitech MZ++ Mouse"}; diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c index ad5d0a85e96..392108c436b 100644 --- a/drivers/input/mouse/synaptics.c +++ b/drivers/input/mouse/synaptics.c @@ -430,11 +430,11 @@ static void synaptics_process_packet(struct psmouse *psmouse) static int synaptics_validate_byte(unsigned char packet[], int idx, unsigned char pkt_type) { - static unsigned char newabs_mask[] = { 0xC8, 0x00, 0x00, 0xC8, 0x00 }; - static unsigned char newabs_rel_mask[] = { 0xC0, 0x00, 0x00, 0xC0, 0x00 }; - static unsigned char newabs_rslt[] = { 0x80, 0x00, 0x00, 0xC0, 0x00 }; - static unsigned char oldabs_mask[] = { 0xC0, 0x60, 0x00, 0xC0, 0x60 }; - static unsigned char oldabs_rslt[] = { 0xC0, 0x00, 0x00, 0x80, 0x00 }; + static const unsigned char newabs_mask[] = { 0xC8, 0x00, 0x00, 0xC8, 0x00 }; + static const unsigned char newabs_rel_mask[] = { 0xC0, 0x00, 0x00, 0xC0, 0x00 }; + static const unsigned char newabs_rslt[] = { 0x80, 0x00, 0x00, 0xC0, 0x00 }; + static const unsigned char oldabs_mask[] = { 0xC0, 0x60, 0x00, 0xC0, 0x60 }; + static const unsigned char oldabs_rslt[] = { 0xC0, 0x00, 0x00, 0x80, 0x00 }; if (idx < 0 || idx > 4) return 0; -- cgit v1.2.3 From 6d2750c167d47a97936cf4415165205f945c08f8 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Sun, 10 Sep 2006 21:56:06 -0400 Subject: Input: send key up events at disconnect Emit key up events for all pressed keys/buttons when disconnecting an input device. Cures "stuck" enter key effect when unloading keyboard module. Signed-off-by: Dmitry Torokhov --- drivers/input/input.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/input.c b/drivers/input/input.c index 1fc0517e942..1c71dd6fe5c 100644 --- a/drivers/input/input.c +++ b/drivers/input/input.c @@ -1014,9 +1014,12 @@ EXPORT_SYMBOL(input_register_device); void input_unregister_device(struct input_dev *dev) { struct list_head *node, *next; + int code; - if (!dev) - return; + for (code = 0; code <= KEY_MAX; code++) + if (test_bit(code, dev->key)) + input_report_key(dev, code, 0); + input_sync(dev); del_timer_sync(&dev->timer); -- cgit v1.2.3 From de9ce703c6b807b1dfef5942df4f2fadd0fdb67a Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Sun, 10 Sep 2006 21:57:21 -0400 Subject: Input: i8042 - get rid of polling timer Remove polling timer that was used to detect keybord/mice hotplug and register both IRQs right away instead of waiting for a driver to attach to a port. Signed-off-by: Dmitry Torokhov --- drivers/input/serio/i8042.c | 741 +++++++++++++++++++++++--------------------- drivers/input/serio/i8042.h | 9 - 2 files changed, 388 insertions(+), 362 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/serio/i8042.c b/drivers/input/serio/i8042.c index 06a3f25657d..1bb0c76a925 100644 --- a/drivers/input/serio/i8042.c +++ b/drivers/input/serio/i8042.c @@ -90,46 +90,24 @@ static DEFINE_SPINLOCK(i8042_lock); struct i8042_port { struct serio *serio; int irq; - unsigned char disable; - unsigned char irqen; unsigned char exists; signed char mux; - char name[8]; }; #define I8042_KBD_PORT_NO 0 #define I8042_AUX_PORT_NO 1 #define I8042_MUX_PORT_NO 2 #define I8042_NUM_PORTS (I8042_NUM_MUX_PORTS + 2) -static struct i8042_port i8042_ports[I8042_NUM_PORTS] = { - { - .disable = I8042_CTR_KBDDIS, - .irqen = I8042_CTR_KBDINT, - .mux = -1, - .name = "KBD", - }, - { - .disable = I8042_CTR_AUXDIS, - .irqen = I8042_CTR_AUXINT, - .mux = -1, - .name = "AUX", - } -}; + +static struct i8042_port i8042_ports[I8042_NUM_PORTS]; static unsigned char i8042_initial_ctr; static unsigned char i8042_ctr; -static unsigned char i8042_mux_open; static unsigned char i8042_mux_present; -static struct timer_list i8042_timer; +static unsigned char i8042_kbd_irq_registered; +static unsigned char i8042_aux_irq_registered; static struct platform_device *i8042_platform_device; - -/* - * Shared IRQ's require a device pointer, but this driver doesn't support - * multiple devices - */ -#define i8042_request_irq_cookie (&i8042_timer) - static irqreturn_t i8042_interrupt(int irq, void *dev_id, struct pt_regs *regs); /* @@ -141,6 +119,7 @@ static irqreturn_t i8042_interrupt(int irq, void *dev_id, struct pt_regs *regs); static int i8042_wait_read(void) { int i = 0; + while ((~i8042_read_status() & I8042_STR_OBF) && (i < I8042_CTL_TIMEOUT)) { udelay(50); i++; @@ -151,6 +130,7 @@ static int i8042_wait_read(void) static int i8042_wait_write(void) { int i = 0; + while ((i8042_read_status() & I8042_STR_IBF) && (i < I8042_CTL_TIMEOUT)) { udelay(50); i++; @@ -192,48 +172,57 @@ static int i8042_flush(void) * encoded in bits 8-11 of the command number. */ -static int i8042_command(unsigned char *param, int command) +static int __i8042_command(unsigned char *param, int command) { - unsigned long flags; - int i, retval, auxerr = 0; + int i, error; if (i8042_noloop && command == I8042_CMD_AUX_LOOP) return -1; - spin_lock_irqsave(&i8042_lock, flags); - - if ((retval = i8042_wait_write())) - goto out; + error = i8042_wait_write(); + if (error) + return error; dbg("%02x -> i8042 (command)", command & 0xff); i8042_write_command(command & 0xff); for (i = 0; i < ((command >> 12) & 0xf); i++) { - if ((retval = i8042_wait_write())) - goto out; + error = i8042_wait_write(); + if (error) + return error; dbg("%02x -> i8042 (parameter)", param[i]); i8042_write_data(param[i]); } for (i = 0; i < ((command >> 8) & 0xf); i++) { - if ((retval = i8042_wait_read())) - goto out; + error = i8042_wait_read(); + if (error) { + dbg(" -- i8042 (timeout)"); + return error; + } if (command == I8042_CMD_AUX_LOOP && !(i8042_read_status() & I8042_STR_AUXDATA)) { - retval = auxerr = -1; - goto out; + dbg(" -- i8042 (auxerr)"); + return -1; } param[i] = i8042_read_data(); dbg("%02x <- i8042 (return)", param[i]); } - if (retval) - dbg(" -- i8042 (%s)", auxerr ? "auxerr" : "timeout"); + return 0; +} - out: +static int i8042_command(unsigned char *param, int command) +{ + unsigned long flags; + int retval; + + spin_lock_irqsave(&i8042_lock, flags); + retval = __i8042_command(param, command); spin_unlock_irqrestore(&i8042_lock, flags); + return retval; } @@ -248,7 +237,7 @@ static int i8042_kbd_write(struct serio *port, unsigned char c) spin_lock_irqsave(&i8042_lock, flags); - if(!(retval = i8042_wait_write())) { + if (!(retval = i8042_wait_write())) { dbg("%02x -> i8042 (kbd-data)", c); i8042_write_data(c); } @@ -286,100 +275,6 @@ static int i8042_aux_write(struct serio *serio, unsigned char c) return retval; } -/* - * i8042_activate_port() enables port on a chip. - */ - -static int i8042_activate_port(struct i8042_port *port) -{ - if (!port->serio) - return -1; - - i8042_flush(); - - /* - * Enable port again here because it is disabled if we are - * resuming (normally it is enabled already). - */ - i8042_ctr &= ~port->disable; - - i8042_ctr |= port->irqen; - - if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) { - i8042_ctr &= ~port->irqen; - return -1; - } - - return 0; -} - - -/* - * i8042_open() is called when a port is open by the higher layer. - * It allocates the interrupt and calls i8042_enable_port. - */ - -static int i8042_open(struct serio *serio) -{ - struct i8042_port *port = serio->port_data; - - if (port->mux != -1) - if (i8042_mux_open++) - return 0; - - if (request_irq(port->irq, i8042_interrupt, - IRQF_SHARED, "i8042", i8042_request_irq_cookie)) { - printk(KERN_ERR "i8042.c: Can't get irq %d for %s, unregistering the port.\n", port->irq, port->name); - goto irq_fail; - } - - if (i8042_activate_port(port)) { - printk(KERN_ERR "i8042.c: Can't activate %s, unregistering the port\n", port->name); - goto activate_fail; - } - - i8042_interrupt(0, NULL, NULL); - - return 0; - - activate_fail: - free_irq(port->irq, i8042_request_irq_cookie); - - irq_fail: - serio_unregister_port_delayed(serio); - - return -1; -} - -/* - * i8042_close() frees the interrupt, so that it can possibly be used - * by another driver. We never know - if the user doesn't have a mouse, - * the BIOS could have used the AUX interrupt for PCI. - */ - -static void i8042_close(struct serio *serio) -{ - struct i8042_port *port = serio->port_data; - - if (port->mux != -1) - if (--i8042_mux_open) - return; - - i8042_ctr &= ~port->irqen; - - if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) { - printk(KERN_WARNING "i8042.c: Can't write CTR while closing %s.\n", port->name); -/* - * We still want to continue and free IRQ so if more data keeps coming in - * kernel will just ignore the irq. - */ - } - - free_irq(port->irq, i8042_request_irq_cookie); - - i8042_flush(); -} - /* * i8042_start() is called by serio core when port is about to finish * registering. It will mark port as existing so i8042_interrupt can @@ -423,8 +318,6 @@ static irqreturn_t i8042_interrupt(int irq, void *dev_id, struct pt_regs *regs) unsigned int port_no; int ret; - mod_timer(&i8042_timer, jiffies + I8042_POLL_PERIOD); - spin_lock_irqsave(&i8042_lock, flags); str = i8042_read_status(); if (unlikely(~str & I8042_STR_OBF)) { @@ -480,8 +373,8 @@ static irqreturn_t i8042_interrupt(int irq, void *dev_id, struct pt_regs *regs) port = &i8042_ports[port_no]; - dbg("%02x <- i8042 (interrupt, %s, %d%s%s)", - data, port->name, irq, + dbg("%02x <- i8042 (interrupt, %d, %d%s%s)", + data, port_no, irq, dfl & SERIO_PARITY ? ", bad parity" : "", dfl & SERIO_TIMEOUT ? ", timeout" : ""); @@ -493,6 +386,58 @@ static irqreturn_t i8042_interrupt(int irq, void *dev_id, struct pt_regs *regs) return IRQ_RETVAL(ret); } +/* + * i8042_enable_kbd_port enables keybaord port on chip + */ + +static int i8042_enable_kbd_port(void) +{ + i8042_ctr &= ~I8042_CTR_KBDDIS; + i8042_ctr |= I8042_CTR_KBDINT; + + if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) { + printk(KERN_ERR "i8042.c: Failed to enable KBD port.\n"); + return -EIO; + } + + return 0; +} + +/* + * i8042_enable_aux_port enables AUX (mouse) port on chip + */ + +static int i8042_enable_aux_port(void) +{ + i8042_ctr &= ~I8042_CTR_AUXDIS; + i8042_ctr |= I8042_CTR_AUXINT; + + if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) { + printk(KERN_ERR "i8042.c: Failed to enable AUX port.\n"); + return -EIO; + } + + return 0; +} + +/* + * i8042_enable_mux_ports enables 4 individual AUX ports after + * the controller has been switched into Multiplexed mode + */ + +static int i8042_enable_mux_ports(void) +{ + unsigned char param; + int i; + + for (i = 0; i < I8042_NUM_MUX_PORTS; i++) { + i8042_command(¶m, I8042_CMD_MUX_PFX + i); + i8042_command(¶m, I8042_CMD_AUX_ENABLE); + } + + return i8042_enable_aux_port(); +} + /* * i8042_set_mux_mode checks whether the controller has an active * multiplexor and puts the chip into Multiplexed (1) or Legacy (0) mode. @@ -510,8 +455,7 @@ static int i8042_set_mux_mode(unsigned int mode, unsigned char *mux_version) /* * Internal loopback test - send three bytes, they should come back from the - * mouse interface, the last should be version. Note that we negate mouseport - * command responses for the i8042_check_aux() routine. + * mouse interface, the last should be version. */ param = 0xf0; @@ -530,67 +474,67 @@ static int i8042_set_mux_mode(unsigned int mode, unsigned char *mux_version) return 0; } - /* - * i8042_enable_mux_ports enables 4 individual AUX ports after - * the controller has been switched into Multiplexed mode + * i8042_check_mux() checks whether the controller supports the PS/2 Active + * Multiplexing specification by Synaptics, Phoenix, Insyde and + * LCS/Telegraphics. */ -static int i8042_enable_mux_ports(void) +static int __devinit i8042_check_mux(void) { - unsigned char param; - int i; + unsigned char mux_version; + + if (i8042_set_mux_mode(1, &mux_version)) + return -1; + /* - * Disable all muxed ports by disabling AUX. + * Workaround for interference with USB Legacy emulation + * that causes a v10.12 MUX to be found. */ + if (mux_version == 0xAC) + return -1; + + printk(KERN_INFO "i8042.c: Detected active multiplexing controller, rev %d.%d.\n", + (mux_version >> 4) & 0xf, mux_version & 0xf); +/* + * Disable all muxed ports by disabling AUX. + */ i8042_ctr |= I8042_CTR_AUXDIS; i8042_ctr &= ~I8042_CTR_AUXINT; if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) { printk(KERN_ERR "i8042.c: Failed to disable AUX port, can't use MUX.\n"); - return -1; + return -EIO; } -/* - * Enable all muxed ports. - */ - - for (i = 0; i < I8042_NUM_MUX_PORTS; i++) { - i8042_command(¶m, I8042_CMD_MUX_PFX + i); - i8042_command(¶m, I8042_CMD_AUX_ENABLE); - } + i8042_mux_present = 1; return 0; } - /* - * i8042_check_mux() checks whether the controller supports the PS/2 Active - * Multiplexing specification by Synaptics, Phoenix, Insyde and - * LCS/Telegraphics. + * The following is used to test AUX IRQ delivery. */ +static struct completion i8042_aux_irq_delivered __devinitdata; +static int i8042_irq_being_tested __devinitdata; -static int __devinit i8042_check_mux(void) +static irqreturn_t __devinit i8042_aux_test_irq(int irq, void *dev_id, struct pt_regs *regs) { - unsigned char mux_version; - - if (i8042_set_mux_mode(1, &mux_version)) - return -1; - - /* Workaround for interference with USB Legacy emulation */ - /* that causes a v10.12 MUX to be found. */ - if (mux_version == 0xAC) - return -1; - - printk(KERN_INFO "i8042.c: Detected active multiplexing controller, rev %d.%d.\n", - (mux_version >> 4) & 0xf, mux_version & 0xf); + unsigned long flags; + unsigned char str, data; - if (i8042_enable_mux_ports()) - return -1; + spin_lock_irqsave(&i8042_lock, flags); + str = i8042_read_status(); + if (str & I8042_STR_OBF) { + data = i8042_read_data(); + if (i8042_irq_being_tested && + data == 0xa5 && (str & I8042_STR_AUXDATA)) + complete(&i8042_aux_irq_delivered); + } + spin_unlock_irqrestore(&i8042_lock, flags); - i8042_mux_present = 1; - return 0; + return IRQ_HANDLED; } @@ -601,18 +545,10 @@ static int __devinit i8042_check_mux(void) static int __devinit i8042_check_aux(void) { + int retval = -1; + int irq_registered = 0; + unsigned long flags; unsigned char param; - static int i8042_check_aux_cookie; - -/* - * Check if AUX irq is available. If it isn't, then there is no point - * in trying to detect AUX presence. - */ - - if (request_irq(i8042_ports[I8042_AUX_PORT_NO].irq, i8042_interrupt, - IRQF_SHARED, "i8042", &i8042_check_aux_cookie)) - return -1; - free_irq(i8042_ports[I8042_AUX_PORT_NO].irq, &i8042_check_aux_cookie); /* * Get rid of bytes in the queue. @@ -637,9 +573,9 @@ static int __devinit i8042_check_aux(void) * AUX ports, we test for this only when the LOOP command failed. */ - if (i8042_command(¶m, I8042_CMD_AUX_TEST) - || (param && param != 0xfa && param != 0xff)) - return -1; + if (i8042_command(¶m, I8042_CMD_AUX_TEST) || + (param && param != 0xfa && param != 0xff)) + return -1; } /* @@ -659,54 +595,80 @@ static int __devinit i8042_check_aux(void) return -1; /* - * Disable the interface. + * Test AUX IRQ delivery to make sure BIOS did not grab the IRQ and + * used it for a PCI card or somethig else. */ - i8042_ctr |= I8042_CTR_AUXDIS; - i8042_ctr &= ~I8042_CTR_AUXINT; + if (i8042_noloop) { +/* + * Without LOOP command we can't test AUX IRQ delivery. Assume the port + * is working and hope we are right. + */ + retval = 0; + goto out; + } - if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) - return -1; + if (request_irq(I8042_AUX_IRQ, i8042_aux_test_irq, IRQF_SHARED, + "i8042", i8042_platform_device)) + goto out; - return 0; -} + irq_registered = 1; + + if (i8042_enable_aux_port()) + goto out; + + spin_lock_irqsave(&i8042_lock, flags); + init_completion(&i8042_aux_irq_delivered); + i8042_irq_being_tested = 1; + + param = 0xa5; + retval = __i8042_command(¶m, I8042_CMD_AUX_LOOP & 0xf0ff); + + spin_unlock_irqrestore(&i8042_lock, flags); + + if (retval) + goto out; + if (wait_for_completion_timeout(&i8042_aux_irq_delivered, + msecs_to_jiffies(250)) == 0) { /* - * i8042_port_register() marks the device as existing, - * registers it, and reports to the user. + * AUX IRQ was never delivered so we need to flush the controller to + * get rid of the byte we put there; otherwise keyboard may not work. */ + i8042_flush(); + retval = -1; + } -static int __devinit i8042_port_register(struct i8042_port *port) -{ - i8042_ctr &= ~port->disable; + out: - if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) { - printk(KERN_WARNING "i8042.c: Can't write CTR while registering.\n"); - kfree(port->serio); - port->serio = NULL; - i8042_ctr |= port->disable; - return -EIO; - } +/* + * Disable the interface. + */ - printk(KERN_INFO "serio: i8042 %s port at %#lx,%#lx irq %d\n", - port->name, - (unsigned long) I8042_DATA_REG, - (unsigned long) I8042_COMMAND_REG, - port->irq); + i8042_ctr |= I8042_CTR_AUXDIS; + i8042_ctr &= ~I8042_CTR_AUXINT; - serio_register_port(port->serio); + if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) + retval = -1; - return 0; -} + if (irq_registered) + free_irq(I8042_AUX_IRQ, i8042_platform_device); + return retval; +} -static void i8042_timer_func(unsigned long data) +static int i8042_controller_check(void) { - i8042_interrupt(0, NULL, NULL); + if (i8042_flush() == I8042_BUFFER_SIZE) { + printk(KERN_ERR "i8042.c: No controller found.\n"); + return -ENODEV; + } + + return 0; } -static int i8042_ctl_test(void) +static int i8042_controller_selftest(void) { unsigned char param; @@ -715,13 +677,13 @@ static int i8042_ctl_test(void) if (i8042_command(¶m, I8042_CMD_CTL_TEST)) { printk(KERN_ERR "i8042.c: i8042 controller self test timeout.\n"); - return -1; + return -ENODEV; } if (param != I8042_RET_CTL_TEST) { printk(KERN_ERR "i8042.c: i8042 controller selftest failed. (%#x != %#x)\n", param, I8042_RET_CTL_TEST); - return -1; + return -EIO; } return 0; @@ -737,26 +699,13 @@ static int i8042_controller_init(void) { unsigned long flags; -/* - * Test the i8042. We need to know if it thinks it's working correctly - * before doing anything else. - */ - - if (i8042_flush() == I8042_BUFFER_SIZE) { - printk(KERN_ERR "i8042.c: No controller found.\n"); - return -1; - } - - if (i8042_ctl_test()) - return -1; - /* * Save the CTR for restoral on unload / reboot. */ if (i8042_command(&i8042_ctr, I8042_CMD_CTL_RCTR)) { printk(KERN_ERR "i8042.c: Can't read CTR while initializing i8042.\n"); - return -1; + return -EIO; } i8042_initial_ctr = i8042_ctr; @@ -805,7 +754,7 @@ static int i8042_controller_init(void) if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) { printk(KERN_ERR "i8042.c: Can't write CTR while initializing i8042.\n"); - return -1; + return -EIO; } return 0; @@ -813,15 +762,12 @@ static int i8042_controller_init(void) /* - * Reset the controller. + * Reset the controller and reset CRT to the original value set by BIOS. */ + static void i8042_controller_reset(void) { -/* - * Reset the controller if requested. - */ - - i8042_ctl_test(); + i8042_flush(); /* * Disable MUX mode if present. @@ -831,12 +777,16 @@ static void i8042_controller_reset(void) i8042_set_mux_mode(0, NULL); /* - * Restore the original control register setting. + * Reset the controller if requested. */ - i8042_ctr = i8042_initial_ctr; + i8042_controller_selftest(); - if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) +/* + * Restore the original control register setting. + */ + + if (i8042_command(&i8042_initial_ctr, I8042_CMD_CTL_WCTR)) printk(KERN_WARNING "i8042.c: Can't restore CTR.\n"); } @@ -850,14 +800,12 @@ static void i8042_controller_cleanup(void) { int i; - i8042_flush(); - /* * Reset anything that is connected to the ports. */ for (i = 0; i < I8042_NUM_PORTS; i++) - if (i8042_ports[i].exists) + if (i8042_ports[i].serio) serio_cleanup(i8042_ports[i].serio); i8042_controller_reset(); @@ -913,8 +861,7 @@ static long i8042_panic_blink(long count) static int i8042_suspend(struct platform_device *dev, pm_message_t state) { - del_timer_sync(&i8042_timer); - i8042_controller_reset(); + i8042_controller_cleanup(); return 0; } @@ -926,33 +873,39 @@ static int i8042_suspend(struct platform_device *dev, pm_message_t state) static int i8042_resume(struct platform_device *dev) { - int i; + int error; - if (i8042_ctl_test()) - return -1; + error = i8042_controller_check(); + if (error) + return error; - if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) { - printk(KERN_ERR "i8042: Can't write CTR\n"); - return -1; - } - - if (i8042_mux_present) - if (i8042_set_mux_mode(1, NULL) || i8042_enable_mux_ports()) - printk(KERN_WARNING "i8042: failed to resume active multiplexor, mouse won't work.\n"); + error = i8042_controller_selftest(); + if (error) + return error; /* - * Activate all ports. + * Restore pre-resume CTR value and disable all ports */ - for (i = 0; i < I8042_NUM_PORTS; i++) - i8042_activate_port(&i8042_ports[i]); + i8042_ctr |= I8042_CTR_AUXDIS | I8042_CTR_KBDDIS; + i8042_ctr &= ~(I8042_CTR_AUXINT | I8042_CTR_KBDINT); + if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) { + printk(KERN_ERR "i8042: Can't write CTR to resume\n"); + return -EIO; + } -/* - * Restart timer (for polling "stuck" data) - */ - mod_timer(&i8042_timer, jiffies + I8042_POLL_PERIOD); + if (i8042_mux_present) { + if (i8042_set_mux_mode(1, NULL) || i8042_enable_mux_ports()) + printk(KERN_WARNING + "i8042: failed to resume active multiplexor, " + "mouse won't work.\n"); + } else if (i8042_ports[I8042_AUX_PORT_NO].serio) + i8042_enable_aux_port(); - panic_blink = i8042_panic_blink; + if (i8042_ports[I8042_KBD_PORT_NO].serio) + i8042_enable_kbd_port(); + + i8042_interrupt(0, NULL, NULL); return 0; } @@ -978,24 +931,24 @@ static int __devinit i8042_create_kbd_port(void) serio->id.type = i8042_direct ? SERIO_8042 : SERIO_8042_XL; serio->write = i8042_dumbkbd ? NULL : i8042_kbd_write; - serio->open = i8042_open; - serio->close = i8042_close; serio->start = i8042_start; serio->stop = i8042_stop; serio->port_data = port; serio->dev.parent = &i8042_platform_device->dev; - strlcpy(serio->name, "i8042 Kbd Port", sizeof(serio->name)); + strlcpy(serio->name, "i8042 KBD port", sizeof(serio->name)); strlcpy(serio->phys, I8042_KBD_PHYS_DESC, sizeof(serio->phys)); port->serio = serio; + port->irq = I8042_KBD_IRQ; - return i8042_port_register(port); + return 0; } -static int __devinit i8042_create_aux_port(void) +static int __devinit i8042_create_aux_port(int idx) { struct serio *serio; - struct i8042_port *port = &i8042_ports[I8042_AUX_PORT_NO]; + int port_no = idx < 0 ? I8042_AUX_PORT_NO : I8042_MUX_PORT_NO + idx; + struct i8042_port *port = &i8042_ports[port_no]; serio = kzalloc(sizeof(struct serio), GFP_KERNEL); if (!serio) @@ -1003,111 +956,191 @@ static int __devinit i8042_create_aux_port(void) serio->id.type = SERIO_8042; serio->write = i8042_aux_write; - serio->open = i8042_open; - serio->close = i8042_close; serio->start = i8042_start; serio->stop = i8042_stop; serio->port_data = port; serio->dev.parent = &i8042_platform_device->dev; - strlcpy(serio->name, "i8042 Aux Port", sizeof(serio->name)); - strlcpy(serio->phys, I8042_AUX_PHYS_DESC, sizeof(serio->phys)); + if (idx < 0) { + strlcpy(serio->name, "i8042 AUX port", sizeof(serio->name)); + strlcpy(serio->phys, I8042_AUX_PHYS_DESC, sizeof(serio->phys)); + } else { + snprintf(serio->name, sizeof(serio->name), "i8042 AUX%d port", idx); + snprintf(serio->phys, sizeof(serio->phys), I8042_MUX_PHYS_DESC, idx + 1); + } port->serio = serio; + port->mux = idx; + port->irq = I8042_AUX_IRQ; - return i8042_port_register(port); + return 0; } -static int __devinit i8042_create_mux_port(int index) +static void __devinit i8042_free_kbd_port(void) { - struct serio *serio; - struct i8042_port *port = &i8042_ports[I8042_MUX_PORT_NO + index]; + kfree(i8042_ports[I8042_KBD_PORT_NO].serio); + i8042_ports[I8042_KBD_PORT_NO].serio = NULL; +} - serio = kzalloc(sizeof(struct serio), GFP_KERNEL); - if (!serio) - return -ENOMEM; +static void __devinit i8042_free_aux_ports(void) +{ + int i; - serio->id.type = SERIO_8042; - serio->write = i8042_aux_write; - serio->open = i8042_open; - serio->close = i8042_close; - serio->start = i8042_start; - serio->stop = i8042_stop; - serio->port_data = port; - serio->dev.parent = &i8042_platform_device->dev; - snprintf(serio->name, sizeof(serio->name), "i8042 Aux-%d Port", index); - snprintf(serio->phys, sizeof(serio->phys), I8042_MUX_PHYS_DESC, index + 1); + for (i = I8042_AUX_PORT_NO; i < I8042_NUM_PORTS; i++) { + kfree(i8042_ports[i].serio); + i8042_ports[i].serio = NULL; + } +} - *port = i8042_ports[I8042_AUX_PORT_NO]; - port->exists = 0; - snprintf(port->name, sizeof(port->name), "AUX%d", index); - port->mux = index; - port->serio = serio; +static void __devinit i8042_register_ports(void) +{ + int i; - return i8042_port_register(port); + for (i = 0; i < I8042_NUM_PORTS; i++) { + if (i8042_ports[i].serio) { + printk(KERN_INFO "serio: %s at %#lx,%#lx irq %d\n", + i8042_ports[i].serio->name, + (unsigned long) I8042_DATA_REG, + (unsigned long) I8042_COMMAND_REG, + i8042_ports[i].irq); + serio_register_port(i8042_ports[i].serio); + } + } } -static int __devinit i8042_probe(struct platform_device *dev) +static void __devinit i8042_unregister_ports(void) { - int i, have_ports = 0; - int err; + int i; - init_timer(&i8042_timer); - i8042_timer.function = i8042_timer_func; + for (i = 0; i < I8042_NUM_PORTS; i++) { + if (i8042_ports[i].serio) { + serio_unregister_port(i8042_ports[i].serio); + i8042_ports[i].serio = NULL; + } + } +} + +static void i8042_free_irqs(void) +{ + if (i8042_aux_irq_registered) + free_irq(I8042_AUX_IRQ, i8042_platform_device); + if (i8042_kbd_irq_registered) + free_irq(I8042_KBD_IRQ, i8042_platform_device); + + i8042_aux_irq_registered = i8042_kbd_irq_registered = 0; +} + +static int __devinit i8042_setup_aux(void) +{ + int (*aux_enable)(void); + int error; + int i; - if (i8042_controller_init()) + if (i8042_check_aux()) return -ENODEV; - if (!i8042_noaux && !i8042_check_aux()) { - if (!i8042_nomux && !i8042_check_mux()) { - for (i = 0; i < I8042_NUM_MUX_PORTS; i++) { - err = i8042_create_mux_port(i); - if (err) - goto err_unregister_ports; - } - } else { - err = i8042_create_aux_port(); - if (err) - goto err_unregister_ports; + if (i8042_nomux || i8042_check_mux()) { + error = i8042_create_aux_port(-1); + if (error) + goto err_free_ports; + aux_enable = i8042_enable_aux_port; + } else { + for (i = 0; i < I8042_NUM_MUX_PORTS; i++) { + error = i8042_create_aux_port(i); + if (error) + goto err_free_ports; } - have_ports = 1; + aux_enable = i8042_enable_mux_ports; } - if (!i8042_nokbd) { - err = i8042_create_kbd_port(); - if (err) - goto err_unregister_ports; - have_ports = 1; - } + error = request_irq(I8042_AUX_IRQ, i8042_interrupt, IRQF_SHARED, + "i8042", i8042_platform_device); + if (error) + goto err_free_ports; - if (!have_ports) { - err = -ENODEV; - goto err_controller_cleanup; - } + if (aux_enable()) + goto err_free_irq; - mod_timer(&i8042_timer, jiffies + I8042_POLL_PERIOD); + i8042_aux_irq_registered = 1; return 0; - err_unregister_ports: - for (i = 0; i < I8042_NUM_PORTS; i++) - if (i8042_ports[i].serio) - serio_unregister_port(i8042_ports[i].serio); - err_controller_cleanup: - i8042_controller_cleanup(); + err_free_irq: + free_irq(I8042_AUX_IRQ, i8042_platform_device); + err_free_ports: + i8042_free_aux_ports(); + return error; +} - return err; +static int __devinit i8042_setup_kbd(void) +{ + int error; + + error = i8042_create_kbd_port(); + if (error) + return error; + + error = request_irq(I8042_KBD_IRQ, i8042_interrupt, IRQF_SHARED, + "i8042", i8042_platform_device); + if (error) + goto err_free_port; + + error = i8042_enable_kbd_port(); + if (error) + goto err_free_irq; + + i8042_kbd_irq_registered = 1; + return 0; + + err_free_irq: + free_irq(I8042_KBD_IRQ, i8042_platform_device); + err_free_port: + i8042_free_kbd_port(); + return error; } -static int __devexit i8042_remove(struct platform_device *dev) +static int __devinit i8042_probe(struct platform_device *dev) { - int i; + int error; - i8042_controller_cleanup(); + error = i8042_controller_selftest(); + if (error) + return error; - for (i = 0; i < I8042_NUM_PORTS; i++) - if (i8042_ports[i].exists) - serio_unregister_port(i8042_ports[i].serio); + error = i8042_controller_init(); + if (error) + return error; + + if (!i8042_noaux) { + error = i8042_setup_aux(); + if (error && error != -ENODEV && error != -EBUSY) + goto out_fail; + } + + if (!i8042_nokbd) { + error = i8042_setup_kbd(); + if (error) + goto out_fail; + } - del_timer_sync(&i8042_timer); +/* + * Ok, everything is ready, let's register all serio ports + */ + i8042_register_ports(); + + return 0; + + out_fail: + i8042_free_aux_ports(); /* in case KBD failed but AUX not */ + i8042_free_irqs(); + i8042_controller_reset(); + + return error; +} + +static int __devexit i8042_remove(struct platform_device *dev) +{ + i8042_unregister_ports(); + i8042_free_irqs(); + i8042_controller_reset(); return 0; } @@ -1134,8 +1167,9 @@ static int __init i8042_init(void) if (err) return err; - i8042_ports[I8042_AUX_PORT_NO].irq = I8042_AUX_IRQ; - i8042_ports[I8042_KBD_PORT_NO].irq = I8042_KBD_IRQ; + err = i8042_controller_check(); + if (err) + goto err_platform_exit; err = platform_driver_register(&i8042_driver); if (err) @@ -1151,6 +1185,8 @@ static int __init i8042_init(void) if (err) goto err_free_device; + panic_blink = i8042_panic_blink; + return 0; err_free_device: @@ -1167,7 +1203,6 @@ static void __exit i8042_exit(void) { platform_device_unregister(i8042_platform_device); platform_driver_unregister(&i8042_driver); - i8042_platform_exit(); panic_blink = NULL; diff --git a/drivers/input/serio/i8042.h b/drivers/input/serio/i8042.h index af526ab9ec0..b3eb7a72d96 100644 --- a/drivers/input/serio/i8042.h +++ b/drivers/input/serio/i8042.h @@ -36,15 +36,6 @@ #define I8042_CTL_TIMEOUT 10000 -/* - * When the device isn't opened and it's interrupts aren't used, we poll it at - * regular intervals to see if any characters arrived. If yes, we can start - * probing for any mouse / keyboard connected. This is the period of the - * polling. - */ - -#define I8042_POLL_PERIOD HZ/20 - /* * Status register bits. */ -- cgit v1.2.3 From a91eaa16df5cd6c552e3a4a6e40e60ffbd9be951 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Thu, 14 Sep 2006 01:31:06 -0400 Subject: Input: i8042 - disable MUX mode on Toshiba Equium A110 When keyboard controller is in active multiplexing mode ALPS touchpad may get detected twice. Since the box does not have external PS/2 ports simply disabling MUX mode is safe solution. Signed-off-by: Dmitry Torokhov --- drivers/input/serio/i8042-x86ia64io.h | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'drivers/input') diff --git a/drivers/input/serio/i8042-x86ia64io.h b/drivers/input/serio/i8042-x86ia64io.h index 2232ab71fb4..8738edda661 100644 --- a/drivers/input/serio/i8042-x86ia64io.h +++ b/drivers/input/serio/i8042-x86ia64io.h @@ -159,6 +159,13 @@ static struct dmi_system_id __initdata i8042_dmi_nomux_table[] = { DMI_MATCH(DMI_PRODUCT_NAME, "Satellite P10"), }, }, + { + .ident = "Toshiba Equium A110", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"), + DMI_MATCH(DMI_PRODUCT_NAME, "EQUIUM A110"), + }, + }, { .ident = "Alienware Sentia", .matches = { -- cgit v1.2.3 From 9807879bfdc0c2b5106b4b378f5475c6a333d853 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Thu, 14 Sep 2006 01:31:27 -0400 Subject: Input: atkbd - support Microsoft Natural Elite Pro keyboards Microsoft Natural Elite Pro keyboard produces unisual response to the GET ID command - single byte 0xaa (normally keyboards produce 2-byte response). Fail GET ID command so atkbd gets a change to do alternate probe. Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/atkbd.c | 4 +--- drivers/input/serio/libps2.c | 18 ++++++++++++++++-- 2 files changed, 17 insertions(+), 5 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/keyboard/atkbd.c b/drivers/input/keyboard/atkbd.c index ce1f10e8984..9874072cf9d 100644 --- a/drivers/input/keyboard/atkbd.c +++ b/drivers/input/keyboard/atkbd.c @@ -635,9 +635,7 @@ static int atkbd_probe(struct atkbd *atkbd) return 0; } - if (param[0] != 0xab && param[0] != 0xac && /* Regular and NCD Sun keyboards */ - param[0] != 0x2b && param[0] != 0x5d && /* Trust keyboard, raw and translated */ - param[0] != 0x60 && param[0] != 0x47) /* NMB SGI keyboard, raw and translated */ + if (!ps2_is_keyboard_id(param[0])) return -1; atkbd->id = (param[0] << 8) | param[1]; diff --git a/drivers/input/serio/libps2.c b/drivers/input/serio/libps2.c index ed202f2f251..e0a2297a9d2 100644 --- a/drivers/input/serio/libps2.c +++ b/drivers/input/serio/libps2.c @@ -35,6 +35,7 @@ EXPORT_SYMBOL(ps2_schedule_command); EXPORT_SYMBOL(ps2_handle_ack); EXPORT_SYMBOL(ps2_handle_response); EXPORT_SYMBOL(ps2_cmd_aborted); +EXPORT_SYMBOL(ps2_is_keyboard_id); /* Work structure to schedule execution of a command */ struct ps2work { @@ -102,9 +103,9 @@ void ps2_drain(struct ps2dev *ps2dev, int maxbytes, int timeout) * known keyboard IDs. */ -static inline int ps2_is_keyboard_id(char id_byte) +int ps2_is_keyboard_id(char id_byte) { - static char keyboard_ids[] = { + const static char keyboard_ids[] = { 0xab, /* Regular keyboards */ 0xac, /* NCD Sun keyboard */ 0x2b, /* Trust keyboard, translated */ @@ -138,6 +139,19 @@ static int ps2_adjust_timeout(struct ps2dev *ps2dev, int command, int timeout) break; case PS2_CMD_GETID: + /* + * Microsoft Natural Elite keyboard responds to + * the GET ID command as it were a mouse, with + * a single byte. Fail the command so atkbd will + * use alternative probe to detect it. + */ + if (ps2dev->cmdbuf[1] == 0xaa) { + serio_pause_rx(ps2dev->serio); + ps2dev->flags = 0; + serio_continue_rx(ps2dev->serio); + timeout = 0; + } + /* * If device behind the port is not a keyboard there * won't be 2nd byte of ID response. -- cgit v1.2.3 From 5206c0d5ec514733dd098cf658d71327d199c7a0 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Thu, 14 Sep 2006 01:31:40 -0400 Subject: Input: libps2 - rearrange exports The new way is to mark function as exported right after its definition. Signed-off-by: Dmitry Torokhov --- drivers/input/serio/libps2.c | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/serio/libps2.c b/drivers/input/serio/libps2.c index e0a2297a9d2..dcb16b5cbec 100644 --- a/drivers/input/serio/libps2.c +++ b/drivers/input/serio/libps2.c @@ -27,16 +27,6 @@ MODULE_AUTHOR("Dmitry Torokhov "); MODULE_DESCRIPTION("PS/2 driver library"); MODULE_LICENSE("GPL"); -EXPORT_SYMBOL(ps2_init); -EXPORT_SYMBOL(ps2_sendbyte); -EXPORT_SYMBOL(ps2_drain); -EXPORT_SYMBOL(ps2_command); -EXPORT_SYMBOL(ps2_schedule_command); -EXPORT_SYMBOL(ps2_handle_ack); -EXPORT_SYMBOL(ps2_handle_response); -EXPORT_SYMBOL(ps2_cmd_aborted); -EXPORT_SYMBOL(ps2_is_keyboard_id); - /* Work structure to schedule execution of a command */ struct ps2work { struct work_struct work; @@ -72,6 +62,7 @@ int ps2_sendbyte(struct ps2dev *ps2dev, unsigned char byte, int timeout) return -ps2dev->nak; } +EXPORT_SYMBOL(ps2_sendbyte); /* * ps2_drain() waits for device to transmit requested number of bytes @@ -97,6 +88,7 @@ void ps2_drain(struct ps2dev *ps2dev, int maxbytes, int timeout) msecs_to_jiffies(timeout)); mutex_unlock(&ps2dev->cmd_mutex); } +EXPORT_SYMBOL(ps2_drain); /* * ps2_is_keyboard_id() checks received ID byte against the list of @@ -116,6 +108,7 @@ int ps2_is_keyboard_id(char id_byte) return memchr(keyboard_ids, id_byte, sizeof(keyboard_ids)) != NULL; } +EXPORT_SYMBOL(ps2_is_keyboard_id); /* * ps2_adjust_timeout() is called after receiving 1st byte of command @@ -251,6 +244,7 @@ int ps2_command(struct ps2dev *ps2dev, unsigned char *param, int command) mutex_unlock(&ps2dev->cmd_mutex); return rc; } +EXPORT_SYMBOL(ps2_command); /* * ps2_execute_scheduled_command() sends a command, previously scheduled by @@ -293,6 +287,7 @@ int ps2_schedule_command(struct ps2dev *ps2dev, unsigned char *param, int comman return 0; } +EXPORT_SYMBOL(ps2_schedule_command); /* * ps2_init() initializes ps2dev structure @@ -304,6 +299,7 @@ void ps2_init(struct ps2dev *ps2dev, struct serio *serio) init_waitqueue_head(&ps2dev->wait); ps2dev->serio = serio; } +EXPORT_SYMBOL(ps2_init); /* * ps2_handle_ack() is supposed to be used in interrupt handler @@ -349,6 +345,7 @@ int ps2_handle_ack(struct ps2dev *ps2dev, unsigned char data) return 1; } +EXPORT_SYMBOL(ps2_handle_ack); /* * ps2_handle_response() is supposed to be used in interrupt handler @@ -374,6 +371,7 @@ int ps2_handle_response(struct ps2dev *ps2dev, unsigned char data) return 1; } +EXPORT_SYMBOL(ps2_handle_response); void ps2_cmd_aborted(struct ps2dev *ps2dev) { @@ -385,4 +383,4 @@ void ps2_cmd_aborted(struct ps2dev *ps2dev) ps2dev->flags = 0; } - +EXPORT_SYMBOL(ps2_cmd_aborted); -- cgit v1.2.3 From 66e66118837ed95a299328437c2d9fb4b5137352 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Thu, 14 Sep 2006 01:31:59 -0400 Subject: Input: constify input core Signed-off-by: Dmitry Torokhov --- drivers/input/evbug.c | 8 +++++--- drivers/input/evdev.c | 7 ++++--- drivers/input/input.c | 7 ++++--- drivers/input/joydev.c | 9 +++++---- drivers/input/mousedev.c | 7 ++++--- drivers/input/power.c | 4 ++-- drivers/input/tsdev.c | 8 +++----- 7 files changed, 27 insertions(+), 23 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/evbug.c b/drivers/input/evbug.c index 07358fb51b8..1d8ce7a1ec3 100644 --- a/drivers/input/evbug.c +++ b/drivers/input/evbug.c @@ -42,10 +42,12 @@ static char evbug_name[] = "evbug"; static void evbug_event(struct input_handle *handle, unsigned int type, unsigned int code, int value) { - printk(KERN_DEBUG "evbug.c: Event. Dev: %s, Type: %d, Code: %d, Value: %d\n", handle->dev->phys, type, code, value); + printk(KERN_DEBUG "evbug.c: Event. Dev: %s, Type: %d, Code: %d, Value: %d\n", + handle->dev->phys, type, code, value); } -static struct input_handle *evbug_connect(struct input_handler *handler, struct input_dev *dev, struct input_device_id *id) +static struct input_handle *evbug_connect(struct input_handler *handler, struct input_dev *dev, + const struct input_device_id *id) { struct input_handle *handle; @@ -72,7 +74,7 @@ static void evbug_disconnect(struct input_handle *handle) kfree(handle); } -static struct input_device_id evbug_ids[] = { +static const struct input_device_id evbug_ids[] = { { .driver_info = 1 }, /* Matches all devices */ { }, /* Terminating zero entry */ }; diff --git a/drivers/input/evdev.c b/drivers/input/evdev.c index 12c7ab876c3..154e423167b 100644 --- a/drivers/input/evdev.c +++ b/drivers/input/evdev.c @@ -601,7 +601,7 @@ static long evdev_ioctl_compat(struct file *file, unsigned int cmd, unsigned lon } #endif -static struct file_operations evdev_fops = { +static const struct file_operations evdev_fops = { .owner = THIS_MODULE, .read = evdev_read, .write = evdev_write, @@ -616,7 +616,8 @@ static struct file_operations evdev_fops = { .flush = evdev_flush }; -static struct input_handle *evdev_connect(struct input_handler *handler, struct input_dev *dev, struct input_device_id *id) +static struct input_handle *evdev_connect(struct input_handler *handler, struct input_dev *dev, + const struct input_device_id *id) { struct evdev *evdev; struct class_device *cdev; @@ -675,7 +676,7 @@ static void evdev_disconnect(struct input_handle *handle) evdev_free(evdev); } -static struct input_device_id evdev_ids[] = { +static const struct input_device_id evdev_ids[] = { { .driver_info = 1 }, /* Matches all devices */ { }, /* Terminating zero entry */ }; diff --git a/drivers/input/input.c b/drivers/input/input.c index 1c71dd6fe5c..4954c790ccb 100644 --- a/drivers/input/input.c +++ b/drivers/input/input.c @@ -313,7 +313,8 @@ static void input_link_handle(struct input_handle *handle) if (i != NBITS(max)) \ continue; -static struct input_device_id *input_match_device(struct input_device_id *id, struct input_dev *dev) +static const struct input_device_id *input_match_device(const struct input_device_id *id, + struct input_dev *dev) { int i; @@ -935,7 +936,7 @@ int input_register_device(struct input_dev *dev) static atomic_t input_no = ATOMIC_INIT(0); struct input_handle *handle; struct input_handler *handler; - struct input_device_id *id; + const struct input_device_id *id; const char *path; int error; @@ -1050,7 +1051,7 @@ void input_register_handler(struct input_handler *handler) { struct input_dev *dev; struct input_handle *handle; - struct input_device_id *id; + const struct input_device_id *id; if (!handler) return; diff --git a/drivers/input/joydev.c b/drivers/input/joydev.c index d67157513bf..033e3aac92a 100644 --- a/drivers/input/joydev.c +++ b/drivers/input/joydev.c @@ -451,7 +451,7 @@ static int joydev_ioctl(struct inode *inode, struct file *file, unsigned int cmd } } -static struct file_operations joydev_fops = { +static const struct file_operations joydev_fops = { .owner = THIS_MODULE, .read = joydev_read, .write = joydev_write, @@ -465,7 +465,8 @@ static struct file_operations joydev_fops = { .fasync = joydev_fasync, }; -static struct input_handle *joydev_connect(struct input_handler *handler, struct input_dev *dev, struct input_device_id *id) +static struct input_handle *joydev_connect(struct input_handler *handler, struct input_dev *dev, + const struct input_device_id *id) { struct joydev *joydev; struct class_device *cdev; @@ -562,7 +563,7 @@ static void joydev_disconnect(struct input_handle *handle) joydev_free(joydev); } -static struct input_device_id joydev_blacklist[] = { +static const struct input_device_id joydev_blacklist[] = { { .flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_KEYBIT, .evbit = { BIT(EV_KEY) }, @@ -571,7 +572,7 @@ static struct input_device_id joydev_blacklist[] = { { } /* Terminating entry */ }; -static struct input_device_id joydev_ids[] = { +static const struct input_device_id joydev_ids[] = { { .flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_ABSBIT, .evbit = { BIT(EV_ABS) }, diff --git a/drivers/input/mousedev.c b/drivers/input/mousedev.c index 1f851acab30..cd02f1b62b6 100644 --- a/drivers/input/mousedev.c +++ b/drivers/input/mousedev.c @@ -614,7 +614,7 @@ static unsigned int mousedev_poll(struct file *file, poll_table *wait) (list->mousedev->exist ? 0 : (POLLHUP | POLLERR)); } -static struct file_operations mousedev_fops = { +static const struct file_operations mousedev_fops = { .owner = THIS_MODULE, .read = mousedev_read, .write = mousedev_write, @@ -624,7 +624,8 @@ static struct file_operations mousedev_fops = { .fasync = mousedev_fasync, }; -static struct input_handle *mousedev_connect(struct input_handler *handler, struct input_dev *dev, struct input_device_id *id) +static struct input_handle *mousedev_connect(struct input_handler *handler, struct input_dev *dev, + const struct input_device_id *id) { struct mousedev *mousedev; struct class_device *cdev; @@ -688,7 +689,7 @@ static void mousedev_disconnect(struct input_handle *handle) } } -static struct input_device_id mousedev_ids[] = { +static const struct input_device_id mousedev_ids[] = { { .flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_KEYBIT | INPUT_DEVICE_ID_MATCH_RELBIT, .evbit = { BIT(EV_KEY) | BIT(EV_REL) }, diff --git a/drivers/input/power.c b/drivers/input/power.c index 51a519e24b6..75d018759cd 100644 --- a/drivers/input/power.c +++ b/drivers/input/power.c @@ -98,7 +98,7 @@ static void power_event(struct input_handle *handle, unsigned int type, static struct input_handle *power_connect(struct input_handler *handler, struct input_dev *dev, - struct input_device_id *id) + const struct input_device_id *id) { struct input_handle *handle; @@ -120,7 +120,7 @@ static void power_disconnect(struct input_handle *handle) kfree(handle); } -static struct input_device_id power_ids[] = { +static const struct input_device_id power_ids[] = { { .flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_KEYBIT, .evbit = { BIT(EV_KEY) }, diff --git a/drivers/input/tsdev.c b/drivers/input/tsdev.c index 00e3929c628..162ee08223a 100644 --- a/drivers/input/tsdev.c +++ b/drivers/input/tsdev.c @@ -135,8 +135,6 @@ struct tsdev_list { #define TS_GET_CAL _IOR(IOC_H3600_TS_MAGIC, 10, struct ts_calibration) #define TS_SET_CAL _IOW(IOC_H3600_TS_MAGIC, 11, struct ts_calibration) -static struct input_handler tsdev_handler; - static struct tsdev *tsdev_table[TSDEV_MINORS/2]; static int tsdev_fasync(int fd, struct file *file, int on) @@ -263,7 +261,7 @@ static int tsdev_ioctl(struct inode *inode, struct file *file, return retval; } -static struct file_operations tsdev_fops = { +static const struct file_operations tsdev_fops = { .owner = THIS_MODULE, .open = tsdev_open, .release = tsdev_release, @@ -370,7 +368,7 @@ static void tsdev_event(struct input_handle *handle, unsigned int type, static struct input_handle *tsdev_connect(struct input_handler *handler, struct input_dev *dev, - struct input_device_id *id) + const struct input_device_id *id) { struct tsdev *tsdev; struct class_device *cdev; @@ -443,7 +441,7 @@ static void tsdev_disconnect(struct input_handle *handle) tsdev_free(tsdev); } -static struct input_device_id tsdev_ids[] = { +static const struct input_device_id tsdev_ids[] = { { .flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_KEYBIT | INPUT_DEVICE_ID_MATCH_RELBIT, .evbit = { BIT(EV_KEY) | BIT(EV_REL) }, -- cgit v1.2.3 From 655816e49867082d13ece0da31d76e12cc0de4a5 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Thu, 14 Sep 2006 01:32:14 -0400 Subject: Input: fix input module refcounting Now that input_free_device is basically an alias for input_put_device we need to acquire a reference to input module right when we allocate device because input_dev_release releases reference to input module unconditionally. Signed-off-by: Dmitry Torokhov --- drivers/input/input.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/input.c b/drivers/input/input.c index 4954c790ccb..f2c85a60a0d 100644 --- a/drivers/input/input.c +++ b/drivers/input/input.c @@ -912,6 +912,8 @@ struct input_dev *input_allocate_device(void) mutex_init(&dev->mutex); INIT_LIST_HEAD(&dev->h_list); INIT_LIST_HEAD(&dev->node); + + __module_get(THIS_MODULE); } return dev; @@ -985,8 +987,6 @@ int input_register_device(struct input_dev *dev) if (error) goto fail3; - __module_get(THIS_MODULE); - path = kobject_get_path(&dev->cdev.kobj, GFP_KERNEL); printk(KERN_INFO "input: %s as %s\n", dev->name ? dev->name : "Unspecified device", path ? path : "N/A"); -- cgit v1.2.3 From 68c2a1607cd6dd12427c9566b39756e92708713c Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Thu, 14 Sep 2006 01:32:28 -0400 Subject: Input: remove cruft that was needed for transition to sysfs Signed-off-by: Dmitry Torokhov --- drivers/input/input.c | 10 ---------- 1 file changed, 10 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/input.c b/drivers/input/input.c index f2c85a60a0d..c3448364cc7 100644 --- a/drivers/input/input.c +++ b/drivers/input/input.c @@ -906,7 +906,6 @@ struct input_dev *input_allocate_device(void) dev = kzalloc(sizeof(struct input_dev), GFP_KERNEL); if (dev) { - dev->dynalloc = 1; dev->cdev.class = &input_class; class_device_initialize(&dev->cdev); mutex_init(&dev->mutex); @@ -942,13 +941,6 @@ int input_register_device(struct input_dev *dev) const char *path; int error; - if (!dev->dynalloc) { - printk(KERN_WARNING "input: device %s is statically allocated, will not register\n" - "Please convert to input_allocate_device() or contact dtor_core@ameritech.net\n", - dev->name ? dev->name : ""); - return -EINVAL; - } - set_bit(EV_SYN, dev->evbit); /* @@ -964,10 +956,8 @@ int input_register_device(struct input_dev *dev) dev->rep[REP_PERIOD] = 33; } - INIT_LIST_HEAD(&dev->h_list); list_add_tail(&dev->node, &input_dev_list); - dev->cdev.class = &input_class; snprintf(dev->cdev.class_id, sizeof(dev->cdev.class_id), "input%ld", (unsigned long) atomic_inc_return(&input_no) - 1); -- cgit v1.2.3 From 4263cf0fac28122c8381b6f4f9441a43cd93c81f Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Thu, 14 Sep 2006 01:32:39 -0400 Subject: Input: make input_register_handler() return error codes Signed-off-by: Dmitry Torokhov --- drivers/input/evbug.c | 3 +-- drivers/input/evdev.c | 3 +-- drivers/input/input.c | 12 +++++++----- drivers/input/joydev.c | 3 +-- drivers/input/mousedev.c | 21 +++++++++++++++++---- drivers/input/power.c | 3 +-- drivers/input/tsdev.c | 4 +--- 7 files changed, 29 insertions(+), 20 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/evbug.c b/drivers/input/evbug.c index 1d8ce7a1ec3..5a9653c3128 100644 --- a/drivers/input/evbug.c +++ b/drivers/input/evbug.c @@ -91,8 +91,7 @@ static struct input_handler evbug_handler = { static int __init evbug_init(void) { - input_register_handler(&evbug_handler); - return 0; + return input_register_handler(&evbug_handler); } static void __exit evbug_exit(void) diff --git a/drivers/input/evdev.c b/drivers/input/evdev.c index 154e423167b..6439f378f6c 100644 --- a/drivers/input/evdev.c +++ b/drivers/input/evdev.c @@ -695,8 +695,7 @@ static struct input_handler evdev_handler = { static int __init evdev_init(void) { - input_register_handler(&evdev_handler); - return 0; + return input_register_handler(&evdev_handler); } static void __exit evdev_exit(void) diff --git a/drivers/input/input.c b/drivers/input/input.c index c3448364cc7..1c8c8a5bc4a 100644 --- a/drivers/input/input.c +++ b/drivers/input/input.c @@ -1037,19 +1037,20 @@ void input_unregister_device(struct input_dev *dev) } EXPORT_SYMBOL(input_unregister_device); -void input_register_handler(struct input_handler *handler) +int input_register_handler(struct input_handler *handler) { struct input_dev *dev; struct input_handle *handle; const struct input_device_id *id; - if (!handler) - return; - INIT_LIST_HEAD(&handler->h_list); - if (handler->fops != NULL) + if (handler->fops != NULL) { + if (input_table[handler->minor >> 5]) + return -EBUSY; + input_table[handler->minor >> 5] = handler; + } list_add_tail(&handler->node, &input_handler_list); @@ -1063,6 +1064,7 @@ void input_register_handler(struct input_handler *handler) } input_wakeup_procfs_readers(); + return 0; } EXPORT_SYMBOL(input_register_handler); diff --git a/drivers/input/joydev.c b/drivers/input/joydev.c index 033e3aac92a..9f3529ad3fd 100644 --- a/drivers/input/joydev.c +++ b/drivers/input/joydev.c @@ -606,8 +606,7 @@ static struct input_handler joydev_handler = { static int __init joydev_init(void) { - input_register_handler(&joydev_handler); - return 0; + return input_register_handler(&joydev_handler); } static void __exit joydev_exit(void) diff --git a/drivers/input/mousedev.c b/drivers/input/mousedev.c index cd02f1b62b6..a22a74a2a3d 100644 --- a/drivers/input/mousedev.c +++ b/drivers/input/mousedev.c @@ -738,7 +738,12 @@ static int psaux_registered; static int __init mousedev_init(void) { - input_register_handler(&mousedev_handler); + struct class_device *cdev; + int error; + + error = input_register_handler(&mousedev_handler); + if (error) + return error; memset(&mousedev_mix, 0, sizeof(struct mousedev)); INIT_LIST_HEAD(&mousedev_mix.list); @@ -747,12 +752,20 @@ static int __init mousedev_init(void) mousedev_mix.exist = 1; mousedev_mix.minor = MOUSEDEV_MIX; - class_device_create(&input_class, NULL, + cdev = class_device_create(&input_class, NULL, MKDEV(INPUT_MAJOR, MOUSEDEV_MINOR_BASE + MOUSEDEV_MIX), NULL, "mice"); + if (IS_ERR(cdev)) { + input_unregister_handler(&mousedev_handler); + return PTR_ERR(cdev); + } #ifdef CONFIG_INPUT_MOUSEDEV_PSAUX - if (!(psaux_registered = !misc_register(&psaux_mouse))) - printk(KERN_WARNING "mice: could not misc_register the device\n"); + error = misc_register(&psaux_mouse); + if (error) + printk(KERN_WARNING "mice: could not register psaux device, " + "error: %d\n", error); + else + psaux_registered = 1; #endif printk(KERN_INFO "mice: PS/2 mouse device common for all mice\n"); diff --git a/drivers/input/power.c b/drivers/input/power.c index 75d018759cd..ee82464a2fa 100644 --- a/drivers/input/power.c +++ b/drivers/input/power.c @@ -150,8 +150,7 @@ static struct input_handler power_handler = { static int __init power_init(void) { - input_register_handler(&power_handler); - return 0; + return input_register_handler(&power_handler); } static void __exit power_exit(void) diff --git a/drivers/input/tsdev.c b/drivers/input/tsdev.c index 162ee08223a..a730c461227 100644 --- a/drivers/input/tsdev.c +++ b/drivers/input/tsdev.c @@ -479,9 +479,7 @@ static struct input_handler tsdev_handler = { static int __init tsdev_init(void) { - input_register_handler(&tsdev_handler); - printk(KERN_INFO "ts: Compaq touchscreen protocol output\n"); - return 0; + return input_register_handler(&tsdev_handler); } static void __exit tsdev_exit(void) -- cgit v1.2.3 From f35d0616bddf4efdfaedc5dfad2267202a3c739f Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Tue, 19 Sep 2006 01:59:32 -0400 Subject: Input: add driver for stowaway serial keyboards Add support for stowaway and stowaway compatible (eg. dicota inutPDA) serial keyboards. Reported to work on palm zire71 and palm tungsten T3. Signed-off-by: Marek Vasut Signed-off-by: Andrew Morton Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/Kconfig | 11 +++ drivers/input/keyboard/Makefile | 1 + drivers/input/keyboard/stowaway.c | 187 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 199 insertions(+) create mode 100644 drivers/input/keyboard/stowaway.c (limited to 'drivers/input') diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig index a9dda56f62c..ebd98d18cf0 100644 --- a/drivers/input/keyboard/Kconfig +++ b/drivers/input/keyboard/Kconfig @@ -121,6 +121,17 @@ config KEYBOARD_NEWTON To compile this driver as a module, choose M here: the module will be called newtonkbd. +config KEYBOARD_STOWAWAY + tristate "Stowaway keyboard" + select SERIO + help + Say Y here if you have a Stowaway keyboard on a serial port. + Stowaway compatible keyboards like Dicota Input-PDA keyboard + are also supported by this driver. + + To compile this driver as a module, choose M here: the + module will be called stowaway. + config KEYBOARD_CORGI tristate "Corgi keyboard" depends on PXA_SHARPSL diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile index 2708167ba17..bb4525e59df 100644 --- a/drivers/input/keyboard/Makefile +++ b/drivers/input/keyboard/Makefile @@ -11,6 +11,7 @@ obj-$(CONFIG_KEYBOARD_XTKBD) += xtkbd.o obj-$(CONFIG_KEYBOARD_AMIGA) += amikbd.o obj-$(CONFIG_KEYBOARD_LOCOMO) += locomokbd.o obj-$(CONFIG_KEYBOARD_NEWTON) += newtonkbd.o +obj-$(CONFIG_KEYBOARD_STOWAWAY) += stowaway.o obj-$(CONFIG_KEYBOARD_CORGI) += corgikbd.o obj-$(CONFIG_KEYBOARD_SPITZ) += spitzkbd.o obj-$(CONFIG_KEYBOARD_HIL) += hil_kbd.o diff --git a/drivers/input/keyboard/stowaway.c b/drivers/input/keyboard/stowaway.c new file mode 100644 index 00000000000..04c54c57f25 --- /dev/null +++ b/drivers/input/keyboard/stowaway.c @@ -0,0 +1,187 @@ +/* + * Stowaway keyboard driver for Linux + */ + +/* + * Copyright (c) 2006 Marek Vasut + * + * Based on Newton keyboard driver for Linux + * by Justin Cormack + */ + +/* + * 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 , or by paper mail: + * Marek Vasut, Liskovecka 559, Frydek-Mistek, 738 01 Czech Republic + */ + +#include +#include +#include +#include +#include + +#define DRIVER_DESC "Stowaway keyboard driver" + +MODULE_AUTHOR("Marek Vasut "); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); + +#define SKBD_KEY_MASK 0x7f +#define SKBD_RELEASE 0x80 + +static unsigned char skbd_keycode[128] = { + KEY_1, KEY_2, KEY_3, KEY_Z, KEY_4, KEY_5, KEY_6, KEY_7, + 0, KEY_Q, KEY_W, KEY_E, KEY_R, KEY_T, KEY_Y, KEY_GRAVE, + KEY_X, KEY_A, KEY_S, KEY_D, KEY_F, KEY_G, KEY_H, KEY_SPACE, + KEY_CAPSLOCK, KEY_TAB, KEY_LEFTCTRL, 0, 0, 0, 0, 0, + 0, 0, 0, KEY_LEFTALT, 0, 0, 0, 0, + 0, 0, 0, 0, KEY_C, KEY_V, KEY_B, KEY_N, + KEY_MINUS, KEY_EQUAL, KEY_BACKSPACE, KEY_HOME, KEY_8, KEY_9, KEY_0, KEY_ESC, + KEY_LEFTBRACE, KEY_RIGHTBRACE, KEY_BACKSLASH, KEY_END, KEY_U, KEY_I, KEY_O, KEY_P, + KEY_APOSTROPHE, KEY_ENTER, KEY_PAGEUP,0, KEY_J, KEY_K, KEY_L, KEY_SEMICOLON, + KEY_SLASH, KEY_UP, KEY_PAGEDOWN, 0,KEY_M, KEY_COMMA, KEY_DOT, KEY_INSERT, + KEY_DELETE, KEY_LEFT, KEY_DOWN, KEY_RIGHT, 0, 0, 0, + KEY_LEFTSHIFT, KEY_RIGHTSHIFT, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, KEY_F1, KEY_F2, KEY_F3, KEY_F4, KEY_F5, KEY_F6, KEY_F7, + KEY_F8, KEY_F9, KEY_F10, KEY_F11, KEY_F12, 0, 0, 0 +}; + +struct skbd { + unsigned char keycode[128]; + struct input_dev *dev; + struct serio *serio; + char phys[32]; +}; + +static irqreturn_t skbd_interrupt(struct serio *serio, unsigned char data, + unsigned int flags, struct pt_regs *regs) +{ + struct skbd *skbd = serio_get_drvdata(serio); + struct input_dev *dev = skbd->dev; + + if (skbd->keycode[data & SKBD_KEY_MASK]) { + input_regs(dev, regs); + input_report_key(dev, skbd->keycode[data & SKBD_KEY_MASK], + !(data & SKBD_RELEASE)); + input_sync(dev); + } + + return IRQ_HANDLED; +} + +static int skbd_connect(struct serio *serio, struct serio_driver *drv) +{ + struct skbd *skbd; + struct input_dev *input_dev; + int err = -ENOMEM; + int i; + + skbd = kzalloc(sizeof(struct skbd), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!skbd || !input_dev) + goto fail1; + + skbd->serio = serio; + skbd->dev = input_dev; + snprintf(skbd->phys, sizeof(skbd->phys), "%s/input0", serio->phys); + memcpy(skbd->keycode, skbd_keycode, sizeof(skbd->keycode)); + + input_dev->name = "Stowaway Keyboard"; + input_dev->phys = skbd->phys; + input_dev->id.bustype = BUS_RS232; + input_dev->id.vendor = SERIO_STOWAWAY; + input_dev->id.product = 0x0001; + input_dev->id.version = 0x0100; + input_dev->cdev.dev = &serio->dev; + input_dev->private = skbd; + + input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_REP); + input_dev->keycode = skbd->keycode; + input_dev->keycodesize = sizeof(unsigned char); + input_dev->keycodemax = ARRAY_SIZE(skbd_keycode); + for (i = 0; i < ARRAY_SIZE(skbd_keycode); i++) + set_bit(skbd_keycode[i], input_dev->keybit); + clear_bit(0, input_dev->keybit); + + serio_set_drvdata(serio, skbd); + + err = serio_open(serio, drv); + if (err) + goto fail2; + + err = input_register_device(skbd->dev); + if (err) + goto fail3; + + return 0; + + fail3: serio_close(serio); + fail2: serio_set_drvdata(serio, NULL); + fail1: input_free_device(input_dev); + kfree(skbd); + return err; +} + +static void skbd_disconnect(struct serio *serio) +{ + struct skbd *skbd = serio_get_drvdata(serio); + + serio_close(serio); + serio_set_drvdata(serio, NULL); + input_unregister_device(skbd->dev); + kfree(skbd); +} + +static struct serio_device_id skbd_serio_ids[] = { + { + .type = SERIO_RS232, + .proto = SERIO_STOWAWAY, + .id = SERIO_ANY, + .extra = SERIO_ANY, + }, + { 0 } +}; + +MODULE_DEVICE_TABLE(serio, skbd_serio_ids); + +static struct serio_driver skbd_drv = { + .driver = { + .name = "stowaway", + }, + .description = DRIVER_DESC, + .id_table = skbd_serio_ids, + .interrupt = skbd_interrupt, + .connect = skbd_connect, + .disconnect = skbd_disconnect, +}; + +static int __init skbd_init(void) +{ + serio_register_driver(&skbd_drv); + return 0; +} + +static void __exit skbd_exit(void) +{ + serio_unregister_driver(&skbd_drv); +} + +module_init(skbd_init); +module_exit(skbd_exit); -- cgit v1.2.3 From cde45f19ca0d2ff1ede01528a7629388d4139309 Mon Sep 17 00:00:00 2001 From: Reiner Herrmann Date: Sun, 1 Oct 2006 21:58:51 -0400 Subject: Input: wistron - fix setting up special buttons If either wifi or bluetooth button has been detected, the code would break off the loop. But there are laptops that have both types of buttons, so the loop has to continue checking. Signed-off-by: Reiner Herrmann Signed-off-by: Dmitry Torokhov --- drivers/input/misc/wistron_btns.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/misc/wistron_btns.c b/drivers/input/misc/wistron_btns.c index de0f46dd969..a172ea1dc97 100644 --- a/drivers/input/misc/wistron_btns.c +++ b/drivers/input/misc/wistron_btns.c @@ -248,13 +248,10 @@ static int __init dmi_matched(struct dmi_system_id *dmi) keymap = dmi->driver_data; for (key = keymap; key->type != KE_END; key++) { - if (key->type == KE_WIFI) { + if (key->type == KE_WIFI) have_wifi = 1; - break; - } else if (key->type == KE_BLUETOOTH) { + else if (key->type == KE_BLUETOOTH) have_bluetooth = 1; - break; - } } return 1; } -- cgit v1.2.3 From bb0885900de49b5822d7e8c91c1adf9a0fcc228b Mon Sep 17 00:00:00 2001 From: Ashutosh Naik Date: Sun, 1 Oct 2006 22:07:14 -0400 Subject: Input: wistron - add support for Acer TravelMate 2424NWXCi The key mappings are the same as the older Acer TravelMate 240. Signed-off-by: Ashutosh Naik Signed-off-by: Andrew Morton Signed-off-by: Dmitry Torokhov --- drivers/input/misc/wistron_btns.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) (limited to 'drivers/input') diff --git a/drivers/input/misc/wistron_btns.c b/drivers/input/misc/wistron_btns.c index a172ea1dc97..4639537336f 100644 --- a/drivers/input/misc/wistron_btns.c +++ b/drivers/input/misc/wistron_btns.c @@ -386,7 +386,16 @@ static struct dmi_system_id dmi_ids[] __initdata = { }, .driver_data = keymap_acer_travelmate_240 }, - { + { + .callback = dmi_matched, + .ident = "Acer TravelMate 2424NWXCi", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 2420"), + }, + .driver_data = keymap_acer_travelmate_240 + }, + { .callback = dmi_matched, .ident = "AOpen 1559AS", .matches = { -- cgit v1.2.3