diff options
author | Linus Torvalds <torvalds@g5.osdl.org> | 2006-10-02 08:20:33 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@g5.osdl.org> | 2006-10-02 08:20:33 -0700 |
commit | a12f66fccf2e266ad197df142b5ebafc6a169a8c (patch) | |
tree | 9d0bc76f8aa9c42fb44ce5f5bf6b4b09f4efafed /drivers/input | |
parent | 12dce6263d43daeb4e16fa4eb964c1c99fa4fa2e (diff) | |
parent | bb0885900de49b5822d7e8c91c1adf9a0fcc228b (diff) | |
download | op-kernel-dev-a12f66fccf2e266ad197df142b5ebafc6a169a8c.zip op-kernel-dev-a12f66fccf2e266ad197df142b5ebafc6a169a8c.tar.gz |
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input
* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input: (35 commits)
Input: wistron - add support for Acer TravelMate 2424NWXCi
Input: wistron - fix setting up special buttons
Input: add KEY_BLUETOOTH and KEY_WLAN definitions
Input: add new BUS_VIRTUAL bus type
Input: add driver for stowaway serial keyboards
Input: make input_register_handler() return error codes
Input: remove cruft that was needed for transition to sysfs
Input: fix input module refcounting
Input: constify input core
Input: libps2 - rearrange exports
Input: atkbd - support Microsoft Natural Elite Pro keyboards
Input: i8042 - disable MUX mode on Toshiba Equium A110
Input: i8042 - get rid of polling timer
Input: send key up events at disconnect
Input: constify psmouse driver
Input: i8042 - add Amoi to the MUX blacklist
Input: logips2pp - add sugnature 56 (Cordless MouseMan Wheel), cleanup
Input: add driver for Touchwin serial touchscreens
Input: add driver for Touchright serial touchscreens
Input: add driver for Penmount serial touchscreens
...
Diffstat (limited to 'drivers/input')
39 files changed, 2716 insertions, 789 deletions
diff --git a/drivers/input/Kconfig b/drivers/input/Kconfig index 58223b5..9623231 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 1a6ff49..a005b1d 100644 --- a/drivers/input/Makefile +++ b/drivers/input/Makefile @@ -4,7 +4,11 @@ # Each configuration option enables a list of files. -obj-$(CONFIG_INPUT) += input.o +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/evbug.c b/drivers/input/evbug.c index 07358fb..5a9653c 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 */ }; @@ -89,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 4bf4818..6439f37 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; @@ -604,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, @@ -619,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; @@ -669,6 +667,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) @@ -677,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 */ }; @@ -696,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/ff-core.c b/drivers/input/ff-core.c new file mode 100644 index 0000000..35656ca --- /dev/null +++ b/drivers/input/ff-core.c @@ -0,0 +1,367 @@ +/* + * Force feedback support for Linux input subsystem + * + * Copyright (c) 2006 Anssi Hannula <anssi.hannula@gmail.com> + * Copyright (c) 2006 Dmitry Torokhov <dtor@mail.ru> + */ + +/* + * 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 <linux/input.h> +#include <linux/module.h> +#include <linux/mutex.h> + +/* + * 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/ff-memless.c b/drivers/input/ff-memless.c new file mode 100644 index 0000000..cd8b729 --- /dev/null +++ b/drivers/input/ff-memless.c @@ -0,0 +1,515 @@ +/* + * Force feedback support for memoryless devices + * + * Copyright (c) 2006 Anssi Hannula <anssi.hannula@gmail.com> + * Copyright (c) 2006 Dmitry Torokhov <dtor@mail.ru> + */ + +/* + * 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 <linux/input.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/spinlock.h> +#include <linux/sched.h> + +#include "fixp-arith.h" + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Anssi Hannula <anssi.hannula@gmail.com>"); +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); diff --git a/drivers/input/fixp-arith.h b/drivers/input/fixp-arith.h new file mode 100644 index 0000000..ed3d2da --- /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 <deneux@ifrance.com> + */ + +#include <linux/types.h> + +/* The type representing fixed-point values */ +typedef s16 fixp_t; + +#define FRAC_N 8 +#define FRAC_MASK ((1<<FRAC_N)-1) + +/* Not to be used directly. Use fixp_{cos,sin} */ +static const fixp_t cos_table[46] = { + 0x0100, 0x00FF, 0x00FF, 0x00FE, 0x00FD, 0x00FC, 0x00FA, 0x00F8, + 0x00F6, 0x00F3, 0x00F0, 0x00ED, 0x00E9, 0x00E6, 0x00E2, 0x00DD, + 0x00D9, 0x00D4, 0x00CF, 0x00C9, 0x00C4, 0x00BE, 0x00B8, 0x00B1, + 0x00AB, 0x00A4, 0x009D, 0x0096, 0x008F, 0x0087, 0x0080, 0x0078, + 0x0070, 0x0068, 0x005F, 0x0057, 0x004F, 0x0046, 0x003D, 0x0035, + 0x002C, 0x0023, 0x001A, 0x0011, 0x0008, 0x0000 +}; + + +/* a: 123 -> 123.0 */ +static inline fixp_t fixp_new(s16 a) +{ + return a<<FRAC_N; +} + +/* a: 0xFFFF -> -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 diff --git a/drivers/input/input.c b/drivers/input/input.c index 9cb4b9a..1c8c8a5 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; @@ -309,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; @@ -762,7 +767,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); } @@ -899,12 +906,13 @@ 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); INIT_LIST_HEAD(&dev->h_list); INIT_LIST_HEAD(&dev->node); + + __module_get(THIS_MODULE); } return dev; @@ -929,17 +937,10 @@ 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; - 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 : "<Unknown>"); - return -EINVAL; - } - set_bit(EV_SYN, dev->evbit); /* @@ -955,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); @@ -978,8 +977,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"); @@ -1008,9 +1005,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); @@ -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; - struct input_device_id *id; - - if (!handler) - return; + const struct input_device_id *id; 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 d671575..9f3529a 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) }, @@ -605,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/joystick/iforce/iforce-ff.c b/drivers/input/joystick/iforce/iforce-ff.c index 50c9076..8fb0c19 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 b4914e7..24c684b 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; i<dev->ff_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; i<dev->ff_effects_max; ++i) { - if (test_bit(FF_CORE_IS_USED, iforce->core_effects[i].flags)) - break; - } - if (i<dev->ff_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 76cb1f8..8632d47 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<LO(cmd); j+=2) { + for (j = 3; j < LO(cmd); j += 2) mark_core_as_ready(iforce, data[j] | (data[j+1]<<8)); - } } break; } diff --git a/drivers/input/joystick/iforce/iforce.h b/drivers/input/joystick/iforce/iforce.h index e9924d6..947df27 100644 --- a/drivers/input/joystick/iforce/iforce.h +++ b/drivers/input/joystick/iforce/iforce.h @@ -51,10 +51,7 @@ #define IFORCE_232 1 #define IFORCE_USB 2 -#define FALSE 0 -#define TRUE 1 - -#define FF_EFFECTS_MAX 32 +#define IFORCE_EFFECTS_MAX 32 /* Each force feedback effect is made of one core effect, which can be * associated to at most to effect modifiers @@ -67,24 +64,11 @@ #define FF_CORE_UPDATE 5 /* Effect is being updated */ #define FF_MODCORE_MAX 5 -#define CHECK_OWNERSHIP(i, iforce) \ - ((i) < FF_EFFECTS_MAX && i >= 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; diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig index 83eac3a..c62e00c 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 b265391..4c79e7b 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/atkbd.c b/drivers/input/keyboard/atkbd.c index a86afd0..40244d4 100644 --- a/drivers/input/keyboard/atkbd.c +++ b/drivers/input/keyboard/atkbd.c @@ -652,9 +652,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/keyboard/stowaway.c b/drivers/input/keyboard/stowaway.c new file mode 100644 index 0000000..04c54c5 --- /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 <marek.vasut@gmail.com>, or by paper mail: + * Marek Vasut, Liskovecka 559, Frydek-Mistek, 738 01 Czech Republic + */ + +#include <linux/slab.h> +#include <linux/module.h> +#include <linux/input.h> +#include <linux/init.h> +#include <linux/serio.h> + +#define DRIVER_DESC "Stowaway keyboard driver" + +MODULE_AUTHOR("Marek Vasut <marek.vasut@gmail.com>"); +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); diff --git a/drivers/input/misc/uinput.c b/drivers/input/misc/uinput.c index d723e9a..9516439b7 100644 --- a/drivers/input/misc/uinput.c +++ b/drivers/input/misc/uinput.c @@ -20,6 +20,9 @@ * Author: Aristeu Sergio Rozanski Filho <aris@cathedrallabs.org> * * Changes/Revisions: + * 0.3 09/04/2006 (Anssi Hannula <anssi.hannula@gmail.com>) + * - updated ff support for the changes in kernel interface + * - added MODULE_VERSION * 0.2 16/10/2004 (Micah Dowty <micah@navi.cx>) * - 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); diff --git a/drivers/input/misc/wistron_btns.c b/drivers/input/misc/wistron_btns.c index de0f46d..4639537 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; } @@ -389,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 = { diff --git a/drivers/input/mouse/alps.c b/drivers/input/mouse/alps.c index 070d753..450b68a 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 e428f8d..69db732 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 c14395b..5e9d250 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/logips2pp.c b/drivers/input/mouse/logips2pp.c index 54b696c..7972eec 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 }, @@ -244,12 +245,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]; @@ -261,7 +261,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; @@ -327,7 +328,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; @@ -349,7 +350,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; diff --git a/drivers/input/mouse/psmouse-base.c b/drivers/input/mouse/psmouse-base.c index 343afa3..9fb7eb6 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 *); @@ -507,15 +507,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) @@ -652,7 +654,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", @@ -726,7 +728,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; @@ -738,9 +740,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++) { @@ -795,13 +797,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; } /* @@ -810,12 +814,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; } /* @@ -1031,7 +1037,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; @@ -1362,7 +1368,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))) @@ -1459,7 +1465,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 0023501..680b323 100644 --- a/drivers/input/mouse/sermouse.c +++ b/drivers/input/mouse/sermouse.c @@ -42,7 +42,7 @@ MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>"); 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 ad5d0a8..392108c 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; diff --git a/drivers/input/mousedev.c b/drivers/input/mousedev.c index 1f851ac..a22a74a 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) }, @@ -737,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); @@ -746,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 51a519e..ee82464 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) }, @@ -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/serio/i8042-x86ia64io.h b/drivers/input/serio/i8042-x86ia64io.h index f606e96..8738edd 100644 --- a/drivers/input/serio/i8042-x86ia64io.h +++ b/drivers/input/serio/i8042-x86ia64io.h @@ -160,6 +160,13 @@ static struct dmi_system_id __initdata i8042_dmi_nomux_table[] = { }, }, { + .ident = "Toshiba Equium A110", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"), + DMI_MATCH(DMI_PRODUCT_NAME, "EQUIUM A110"), + }, + }, + { .ident = "Alienware Sentia", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "ALIENWARE"), @@ -180,6 +187,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"), + }, + }, { } }; diff --git a/drivers/input/serio/i8042.c b/drivers/input/serio/i8042.c index 06a3f25..1bb0c76 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); } @@ -287,100 +276,6 @@ static int i8042_aux_write(struct serio *serio, unsigned char c) } /* - * 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 * start sending data through it. @@ -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" : ""); @@ -494,6 +387,58 @@ static irqreturn_t i8042_interrupt(int irq, void *dev_id, struct pt_regs *regs) } /* + * 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; @@ -738,25 +700,12 @@ 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 af526ab..b3eb7a7 100644 --- a/drivers/input/serio/i8042.h +++ b/drivers/input/serio/i8042.h @@ -37,15 +37,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. */ diff --git a/drivers/input/serio/libps2.c b/drivers/input/serio/libps2.c index ed202f2..dcb16b5 100644 --- a/drivers/input/serio/libps2.c +++ b/drivers/input/serio/libps2.c @@ -27,15 +27,6 @@ MODULE_AUTHOR("Dmitry Torokhov <dtor@mail.ru>"); 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); - /* Work structure to schedule execution of a command */ struct ps2work { struct work_struct work; @@ -71,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 @@ -96,15 +88,16 @@ 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 * 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 */ @@ -115,6 +108,7 @@ static inline 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 @@ -139,6 +133,19 @@ static int ps2_adjust_timeout(struct ps2dev *ps2dev, int command, int timeout) 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. */ @@ -237,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 @@ -279,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 @@ -290,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 @@ -335,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 @@ -360,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) { @@ -371,4 +383,4 @@ void ps2_cmd_aborted(struct ps2dev *ps2dev) ps2dev->flags = 0; } - +EXPORT_SYMBOL(ps2_cmd_aborted); diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index b1b14f8..9418bbe 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -108,4 +108,40 @@ 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. + +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. + +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 5e5557c..1abb8f1 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -12,3 +12,6 @@ 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 +obj-$(CONFIG_TOUCHSCREEN_TOUCHRIGHT) += touchright.o +obj-$(CONFIG_TOUCHSCREEN_TOUCHWIN) += touchwin.o diff --git a/drivers/input/touchscreen/elo.c b/drivers/input/touchscreen/elo.c index c86a2eb..ab56533 100644 --- a/drivers/input/touchscreen/elo.c +++ b/drivers/input/touchscreen/elo.c @@ -23,6 +23,7 @@ #include <linux/input.h> #include <linux/serio.h> #include <linux/init.h> +#include <linux/ctype.h> #define DRIVER_DESC "Elo serial touchscreen driver" @@ -34,7 +35,19 @@ MODULE_LICENSE("GPL"); * Definitions & global arrays. */ -#define ELO_MAX_LENGTH 10 +#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. @@ -43,51 +56,67 @@ 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]; }; -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->csum = 0xaa; + if (data != ELO10_LEAD_BYTE) { + pr_debug("elo: unsynchronized data: 0x%02x\n", data); elo->idx = 0; - elo->csum = 0; - } - break; - - case 1: - if (data != 'T') { - elo->idx = 0; - elo->csum = 0; } break; case 9: - if (elo->csum) { + elo->idx = 0; + if (data != elo->csum) { + pr_debug("elo: bad checksum: 0x%02x, expected 0x%02x\n", + data, elo->csum); + break; + } + 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; + } + 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]); - 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]); + 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; } - elo->idx = 0; - elo->csum = 0; 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 +164,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 +190,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: @@ -181,17 +210,81 @@ 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() */ 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,12 +304,15 @@ 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; 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; @@ -231,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 */ @@ -253,17 +354,15 @@ static int elo_connect(struct serio *serio, struct serio_driver *drv) break; } - serio_set_drvdata(serio, elo); - - err = serio_open(serio, drv); + err = input_register_device(elo->dev); if (err) - goto fail; + 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; } diff --git a/drivers/input/touchscreen/penmount.c b/drivers/input/touchscreen/penmount.c new file mode 100644 index 0000000..f737010 --- /dev/null +++ b/drivers/input/touchscreen/penmount.c @@ -0,0 +1,185 @@ +/* + * Penmount serial touchscreen driver + * + * Copyright (c) 2006 Rick Koch <n1gp@hotmail.com> + * + * 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 <linux/errno.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/input.h> +#include <linux/serio.h> +#include <linux/init.h> + +#define DRIVER_DESC "Penmount serial touchscreen driver" + +MODULE_AUTHOR("Rick Koch <n1gp@hotmail.com>"); +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); diff --git a/drivers/input/touchscreen/touchright.c b/drivers/input/touchscreen/touchright.c new file mode 100644 index 0000000..1c89fa5 --- /dev/null +++ b/drivers/input/touchscreen/touchright.c @@ -0,0 +1,196 @@ +/* + * Touchright serial touchscreen driver + * + * Copyright (c) 2006 Rick Koch <n1gp@hotmail.com> + * + * Based on MicroTouch driver (drivers/input/touchscreen/mtouch.c) + * Copyright (c) 2004 Vojtech Pavlik + * and Dan Streetman <ddstreet@ieee.org> + */ + +/* + * 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 <linux/errno.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/input.h> +#include <linux/serio.h> +#include <linux/init.h> + +#define DRIVER_DESC "Touchright serial touchscreen driver" + +MODULE_AUTHOR("Rick Koch <n1gp@hotmail.com>"); +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); diff --git a/drivers/input/touchscreen/touchwin.c b/drivers/input/touchscreen/touchwin.c new file mode 100644 index 0000000..a7b4c75 --- /dev/null +++ b/drivers/input/touchscreen/touchwin.c @@ -0,0 +1,203 @@ +/* + * Touchwindow serial touchscreen driver + * + * Copyright (c) 2006 Rick Koch <n1gp@hotmail.com> + * + * Based on MicroTouch driver (drivers/input/touchscreen/mtouch.c) + * Copyright (c) 2004 Vojtech Pavlik + * and Dan Streetman <ddstreet@ieee.org> + */ + +/* + * 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 <linux/errno.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/input.h> +#include <linux/serio.h> +#include <linux/init.h> + +#define DRIVER_DESC "Touchwindow serial touchscreen driver" + +MODULE_AUTHOR("Rick Koch <n1gp@hotmail.com>"); +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); diff --git a/drivers/input/tsdev.c b/drivers/input/tsdev.c index 00e3929..a730c46 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) }, @@ -481,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) |