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 ...
This commit is contained in:
@@ -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
|
||||
|
@@ -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
|
||||
|
@@ -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)
|
||||
|
@@ -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;
|
||||
|
||||
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;
|
||||
error = input_ff_upload(dev, &effect, file);
|
||||
|
||||
if (put_user(effect.id, &(((struct ff_effect __user *)p)->id)))
|
||||
return -EFAULT;
|
||||
|
||||
return error;
|
||||
|
||||
case EVIOCRMFF:
|
||||
if (!dev->erase_effect)
|
||||
return -ENOSYS;
|
||||
|
||||
return dev->erase_effect(dev, (int)(unsigned long) p);
|
||||
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)
|
||||
|
367
drivers/input/ff-core.c
Normal file
367
drivers/input/ff-core.c
Normal file
@@ -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);
|
515
drivers/input/ff-memless.c
Normal file
515
drivers/input/ff-memless.c
Normal file
@@ -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);
|
87
drivers/input/fixp-arith.h
Normal file
87
drivers/input/fixp-arith.h
Normal file
@@ -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
|
@@ -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);
|
||||
|
||||
|
@@ -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)
|
||||
|
@@ -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,
|
||||
|
@@ -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_playback(struct input_dev *dev, int effect_id, int value)
|
||||
{
|
||||
struct iforce* iforce = dev->private;
|
||||
struct iforce_core_effect *core_effect = &iforce->core_effects[effect_id];
|
||||
|
||||
if (value > 0)
|
||||
set_bit(FF_CORE_SHOULD_PLAY, core_effect->flags);
|
||||
else
|
||||
clear_bit(FF_CORE_SHOULD_PLAY, core_effect->flags);
|
||||
|
||||
static int iforce_input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
|
||||
iforce_control_playback(iforce, effect_id, value);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void iforce_set_gain(struct input_dev *dev, u16 gain)
|
||||
{
|
||||
struct iforce* iforce = dev->private;
|
||||
unsigned char data[3];
|
||||
|
||||
if (type != EV_FF)
|
||||
return -1;
|
||||
data[0] = gain >> 9;
|
||||
iforce_send_packet(iforce, FF_CMD_GAIN, data);
|
||||
}
|
||||
|
||||
switch (code) {
|
||||
static void iforce_set_autocenter(struct input_dev *dev, u16 magnitude)
|
||||
{
|
||||
struct iforce* iforce = dev->private;
|
||||
unsigned char data[3];
|
||||
|
||||
case FF_GAIN:
|
||||
data[0] = 0x03;
|
||||
data[1] = magnitude >> 9;
|
||||
iforce_send_packet(iforce, FF_CMD_AUTOCENTER, data);
|
||||
|
||||
data[0] = value >> 9;
|
||||
iforce_send_packet(iforce, FF_CMD_GAIN, data);
|
||||
|
||||
return 0;
|
||||
|
||||
case FF_AUTOCENTER:
|
||||
|
||||
data[0] = 0x03;
|
||||
data[1] = value >> 9;
|
||||
iforce_send_packet(iforce, FF_CMD_AUTOCENTER, data);
|
||||
|
||||
data[0] = 0x04;
|
||||
data[1] = 0x01;
|
||||
iforce_send_packet(iforce, FF_CMD_AUTOCENTER, data);
|
||||
|
||||
return 0;
|
||||
|
||||
default: /* Play or stop an effect */
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
iforce_control_playback(iforce, code, value);
|
||||
return 0;
|
||||
}
|
||||
|
||||
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,33 +203,11 @@ static int iforce_open(struct input_dev *dev)
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Enable force feedback */
|
||||
iforce_send_packet(iforce, FF_CMD_ENABLE, "\004");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int iforce_flush(struct input_dev *dev, struct file *file)
|
||||
{
|
||||
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)) {
|
||||
/* Enable force feedback */
|
||||
iforce_send_packet(iforce, FF_CMD_ENABLE, "\004");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -295,17 +216,18 @@ 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");
|
||||
}
|
||||
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");
|
||||
/* 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.
|
||||
*/
|
||||
|
@@ -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;
|
||||
}
|
||||
|
@@ -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;
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
|
@@ -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];
|
||||
|
187
drivers/input/keyboard/stowaway.c
Normal file
187
drivers/input/keyboard/stowaway.c
Normal file
@@ -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);
|
@@ -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);
|
||||
|
@@ -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 = {
|
||||
|
@@ -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;
|
||||
|
@@ -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 */
|
||||
};
|
||||
|
||||
|
@@ -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)
|
||||
|
@@ -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;
|
||||
|
@@ -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;
|
||||
|
@@ -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"};
|
||||
|
||||
|
@@ -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;
|
||||
|
@@ -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");
|
||||
|
@@ -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)
|
||||
|
@@ -159,6 +159,13 @@ static struct dmi_system_id __initdata i8042_dmi_nomux_table[] = {
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "Satellite P10"),
|
||||
},
|
||||
},
|
||||
{
|
||||
.ident = "Toshiba Equium A110",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "EQUIUM A110"),
|
||||
},
|
||||
},
|
||||
{
|
||||
.ident = "Alienware Sentia",
|
||||
.matches = {
|
||||
@@ -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"),
|
||||
},
|
||||
},
|
||||
{ }
|
||||
};
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -36,15 +36,6 @@
|
||||
|
||||
#define I8042_CTL_TIMEOUT 10000
|
||||
|
||||
/*
|
||||
* When the device isn't opened and it's interrupts aren't used, we poll it at
|
||||
* regular intervals to see if any characters arrived. If yes, we can start
|
||||
* probing for any mouse / keyboard connected. This is the period of the
|
||||
* polling.
|
||||
*/
|
||||
|
||||
#define I8042_POLL_PERIOD HZ/20
|
||||
|
||||
/*
|
||||
* Status register bits.
|
||||
*/
|
||||
|
@@ -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
|
||||
@@ -138,6 +132,19 @@ static int ps2_adjust_timeout(struct ps2dev *ps2dev, int command, int timeout)
|
||||
break;
|
||||
|
||||
case PS2_CMD_GETID:
|
||||
/*
|
||||
* Microsoft Natural Elite keyboard responds to
|
||||
* the GET ID command as it were a mouse, with
|
||||
* a single byte. Fail the command so atkbd will
|
||||
* use alternative probe to detect it.
|
||||
*/
|
||||
if (ps2dev->cmdbuf[1] == 0xaa) {
|
||||
serio_pause_rx(ps2dev->serio);
|
||||
ps2dev->flags = 0;
|
||||
serio_continue_rx(ps2dev->serio);
|
||||
timeout = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* If device behind the port is not a keyboard there
|
||||
* won't be 2nd byte of ID response.
|
||||
@@ -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);
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
|
@@ -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;
|
||||
}
|
||||
|
185
drivers/input/touchscreen/penmount.c
Normal file
185
drivers/input/touchscreen/penmount.c
Normal file
@@ -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);
|
196
drivers/input/touchscreen/touchright.c
Normal file
196
drivers/input/touchscreen/touchright.c
Normal file
@@ -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);
|
203
drivers/input/touchscreen/touchwin.c
Normal file
203
drivers/input/touchscreen/touchwin.c
Normal file
@@ -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);
|
@@ -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)
|
||||
|
Reference in New Issue
Block a user