forked from Minki/linux
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:
commit
a12f66fccf
@ -1,67 +1,37 @@
|
||||
Force feedback for Linux.
|
||||
By Johann Deneux <deneux@ifrance.com> on 2001/04/22.
|
||||
Updated by Anssi Hannula <anssi.hannula@gmail.com> on 2006/04/09.
|
||||
You may redistribute this file. Please remember to include shape.fig and
|
||||
interactive.fig as well.
|
||||
----------------------------------------------------------------------------
|
||||
|
||||
0. Introduction
|
||||
1. Introduction
|
||||
~~~~~~~~~~~~~~~
|
||||
This document describes how to use force feedback devices under Linux. The
|
||||
goal is not to support these devices as if they were simple input-only devices
|
||||
(as it is already the case), but to really enable the rendering of force
|
||||
effects.
|
||||
At the moment, only I-Force devices are supported, and not officially. That
|
||||
means I had to find out how the protocol works on my own. Of course, the
|
||||
information I managed to grasp is far from being complete, and I can not
|
||||
guarranty that this driver will work for you.
|
||||
This document only describes the force feedback part of the driver for I-Force
|
||||
devices. Please read joystick.txt before reading further this document.
|
||||
This document only describes the force feedback part of the Linux input
|
||||
interface. Please read joystick.txt and input.txt before reading further this
|
||||
document.
|
||||
|
||||
2. Instructions to the user
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Here are instructions on how to compile and use the driver. In fact, this
|
||||
driver is the normal iforce, input and evdev drivers written by Vojtech
|
||||
Pavlik, plus additions to support force feedback.
|
||||
To enable force feedback, you have to:
|
||||
|
||||
1. have your kernel configured with evdev and a driver that supports your
|
||||
device.
|
||||
2. make sure evdev module is loaded and /dev/input/event* device files are
|
||||
created.
|
||||
|
||||
Before you start, let me WARN you that some devices shake violently during the
|
||||
initialisation phase. This happens for example with my "AVB Top Shot Pegasus".
|
||||
To stop this annoying behaviour, move you joystick to its limits. Anyway, you
|
||||
should keep a hand on your device, in order to avoid it to brake down if
|
||||
should keep a hand on your device, in order to avoid it to break down if
|
||||
something goes wrong.
|
||||
|
||||
At the kernel's compilation:
|
||||
- Enable IForce/Serial
|
||||
- Enable Event interface
|
||||
|
||||
Compile the modules, install them.
|
||||
|
||||
You also need inputattach.
|
||||
|
||||
You then need to insert the modules into the following order:
|
||||
% modprobe joydev
|
||||
% modprobe serport # Only for serial
|
||||
% modprobe iforce
|
||||
% modprobe evdev
|
||||
% ./inputattach -ifor $2 & # Only for serial
|
||||
If you are using USB, you don't need the inputattach step.
|
||||
|
||||
Please check that you have all the /dev/input entries needed:
|
||||
cd /dev
|
||||
rm js*
|
||||
mkdir input
|
||||
mknod input/js0 c 13 0
|
||||
mknod input/js1 c 13 1
|
||||
mknod input/js2 c 13 2
|
||||
mknod input/js3 c 13 3
|
||||
ln -s input/js0 js0
|
||||
ln -s input/js1 js1
|
||||
ln -s input/js2 js2
|
||||
ln -s input/js3 js3
|
||||
|
||||
mknod input/event0 c 13 64
|
||||
mknod input/event1 c 13 65
|
||||
mknod input/event2 c 13 66
|
||||
mknod input/event3 c 13 67
|
||||
If you have a serial iforce device, you need to start inputattach. See
|
||||
joystick.txt for details.
|
||||
|
||||
2.1 Does it work ?
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
@ -70,9 +40,9 @@ There is an utility called fftest that will allow you to test the driver.
|
||||
|
||||
3. Instructions to the developper
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
All interactions are done using the event API. That is, you can use ioctl()
|
||||
All interactions are done using the event API. That is, you can use ioctl()
|
||||
and write() on /dev/input/eventXX.
|
||||
This information is subject to change.
|
||||
This information is subject to change.
|
||||
|
||||
3.1 Querying device capabilities
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
@ -86,18 +56,29 @@ int ioctl(int file_descriptor, int request, unsigned long *features);
|
||||
|
||||
Returns the features supported by the device. features is a bitfield with the
|
||||
following bits:
|
||||
- FF_X has an X axis (usually joysticks)
|
||||
- FF_Y has an Y axis (usually joysticks)
|
||||
- FF_WHEEL has a wheel (usually sterring wheels)
|
||||
- FF_CONSTANT can render constant force effects
|
||||
- FF_PERIODIC can render periodic effects (sine, triangle, square...)
|
||||
- FF_PERIODIC can render periodic effects with the following waveforms:
|
||||
- FF_SQUARE square waveform
|
||||
- FF_TRIANGLE triangle waveform
|
||||
- FF_SINE sine waveform
|
||||
- FF_SAW_UP sawtooth up waveform
|
||||
- FF_SAW_DOWN sawtooth down waveform
|
||||
- FF_CUSTOM custom waveform
|
||||
- FF_RAMP can render ramp effects
|
||||
- FF_SPRING can simulate the presence of a spring
|
||||
- FF_FRICTION can simulate friction
|
||||
- FF_FRICTION can simulate friction
|
||||
- FF_DAMPER can simulate damper effects
|
||||
- FF_RUMBLE rumble effects (normally the only effect supported by rumble
|
||||
pads)
|
||||
- FF_RUMBLE rumble effects
|
||||
- FF_INERTIA can simulate inertia
|
||||
- FF_GAIN gain is adjustable
|
||||
- FF_AUTOCENTER autocenter is adjustable
|
||||
|
||||
Note: In most cases you should use FF_PERIODIC instead of FF_RUMBLE. All
|
||||
devices that support FF_RUMBLE support FF_PERIODIC (square, triangle,
|
||||
sine) and the other way around.
|
||||
|
||||
Note: The exact syntax FF_CUSTOM is undefined for the time being as no driver
|
||||
supports it yet.
|
||||
|
||||
|
||||
int ioctl(int fd, EVIOCGEFFECTS, int *n);
|
||||
@ -108,7 +89,7 @@ Returns the number of effects the device can keep in its memory.
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
#include <linux/input.h>
|
||||
#include <sys/ioctl.h>
|
||||
|
||||
|
||||
int ioctl(int file_descriptor, int request, struct ff_effect *effect);
|
||||
|
||||
"request" must be EVIOCSFF.
|
||||
@ -120,6 +101,9 @@ to the unique id assigned by the driver. This data is required for performing
|
||||
some operations (removing an effect, controlling the playback).
|
||||
This if field must be set to -1 by the user in order to tell the driver to
|
||||
allocate a new effect.
|
||||
|
||||
Effects are file descriptor specific.
|
||||
|
||||
See <linux/input.h> for a description of the ff_effect struct. You should also
|
||||
find help in a few sketches, contained in files shape.fig and interactive.fig.
|
||||
You need xfig to visualize these files.
|
||||
@ -128,8 +112,8 @@ You need xfig to visualize these files.
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
int ioctl(int fd, EVIOCRMFF, effect.id);
|
||||
|
||||
This makes room for new effects in the device's memory. Please note this won't
|
||||
stop the effect if it was playing.
|
||||
This makes room for new effects in the device's memory. Note that this also
|
||||
stops the effect if it was playing.
|
||||
|
||||
3.4 Controlling the playback of effects
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
@ -149,22 +133,21 @@ Control of playing is done with write(). Below is an example:
|
||||
play.type = EV_FF;
|
||||
play.code = effect.id;
|
||||
play.value = 3;
|
||||
|
||||
|
||||
write(fd, (const void*) &play, sizeof(play));
|
||||
...
|
||||
/* Stop an effect */
|
||||
stop.type = EV_FF;
|
||||
stop.code = effect.id;
|
||||
stop.value = 0;
|
||||
|
||||
|
||||
write(fd, (const void*) &play, sizeof(stop));
|
||||
|
||||
3.5 Setting the gain
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
Not all devices have the same strength. Therefore, users should set a gain
|
||||
factor depending on how strong they want effects to be. This setting is
|
||||
persistent across access to the driver, so you should not care about it if
|
||||
you are writing games, as another utility probably already set this for you.
|
||||
persistent across access to the driver.
|
||||
|
||||
/* Set the gain of the device
|
||||
int gain; /* between 0 and 100 */
|
||||
@ -204,11 +187,14 @@ type of device, not all parameters can be dynamically updated. For example,
|
||||
the direction of an effect cannot be updated with iforce devices. In this
|
||||
case, the driver stops the effect, up-load it, and restart it.
|
||||
|
||||
Therefore it is recommended to dynamically change direction while the effect
|
||||
is playing only when it is ok to restart the effect with a replay count of 1.
|
||||
|
||||
3.8 Information about the status of effects
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Every time the status of an effect is changed, an event is sent. The values
|
||||
and meanings of the fields of the event are as follows:
|
||||
|
||||
struct input_event {
|
||||
/* When the status of the effect changed */
|
||||
struct timeval time;
|
||||
@ -225,3 +211,9 @@ struct input_event {
|
||||
|
||||
FF_STATUS_STOPPED The effect stopped playing
|
||||
FF_STATUS_PLAYING The effect started to play
|
||||
|
||||
NOTE: Status feedback is only supported by iforce driver. If you have
|
||||
a really good reason to use this, please contact
|
||||
linux-joystick@atrey.karlin.mff.cuni.cz or anssi.hannula@gmail.com
|
||||
so that support for it can be added to the rest of the drivers.
|
||||
|
||||
|
@ -1293,7 +1293,7 @@ static void kbd_event(struct input_handle *handle, unsigned int event_type,
|
||||
*/
|
||||
static struct input_handle *kbd_connect(struct input_handler *handler,
|
||||
struct input_dev *dev,
|
||||
struct input_device_id *id)
|
||||
const struct input_device_id *id)
|
||||
{
|
||||
struct input_handle *handle;
|
||||
int i;
|
||||
@ -1342,7 +1342,7 @@ static void kbd_start(struct input_handle *handle)
|
||||
tasklet_enable(&keyboard_tasklet);
|
||||
}
|
||||
|
||||
static struct input_device_id kbd_ids[] = {
|
||||
static const struct input_device_id kbd_ids[] = {
|
||||
{
|
||||
.flags = INPUT_DEVICE_ID_MATCH_EVBIT,
|
||||
.evbit = { BIT(EV_KEY) },
|
||||
@ -1370,6 +1370,7 @@ static struct input_handler kbd_handler = {
|
||||
int __init kbd_init(void)
|
||||
{
|
||||
int i;
|
||||
int error;
|
||||
|
||||
for (i = 0; i < MAX_NR_CONSOLES; i++) {
|
||||
kbd_table[i].ledflagstate = KBD_DEFLEDS;
|
||||
@ -1381,7 +1382,9 @@ int __init kbd_init(void)
|
||||
kbd_table[i].kbdmode = VC_XLATE;
|
||||
}
|
||||
|
||||
input_register_handler(&kbd_handler);
|
||||
error = input_register_handler(&kbd_handler);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
tasklet_enable(&keyboard_tasklet);
|
||||
tasklet_schedule(&keyboard_tasklet);
|
||||
|
@ -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);
|
@ -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)
|
||||
|
@ -60,16 +60,17 @@ config HID_FF
|
||||
If unsure, say N.
|
||||
|
||||
config HID_PID
|
||||
bool "PID Devices (Microsoft Sidewinder Force Feedback 2)"
|
||||
bool "PID device support"
|
||||
depends on HID_FF
|
||||
help
|
||||
Say Y here if you have a PID-compliant joystick and wish to enable force
|
||||
feedback for it. The Microsoft Sidewinder Force Feedback 2 is one such
|
||||
device.
|
||||
Say Y here if you have a PID-compliant device and wish to enable force
|
||||
feedback for it. Microsoft Sidewinder Force Feedback 2 is one of such
|
||||
devices.
|
||||
|
||||
config LOGITECH_FF
|
||||
bool "Logitech WingMan *3D support"
|
||||
depends on HID_FF
|
||||
select INPUT_FF_MEMLESS if USB_HID
|
||||
help
|
||||
Say Y here if you have one of these devices:
|
||||
- Logitech WingMan Cordless RumblePad
|
||||
@ -81,12 +82,21 @@ config LOGITECH_FF
|
||||
config THRUSTMASTER_FF
|
||||
bool "ThrustMaster FireStorm Dual Power 2 support (EXPERIMENTAL)"
|
||||
depends on HID_FF && EXPERIMENTAL
|
||||
select INPUT_FF_MEMLESS if USB_HID
|
||||
help
|
||||
Say Y here if you have a THRUSTMASTER FireStore Dual Power 2,
|
||||
and want to enable force feedback support for it.
|
||||
Note: if you say N here, this device will still be supported, but without
|
||||
force feedback.
|
||||
|
||||
config ZEROPLUS_FF
|
||||
bool "Zeroplus based game controller support"
|
||||
depends on HID_FF
|
||||
select INPUT_FF_MEMLESS if USB_HID
|
||||
help
|
||||
Say Y here if you have a Zeroplus based game controller and want to
|
||||
enable force feedback for it.
|
||||
|
||||
config USB_HIDDEV
|
||||
bool "/dev/hiddev raw HID device support"
|
||||
depends on USB_HID
|
||||
|
@ -15,7 +15,7 @@ ifeq ($(CONFIG_USB_HIDINPUT),y)
|
||||
usbhid-objs += hid-input.o
|
||||
endif
|
||||
ifeq ($(CONFIG_HID_PID),y)
|
||||
usbhid-objs += pid.o
|
||||
usbhid-objs += hid-pidff.o
|
||||
endif
|
||||
ifeq ($(CONFIG_LOGITECH_FF),y)
|
||||
usbhid-objs += hid-lgff.o
|
||||
@ -23,6 +23,9 @@ endif
|
||||
ifeq ($(CONFIG_THRUSTMASTER_FF),y)
|
||||
usbhid-objs += hid-tmff.o
|
||||
endif
|
||||
ifeq ($(CONFIG_ZEROPLUS_FF),y)
|
||||
usbhid-objs += hid-zpff.o
|
||||
endif
|
||||
ifeq ($(CONFIG_HID_FF),y)
|
||||
usbhid-objs += hid-ff.o
|
||||
endif
|
||||
|
@ -543,8 +543,6 @@ static void hid_free_device(struct hid_device *device)
|
||||
{
|
||||
unsigned i,j;
|
||||
|
||||
hid_ff_exit(device);
|
||||
|
||||
for (i = 0; i < HID_REPORT_TYPES; i++) {
|
||||
struct hid_report_enum *report_enum = device->report_enum + i;
|
||||
|
||||
@ -1109,7 +1107,7 @@ int hid_set_field(struct hid_field *field, unsigned offset, __s32 value)
|
||||
/*
|
||||
* Find a report field with a specified HID usage.
|
||||
*/
|
||||
|
||||
#if 0
|
||||
struct hid_field *hid_find_field_by_usage(struct hid_device *hid, __u32 wanted_usage, int type)
|
||||
{
|
||||
struct hid_report *report;
|
||||
@ -1121,6 +1119,7 @@ struct hid_field *hid_find_field_by_usage(struct hid_device *hid, __u32 wanted_u
|
||||
return report->field[i];
|
||||
return NULL;
|
||||
}
|
||||
#endif /* 0 */
|
||||
|
||||
static int hid_submit_out(struct hid_device *hid)
|
||||
{
|
||||
|
@ -44,45 +44,38 @@ struct hid_ff_initializer {
|
||||
int (*init)(struct hid_device*);
|
||||
};
|
||||
|
||||
/*
|
||||
* We try pidff when no other driver is found because PID is the
|
||||
* standards compliant way of implementing force feedback in HID.
|
||||
* pidff_init() will quickly abort if the device doesn't appear to
|
||||
* be a PID device
|
||||
*/
|
||||
static struct hid_ff_initializer inits[] = {
|
||||
#ifdef CONFIG_LOGITECH_FF
|
||||
{0x46d, 0xc211, hid_lgff_init}, // Logitech Cordless rumble pad
|
||||
{0x46d, 0xc283, hid_lgff_init}, // Logitech Wingman Force 3d
|
||||
{0x46d, 0xc295, hid_lgff_init}, // Logitech MOMO force wheel
|
||||
{0x46d, 0xc219, hid_lgff_init}, // Logitech Cordless rumble pad 2
|
||||
#endif
|
||||
#ifdef CONFIG_HID_PID
|
||||
{0x45e, 0x001b, hid_pid_init},
|
||||
{ 0x46d, 0xc211, hid_lgff_init }, /* Logitech Cordless rumble pad */
|
||||
{ 0x46d, 0xc283, hid_lgff_init }, /* Logitech Wingman Force 3d */
|
||||
{ 0x46d, 0xc295, hid_lgff_init }, /* Logitech MOMO force wheel */
|
||||
{ 0x46d, 0xc219, hid_lgff_init }, /* Logitech Cordless rumble pad 2 */
|
||||
#endif
|
||||
#ifdef CONFIG_THRUSTMASTER_FF
|
||||
{0x44f, 0xb304, hid_tmff_init},
|
||||
{ 0x44f, 0xb304, hid_tmff_init },
|
||||
#endif
|
||||
{0, 0, NULL} /* Terminating entry */
|
||||
#ifdef CONFIG_ZEROPLUS_FF
|
||||
{ 0xc12, 0x0005, hid_zpff_init },
|
||||
{ 0xc12, 0x0030, hid_zpff_init },
|
||||
#endif
|
||||
{ 0, 0, hid_pidff_init} /* Matches anything */
|
||||
};
|
||||
|
||||
static struct hid_ff_initializer *hid_get_ff_init(__u16 idVendor,
|
||||
__u16 idProduct)
|
||||
{
|
||||
struct hid_ff_initializer *init;
|
||||
for (init = inits;
|
||||
init->idVendor
|
||||
&& !(init->idVendor == idVendor
|
||||
&& init->idProduct == idProduct);
|
||||
init++);
|
||||
|
||||
return init->idVendor? init : NULL;
|
||||
}
|
||||
|
||||
int hid_ff_init(struct hid_device* hid)
|
||||
{
|
||||
struct hid_ff_initializer *init;
|
||||
int vendor = le16_to_cpu(hid->dev->descriptor.idVendor);
|
||||
int product = le16_to_cpu(hid->dev->descriptor.idProduct);
|
||||
|
||||
init = hid_get_ff_init(le16_to_cpu(hid->dev->descriptor.idVendor),
|
||||
le16_to_cpu(hid->dev->descriptor.idProduct));
|
||||
for (init = inits; init->idVendor; init++)
|
||||
if (init->idVendor == vendor && init->idProduct == product)
|
||||
break;
|
||||
|
||||
if (!init) {
|
||||
dbg("hid_ff_init could not find initializer");
|
||||
return -ENOSYS;
|
||||
}
|
||||
return init->init(hid);
|
||||
}
|
||||
|
@ -65,11 +65,9 @@ static const struct {
|
||||
#define map_rel(c) do { usage->code = c; usage->type = EV_REL; bit = input->relbit; max = REL_MAX; } while (0)
|
||||
#define map_key(c) do { usage->code = c; usage->type = EV_KEY; bit = input->keybit; max = KEY_MAX; } while (0)
|
||||
#define map_led(c) do { usage->code = c; usage->type = EV_LED; bit = input->ledbit; max = LED_MAX; } while (0)
|
||||
#define map_ff(c) do { usage->code = c; usage->type = EV_FF; bit = input->ffbit; max = FF_MAX; } while (0)
|
||||
|
||||
#define map_abs_clear(c) do { map_abs(c); clear_bit(c, bit); } while (0)
|
||||
#define map_key_clear(c) do { map_key(c); clear_bit(c, bit); } while (0)
|
||||
#define map_ff_effect(c) do { set_bit(c, input->ffbit); } while (0)
|
||||
|
||||
#ifdef CONFIG_USB_HIDINPUT_POWERBOOK
|
||||
|
||||
@ -525,23 +523,7 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel
|
||||
|
||||
case HID_UP_PID:
|
||||
|
||||
set_bit(EV_FF, input->evbit);
|
||||
switch(usage->hid & HID_USAGE) {
|
||||
case 0x26: map_ff_effect(FF_CONSTANT); goto ignore;
|
||||
case 0x27: map_ff_effect(FF_RAMP); goto ignore;
|
||||
case 0x28: map_ff_effect(FF_CUSTOM); goto ignore;
|
||||
case 0x30: map_ff_effect(FF_SQUARE); map_ff_effect(FF_PERIODIC); goto ignore;
|
||||
case 0x31: map_ff_effect(FF_SINE); map_ff_effect(FF_PERIODIC); goto ignore;
|
||||
case 0x32: map_ff_effect(FF_TRIANGLE); map_ff_effect(FF_PERIODIC); goto ignore;
|
||||
case 0x33: map_ff_effect(FF_SAW_UP); map_ff_effect(FF_PERIODIC); goto ignore;
|
||||
case 0x34: map_ff_effect(FF_SAW_DOWN); map_ff_effect(FF_PERIODIC); goto ignore;
|
||||
case 0x40: map_ff_effect(FF_SPRING); goto ignore;
|
||||
case 0x41: map_ff_effect(FF_DAMPER); goto ignore;
|
||||
case 0x42: map_ff_effect(FF_INERTIA); goto ignore;
|
||||
case 0x43: map_ff_effect(FF_FRICTION); goto ignore;
|
||||
case 0x7e: map_ff(FF_GAIN); break;
|
||||
case 0x83: input->ff_effects_max = field->value[0]; goto ignore;
|
||||
case 0x98: map_ff(FF_AUTOCENTER); break;
|
||||
case 0xa4: map_key_clear(BTN_DEAD); break;
|
||||
default: goto ignore;
|
||||
}
|
||||
@ -698,8 +680,7 @@ void hidinput_hid_event(struct hid_device *hid, struct hid_field *field, struct
|
||||
}
|
||||
|
||||
if (usage->hid == (HID_UP_PID | 0x83UL)) { /* Simultaneous Effects Max */
|
||||
input->ff_effects_max = value;
|
||||
dbg("Maximum Effects - %d",input->ff_effects_max);
|
||||
dbg("Maximum Effects - %d",value);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -748,7 +729,7 @@ static int hidinput_input_event(struct input_dev *dev, unsigned int type, unsign
|
||||
int offset;
|
||||
|
||||
if (type == EV_FF)
|
||||
return hid_ff_event(hid, dev, type, code, value);
|
||||
return input_ff_event(dev, type, code, value);
|
||||
|
||||
if (type != EV_LED)
|
||||
return -1;
|
||||
|
@ -1,12 +1,11 @@
|
||||
/*
|
||||
* $$
|
||||
*
|
||||
* Force feedback support for hid-compliant for some of the devices from
|
||||
* Logitech, namely:
|
||||
* - WingMan Cordless RumblePad
|
||||
* - WingMan Force 3D
|
||||
*
|
||||
* Copyright (c) 2002-2004 Johann Deneux
|
||||
* Copyright (c) 2006 Anssi Hannula <anssi.hannula@gmail.com>
|
||||
*/
|
||||
|
||||
/*
|
||||
@ -29,495 +28,117 @@
|
||||
*/
|
||||
|
||||
#include <linux/input.h>
|
||||
#include <linux/sched.h>
|
||||
|
||||
//#define DEBUG
|
||||
#include <linux/usb.h>
|
||||
|
||||
#include <linux/circ_buf.h>
|
||||
|
||||
#include "hid.h"
|
||||
#include "fixp-arith.h"
|
||||
|
||||
|
||||
/* Periodicity of the update */
|
||||
#define PERIOD (HZ/10)
|
||||
|
||||
#define RUN_AT(t) (jiffies + (t))
|
||||
|
||||
/* Effect status */
|
||||
#define EFFECT_STARTED 0 /* Effect is going to play after some time
|
||||
(ff_replay.delay) */
|
||||
#define EFFECT_PLAYING 1 /* Effect is being played */
|
||||
#define EFFECT_USED 2
|
||||
|
||||
// For lgff_device::flags
|
||||
#define DEVICE_CLOSING 0 /* The driver is being unitialised */
|
||||
|
||||
/* Check that the current process can access an effect */
|
||||
#define CHECK_OWNERSHIP(effect) (current->pid == 0 \
|
||||
|| effect.owner == current->pid)
|
||||
|
||||
#define LGFF_CHECK_OWNERSHIP(i, l) \
|
||||
(i>=0 && i<LGFF_EFFECTS \
|
||||
&& test_bit(EFFECT_USED, l->effects[i].flags) \
|
||||
&& CHECK_OWNERSHIP(l->effects[i]))
|
||||
|
||||
#define LGFF_EFFECTS 8
|
||||
|
||||
struct device_type {
|
||||
u16 idVendor;
|
||||
u16 idProduct;
|
||||
signed short *ff;
|
||||
const signed short *ff;
|
||||
};
|
||||
|
||||
struct lgff_effect {
|
||||
pid_t owner;
|
||||
|
||||
struct ff_effect effect;
|
||||
|
||||
unsigned long flags[1];
|
||||
unsigned int count; /* Number of times left to play */
|
||||
unsigned long started_at; /* When the effect started to play */
|
||||
};
|
||||
|
||||
struct lgff_device {
|
||||
struct hid_device* hid;
|
||||
|
||||
struct hid_report* constant;
|
||||
struct hid_report* rumble;
|
||||
struct hid_report* condition;
|
||||
|
||||
struct lgff_effect effects[LGFF_EFFECTS];
|
||||
spinlock_t lock; /* device-level lock. Having locks on
|
||||
a per-effect basis could be nice, but
|
||||
isn't really necessary */
|
||||
|
||||
unsigned long flags[1]; /* Contains various information about the
|
||||
state of the driver for this device */
|
||||
|
||||
struct timer_list timer;
|
||||
};
|
||||
|
||||
/* Callbacks */
|
||||
static void hid_lgff_exit(struct hid_device* hid);
|
||||
static int hid_lgff_event(struct hid_device *hid, struct input_dev *input,
|
||||
unsigned int type, unsigned int code, int value);
|
||||
static int hid_lgff_flush(struct input_dev *input, struct file *file);
|
||||
static int hid_lgff_upload_effect(struct input_dev *input,
|
||||
struct ff_effect *effect);
|
||||
static int hid_lgff_erase(struct input_dev *input, int id);
|
||||
|
||||
/* Local functions */
|
||||
static void hid_lgff_input_init(struct hid_device* hid);
|
||||
static void hid_lgff_timer(unsigned long timer_data);
|
||||
static struct hid_report* hid_lgff_duplicate_report(struct hid_report*);
|
||||
static void hid_lgff_delete_report(struct hid_report*);
|
||||
|
||||
static signed short ff_rumble[] = {
|
||||
static const signed short ff_rumble[] = {
|
||||
FF_RUMBLE,
|
||||
-1
|
||||
};
|
||||
|
||||
static signed short ff_joystick[] = {
|
||||
static const signed short ff_joystick[] = {
|
||||
FF_CONSTANT,
|
||||
-1
|
||||
};
|
||||
|
||||
static struct device_type devices[] = {
|
||||
{0x046d, 0xc211, ff_rumble},
|
||||
{0x046d, 0xc219, ff_rumble},
|
||||
{0x046d, 0xc283, ff_joystick},
|
||||
{0x0000, 0x0000, ff_joystick}
|
||||
static const struct device_type devices[] = {
|
||||
{ 0x046d, 0xc211, ff_rumble },
|
||||
{ 0x046d, 0xc219, ff_rumble },
|
||||
{ 0x046d, 0xc283, ff_joystick },
|
||||
{ 0x0000, 0x0000, ff_joystick }
|
||||
};
|
||||
|
||||
static int hid_lgff_play(struct input_dev *dev, void *data, struct ff_effect *effect)
|
||||
{
|
||||
struct hid_device *hid = dev->private;
|
||||
struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list;
|
||||
struct hid_report *report = list_entry(report_list->next, struct hid_report, list);
|
||||
int x, y;
|
||||
unsigned int left, right;
|
||||
|
||||
#define CLAMP(x) if (x < 0) x = 0; if (x > 0xff) x = 0xff
|
||||
|
||||
switch (effect->type) {
|
||||
case FF_CONSTANT:
|
||||
x = effect->u.ramp.start_level + 0x7f; /* 0x7f is center */
|
||||
y = effect->u.ramp.end_level + 0x7f;
|
||||
CLAMP(x);
|
||||
CLAMP(y);
|
||||
report->field[0]->value[0] = 0x51;
|
||||
report->field[0]->value[1] = 0x08;
|
||||
report->field[0]->value[2] = x;
|
||||
report->field[0]->value[3] = y;
|
||||
dbg("(x, y)=(%04x, %04x)", x, y);
|
||||
hid_submit_report(hid, report, USB_DIR_OUT);
|
||||
break;
|
||||
|
||||
case FF_RUMBLE:
|
||||
right = effect->u.rumble.strong_magnitude;
|
||||
left = effect->u.rumble.weak_magnitude;
|
||||
right = right * 0xff / 0xffff;
|
||||
left = left * 0xff / 0xffff;
|
||||
CLAMP(left);
|
||||
CLAMP(right);
|
||||
report->field[0]->value[0] = 0x42;
|
||||
report->field[0]->value[1] = 0x00;
|
||||
report->field[0]->value[2] = left;
|
||||
report->field[0]->value[3] = right;
|
||||
dbg("(left, right)=(%04x, %04x)", left, right);
|
||||
hid_submit_report(hid, report, USB_DIR_OUT);
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int hid_lgff_init(struct hid_device* hid)
|
||||
{
|
||||
struct lgff_device *private;
|
||||
struct hid_report* report;
|
||||
struct hid_field* field;
|
||||
struct hid_input *hidinput = list_entry(hid->inputs.next, struct hid_input, list);
|
||||
struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list;
|
||||
struct input_dev *dev = hidinput->input;
|
||||
struct hid_report *report;
|
||||
struct hid_field *field;
|
||||
int error;
|
||||
int i, j;
|
||||
|
||||
/* Find the report to use */
|
||||
if (list_empty(&hid->report_enum[HID_OUTPUT_REPORT].report_list)) {
|
||||
if (list_empty(report_list)) {
|
||||
err("No output report found");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Check that the report looks ok */
|
||||
report = (struct hid_report*)hid->report_enum[HID_OUTPUT_REPORT].report_list.next;
|
||||
report = list_entry(report_list->next, struct hid_report, list);
|
||||
if (!report) {
|
||||
err("NULL output report");
|
||||
return -1;
|
||||
}
|
||||
|
||||
field = report->field[0];
|
||||
if (!field) {
|
||||
err("NULL field");
|
||||
return -1;
|
||||
}
|
||||
|
||||
private = kzalloc(sizeof(struct lgff_device), GFP_KERNEL);
|
||||
if (!private)
|
||||
return -1;
|
||||
hid->ff_private = private;
|
||||
|
||||
/* Input init */
|
||||
hid_lgff_input_init(hid);
|
||||
|
||||
|
||||
private->constant = hid_lgff_duplicate_report(report);
|
||||
if (!private->constant) {
|
||||
kfree(private);
|
||||
return -1;
|
||||
}
|
||||
private->constant->field[0]->value[0] = 0x51;
|
||||
private->constant->field[0]->value[1] = 0x08;
|
||||
private->constant->field[0]->value[2] = 0x7f;
|
||||
private->constant->field[0]->value[3] = 0x7f;
|
||||
|
||||
private->rumble = hid_lgff_duplicate_report(report);
|
||||
if (!private->rumble) {
|
||||
hid_lgff_delete_report(private->constant);
|
||||
kfree(private);
|
||||
return -1;
|
||||
}
|
||||
private->rumble->field[0]->value[0] = 0x42;
|
||||
|
||||
|
||||
private->condition = hid_lgff_duplicate_report(report);
|
||||
if (!private->condition) {
|
||||
hid_lgff_delete_report(private->rumble);
|
||||
hid_lgff_delete_report(private->constant);
|
||||
kfree(private);
|
||||
return -1;
|
||||
for (i = 0; i < ARRAY_SIZE(devices); i++) {
|
||||
if (dev->id.vendor == devices[i].idVendor &&
|
||||
dev->id.product == devices[i].idProduct) {
|
||||
for (j = 0; devices[i].ff[j] >= 0; j++)
|
||||
set_bit(devices[i].ff[j], dev->ffbit);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private->hid = hid;
|
||||
|
||||
spin_lock_init(&private->lock);
|
||||
init_timer(&private->timer);
|
||||
private->timer.data = (unsigned long)private;
|
||||
private->timer.function = hid_lgff_timer;
|
||||
|
||||
/* Event and exit callbacks */
|
||||
hid->ff_exit = hid_lgff_exit;
|
||||
hid->ff_event = hid_lgff_event;
|
||||
|
||||
/* Start the update task */
|
||||
private->timer.expires = RUN_AT(PERIOD);
|
||||
add_timer(&private->timer); /*TODO: only run the timer when at least
|
||||
one effect is playing */
|
||||
error = input_ff_create_memless(dev, NULL, hid_lgff_play);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
printk(KERN_INFO "Force feedback for Logitech force feedback devices by Johann Deneux <johann.deneux@it.uu.se>\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct hid_report* hid_lgff_duplicate_report(struct hid_report* report)
|
||||
{
|
||||
struct hid_report* ret;
|
||||
|
||||
ret = kmalloc(sizeof(struct lgff_device), GFP_KERNEL);
|
||||
if (!ret)
|
||||
return NULL;
|
||||
*ret = *report;
|
||||
|
||||
ret->field[0] = kmalloc(sizeof(struct hid_field), GFP_KERNEL);
|
||||
if (!ret->field[0]) {
|
||||
kfree(ret);
|
||||
return NULL;
|
||||
}
|
||||
*ret->field[0] = *report->field[0];
|
||||
|
||||
ret->field[0]->value = kzalloc(sizeof(s32[8]), GFP_KERNEL);
|
||||
if (!ret->field[0]->value) {
|
||||
kfree(ret->field[0]);
|
||||
kfree(ret);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void hid_lgff_delete_report(struct hid_report* report)
|
||||
{
|
||||
if (report) {
|
||||
kfree(report->field[0]->value);
|
||||
kfree(report->field[0]);
|
||||
kfree(report);
|
||||
}
|
||||
}
|
||||
|
||||
static void hid_lgff_input_init(struct hid_device* hid)
|
||||
{
|
||||
struct device_type* dev = devices;
|
||||
signed short* ff;
|
||||
u16 idVendor = le16_to_cpu(hid->dev->descriptor.idVendor);
|
||||
u16 idProduct = le16_to_cpu(hid->dev->descriptor.idProduct);
|
||||
struct hid_input *hidinput = list_entry(hid->inputs.next, struct hid_input, list);
|
||||
struct input_dev *input_dev = hidinput->input;
|
||||
|
||||
while (dev->idVendor && (idVendor != dev->idVendor || idProduct != dev->idProduct))
|
||||
dev++;
|
||||
|
||||
for (ff = dev->ff; *ff >= 0; ff++)
|
||||
set_bit(*ff, input_dev->ffbit);
|
||||
|
||||
input_dev->upload_effect = hid_lgff_upload_effect;
|
||||
input_dev->flush = hid_lgff_flush;
|
||||
|
||||
set_bit(EV_FF, input_dev->evbit);
|
||||
input_dev->ff_effects_max = LGFF_EFFECTS;
|
||||
}
|
||||
|
||||
static void hid_lgff_exit(struct hid_device* hid)
|
||||
{
|
||||
struct lgff_device *lgff = hid->ff_private;
|
||||
|
||||
set_bit(DEVICE_CLOSING, lgff->flags);
|
||||
del_timer_sync(&lgff->timer);
|
||||
|
||||
hid_lgff_delete_report(lgff->condition);
|
||||
hid_lgff_delete_report(lgff->rumble);
|
||||
hid_lgff_delete_report(lgff->constant);
|
||||
|
||||
kfree(lgff);
|
||||
}
|
||||
|
||||
static int hid_lgff_event(struct hid_device *hid, struct input_dev* input,
|
||||
unsigned int type, unsigned int code, int value)
|
||||
{
|
||||
struct lgff_device *lgff = hid->ff_private;
|
||||
struct lgff_effect *effect = lgff->effects + code;
|
||||
unsigned long flags;
|
||||
|
||||
if (type != EV_FF) return -EINVAL;
|
||||
if (!LGFF_CHECK_OWNERSHIP(code, lgff)) return -EACCES;
|
||||
if (value < 0) return -EINVAL;
|
||||
|
||||
spin_lock_irqsave(&lgff->lock, flags);
|
||||
|
||||
if (value > 0) {
|
||||
if (test_bit(EFFECT_STARTED, effect->flags)) {
|
||||
spin_unlock_irqrestore(&lgff->lock, flags);
|
||||
return -EBUSY;
|
||||
}
|
||||
if (test_bit(EFFECT_PLAYING, effect->flags)) {
|
||||
spin_unlock_irqrestore(&lgff->lock, flags);
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
effect->count = value;
|
||||
|
||||
if (effect->effect.replay.delay) {
|
||||
set_bit(EFFECT_STARTED, effect->flags);
|
||||
} else {
|
||||
set_bit(EFFECT_PLAYING, effect->flags);
|
||||
}
|
||||
effect->started_at = jiffies;
|
||||
}
|
||||
else { /* value == 0 */
|
||||
clear_bit(EFFECT_STARTED, effect->flags);
|
||||
clear_bit(EFFECT_PLAYING, effect->flags);
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&lgff->lock, flags);
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
/* Erase all effects this process owns */
|
||||
static int hid_lgff_flush(struct input_dev *dev, struct file *file)
|
||||
{
|
||||
struct hid_device *hid = dev->private;
|
||||
struct lgff_device *lgff = hid->ff_private;
|
||||
int i;
|
||||
|
||||
for (i=0; i<dev->ff_effects_max; ++i) {
|
||||
|
||||
/*NOTE: no need to lock here. The only times EFFECT_USED is
|
||||
modified is when effects are uploaded or when an effect is
|
||||
erased. But a process cannot close its dev/input/eventX fd
|
||||
and perform ioctls on the same fd all at the same time */
|
||||
if ( current->pid == lgff->effects[i].owner
|
||||
&& test_bit(EFFECT_USED, lgff->effects[i].flags)) {
|
||||
|
||||
if (hid_lgff_erase(dev, i))
|
||||
warn("erase effect %d failed", i);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hid_lgff_erase(struct input_dev *dev, int id)
|
||||
{
|
||||
struct hid_device *hid = dev->private;
|
||||
struct lgff_device *lgff = hid->ff_private;
|
||||
unsigned long flags;
|
||||
|
||||
if (!LGFF_CHECK_OWNERSHIP(id, lgff)) return -EACCES;
|
||||
|
||||
spin_lock_irqsave(&lgff->lock, flags);
|
||||
lgff->effects[id].flags[0] = 0;
|
||||
spin_unlock_irqrestore(&lgff->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hid_lgff_upload_effect(struct input_dev* input,
|
||||
struct ff_effect* effect)
|
||||
{
|
||||
struct hid_device *hid = input->private;
|
||||
struct lgff_device *lgff = hid->ff_private;
|
||||
struct lgff_effect new;
|
||||
int id;
|
||||
unsigned long flags;
|
||||
|
||||
dbg("ioctl rumble");
|
||||
|
||||
if (!test_bit(effect->type, input->ffbit)) return -EINVAL;
|
||||
|
||||
spin_lock_irqsave(&lgff->lock, flags);
|
||||
|
||||
if (effect->id == -1) {
|
||||
int i;
|
||||
|
||||
for (i=0; i<LGFF_EFFECTS && test_bit(EFFECT_USED, lgff->effects[i].flags); ++i);
|
||||
if (i >= LGFF_EFFECTS) {
|
||||
spin_unlock_irqrestore(&lgff->lock, flags);
|
||||
return -ENOSPC;
|
||||
}
|
||||
|
||||
effect->id = i;
|
||||
lgff->effects[i].owner = current->pid;
|
||||
lgff->effects[i].flags[0] = 0;
|
||||
set_bit(EFFECT_USED, lgff->effects[i].flags);
|
||||
}
|
||||
else if (!LGFF_CHECK_OWNERSHIP(effect->id, lgff)) {
|
||||
spin_unlock_irqrestore(&lgff->lock, flags);
|
||||
return -EACCES;
|
||||
}
|
||||
|
||||
id = effect->id;
|
||||
new = lgff->effects[id];
|
||||
|
||||
new.effect = *effect;
|
||||
|
||||
if (test_bit(EFFECT_STARTED, lgff->effects[id].flags)
|
||||
|| test_bit(EFFECT_STARTED, lgff->effects[id].flags)) {
|
||||
|
||||
/* Changing replay parameters is not allowed (for the time
|
||||
being) */
|
||||
if (new.effect.replay.delay != lgff->effects[id].effect.replay.delay
|
||||
|| new.effect.replay.length != lgff->effects[id].effect.replay.length) {
|
||||
spin_unlock_irqrestore(&lgff->lock, flags);
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
lgff->effects[id] = new;
|
||||
|
||||
} else {
|
||||
lgff->effects[id] = new;
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&lgff->lock, flags);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void hid_lgff_timer(unsigned long timer_data)
|
||||
{
|
||||
struct lgff_device *lgff = (struct lgff_device*)timer_data;
|
||||
struct hid_device *hid = lgff->hid;
|
||||
unsigned long flags;
|
||||
int x = 0x7f, y = 0x7f; // Coordinates of constant effects
|
||||
unsigned int left = 0, right = 0; // Rumbling
|
||||
int i;
|
||||
|
||||
spin_lock_irqsave(&lgff->lock, flags);
|
||||
|
||||
for (i=0; i<LGFF_EFFECTS; ++i) {
|
||||
struct lgff_effect* effect = lgff->effects +i;
|
||||
|
||||
if (test_bit(EFFECT_PLAYING, effect->flags)) {
|
||||
|
||||
switch (effect->effect.type) {
|
||||
case FF_CONSTANT: {
|
||||
//TODO: handle envelopes
|
||||
int degrees = effect->effect.direction * 360 >> 16;
|
||||
x += fixp_mult(fixp_sin(degrees),
|
||||
fixp_new16(effect->effect.u.constant.level));
|
||||
y += fixp_mult(-fixp_cos(degrees),
|
||||
fixp_new16(effect->effect.u.constant.level));
|
||||
} break;
|
||||
case FF_RUMBLE:
|
||||
right += effect->effect.u.rumble.strong_magnitude;
|
||||
left += effect->effect.u.rumble.weak_magnitude;
|
||||
break;
|
||||
};
|
||||
|
||||
/* One run of the effect is finished playing */
|
||||
if (time_after(jiffies,
|
||||
effect->started_at
|
||||
+ effect->effect.replay.delay*HZ/1000
|
||||
+ effect->effect.replay.length*HZ/1000)) {
|
||||
dbg("Finished playing once %d", i);
|
||||
if (--effect->count <= 0) {
|
||||
dbg("Stopped %d", i);
|
||||
clear_bit(EFFECT_PLAYING, effect->flags);
|
||||
}
|
||||
else {
|
||||
dbg("Start again %d", i);
|
||||
if (effect->effect.replay.length != 0) {
|
||||
clear_bit(EFFECT_PLAYING, effect->flags);
|
||||
set_bit(EFFECT_STARTED, effect->flags);
|
||||
}
|
||||
effect->started_at = jiffies;
|
||||
}
|
||||
}
|
||||
|
||||
} else if (test_bit(EFFECT_STARTED, lgff->effects[i].flags)) {
|
||||
/* Check if we should start playing the effect */
|
||||
if (time_after(jiffies,
|
||||
lgff->effects[i].started_at
|
||||
+ lgff->effects[i].effect.replay.delay*HZ/1000)) {
|
||||
dbg("Now playing %d", i);
|
||||
clear_bit(EFFECT_STARTED, lgff->effects[i].flags);
|
||||
set_bit(EFFECT_PLAYING, lgff->effects[i].flags);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#define CLAMP(x) if (x < 0) x = 0; if (x > 0xff) x = 0xff
|
||||
|
||||
// Clamp values
|
||||
CLAMP(x);
|
||||
CLAMP(y);
|
||||
CLAMP(left);
|
||||
CLAMP(right);
|
||||
|
||||
#undef CLAMP
|
||||
|
||||
if (x != lgff->constant->field[0]->value[2]
|
||||
|| y != lgff->constant->field[0]->value[3]) {
|
||||
lgff->constant->field[0]->value[2] = x;
|
||||
lgff->constant->field[0]->value[3] = y;
|
||||
dbg("(x,y)=(%04x, %04x)", x, y);
|
||||
hid_submit_report(hid, lgff->constant, USB_DIR_OUT);
|
||||
}
|
||||
|
||||
if (left != lgff->rumble->field[0]->value[2]
|
||||
|| right != lgff->rumble->field[0]->value[3]) {
|
||||
lgff->rumble->field[0]->value[2] = left;
|
||||
lgff->rumble->field[0]->value[3] = right;
|
||||
dbg("(left,right)=(%04x, %04x)", left, right);
|
||||
hid_submit_report(hid, lgff->rumble, USB_DIR_OUT);
|
||||
}
|
||||
|
||||
if (!test_bit(DEVICE_CLOSING, lgff->flags)) {
|
||||
lgff->timer.expires = RUN_AT(PERIOD);
|
||||
add_timer(&lgff->timer);
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&lgff->lock, flags);
|
||||
}
|
||||
|
1330
drivers/usb/input/hid-pidff.c
Normal file
1330
drivers/usb/input/hid-pidff.c
Normal file
File diff suppressed because it is too large
Load Diff
@ -28,97 +28,65 @@
|
||||
*/
|
||||
|
||||
#include <linux/input.h>
|
||||
#include <linux/sched.h>
|
||||
|
||||
#undef DEBUG
|
||||
#include <linux/usb.h>
|
||||
|
||||
#include <linux/circ_buf.h>
|
||||
|
||||
#include "hid.h"
|
||||
#include "fixp-arith.h"
|
||||
|
||||
/* Usages for thrustmaster devices I know about */
|
||||
#define THRUSTMASTER_USAGE_RUMBLE_LR (HID_UP_GENDESK | 0xbb)
|
||||
#define DELAY_CALC(t,delay) ((t) + (delay)*HZ/1000)
|
||||
|
||||
/* Effect status */
|
||||
#define EFFECT_STARTED 0 /* Effect is going to play after some time */
|
||||
#define EFFECT_PLAYING 1 /* Effect is playing */
|
||||
#define EFFECT_USED 2
|
||||
|
||||
/* For tmff_device::flags */
|
||||
#define DEVICE_CLOSING 0 /* The driver is being unitialised */
|
||||
|
||||
/* Check that the current process can access an effect */
|
||||
#define CHECK_OWNERSHIP(effect) (current->pid == 0 \
|
||||
|| effect.owner == current->pid)
|
||||
|
||||
#define TMFF_CHECK_ID(id) ((id) >= 0 && (id) < TMFF_EFFECTS)
|
||||
|
||||
#define TMFF_CHECK_OWNERSHIP(i, l) \
|
||||
(test_bit(EFFECT_USED, l->effects[i].flags) \
|
||||
&& CHECK_OWNERSHIP(l->effects[i]))
|
||||
|
||||
#define TMFF_EFFECTS 8
|
||||
|
||||
struct tmff_effect {
|
||||
pid_t owner;
|
||||
|
||||
struct ff_effect effect;
|
||||
|
||||
unsigned long flags[1];
|
||||
unsigned int count; /* Number of times left to play */
|
||||
|
||||
unsigned long play_at; /* When the effect starts to play */
|
||||
unsigned long stop_at; /* When the effect ends */
|
||||
};
|
||||
|
||||
struct tmff_device {
|
||||
struct hid_device *hid;
|
||||
|
||||
struct hid_report *report;
|
||||
|
||||
struct hid_field *rumble;
|
||||
|
||||
unsigned int effects_playing;
|
||||
struct tmff_effect effects[TMFF_EFFECTS];
|
||||
spinlock_t lock; /* device-level lock. Having locks on
|
||||
a per-effect basis could be nice, but
|
||||
isn't really necessary */
|
||||
|
||||
unsigned long flags[1]; /* Contains various information about the
|
||||
state of the driver for this device */
|
||||
|
||||
struct timer_list timer;
|
||||
};
|
||||
|
||||
/* Callbacks */
|
||||
static void hid_tmff_exit(struct hid_device *hid);
|
||||
static int hid_tmff_event(struct hid_device *hid, struct input_dev *input,
|
||||
unsigned int type, unsigned int code, int value);
|
||||
static int hid_tmff_flush(struct input_dev *input, struct file *file);
|
||||
static int hid_tmff_upload_effect(struct input_dev *input,
|
||||
struct ff_effect *effect);
|
||||
static int hid_tmff_erase(struct input_dev *input, int id);
|
||||
/* Changes values from 0 to 0xffff into values from minimum to maximum */
|
||||
static inline int hid_tmff_scale(unsigned int in, int minimum, int maximum)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* Local functions */
|
||||
static void hid_tmff_recalculate_timer(struct tmff_device *tmff);
|
||||
static void hid_tmff_timer(unsigned long timer_data);
|
||||
ret = (in * (maximum - minimum) / 0xffff) + minimum;
|
||||
if (ret < minimum)
|
||||
return minimum;
|
||||
if (ret > maximum)
|
||||
return maximum;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int hid_tmff_play(struct input_dev *dev, void *data, struct ff_effect *effect)
|
||||
{
|
||||
struct hid_device *hid = dev->private;
|
||||
struct tmff_device *tmff = data;
|
||||
int left, right; /* Rumbling */
|
||||
|
||||
left = hid_tmff_scale(effect->u.rumble.weak_magnitude,
|
||||
tmff->rumble->logical_minimum, tmff->rumble->logical_maximum);
|
||||
right = hid_tmff_scale(effect->u.rumble.strong_magnitude,
|
||||
tmff->rumble->logical_minimum, tmff->rumble->logical_maximum);
|
||||
|
||||
tmff->rumble->value[0] = left;
|
||||
tmff->rumble->value[1] = right;
|
||||
dbg("(left,right)=(%08x, %08x)", left, right);
|
||||
hid_submit_report(hid, tmff->report, USB_DIR_OUT);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int hid_tmff_init(struct hid_device *hid)
|
||||
{
|
||||
struct tmff_device *private;
|
||||
struct tmff_device *tmff;
|
||||
struct list_head *pos;
|
||||
struct hid_input *hidinput = list_entry(hid->inputs.next, struct hid_input, list);
|
||||
struct input_dev *input_dev = hidinput->input;
|
||||
int error;
|
||||
|
||||
private = kzalloc(sizeof(struct tmff_device), GFP_KERNEL);
|
||||
if (!private)
|
||||
tmff = kzalloc(sizeof(struct tmff_device), GFP_KERNEL);
|
||||
if (!tmff)
|
||||
return -ENOMEM;
|
||||
|
||||
hid->ff_private = private;
|
||||
|
||||
/* Find the report to use */
|
||||
__list_for_each(pos, &hid->report_enum[HID_OUTPUT_REPORT].report_list) {
|
||||
struct hid_report *report = (struct hid_report *)pos;
|
||||
@ -142,18 +110,18 @@ int hid_tmff_init(struct hid_device *hid)
|
||||
continue;
|
||||
}
|
||||
|
||||
if (private->report && private->report != report) {
|
||||
if (tmff->report && tmff->report != report) {
|
||||
warn("ignoring THRUSTMASTER_USAGE_RUMBLE_LR in other report");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (private->rumble && private->rumble != field) {
|
||||
if (tmff->rumble && tmff->rumble != field) {
|
||||
warn("ignoring duplicate THRUSTMASTER_USAGE_RUMBLE_LR");
|
||||
continue;
|
||||
}
|
||||
|
||||
private->report = report;
|
||||
private->rumble = field;
|
||||
tmff->report = report;
|
||||
tmff->rumble = field;
|
||||
|
||||
set_bit(FF_RUMBLE, input_dev->ffbit);
|
||||
break;
|
||||
@ -162,302 +130,17 @@ int hid_tmff_init(struct hid_device *hid)
|
||||
warn("ignoring unknown output usage %08x", field->usage[0].hid);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Fallthrough to here only when a valid usage is found */
|
||||
input_dev->upload_effect = hid_tmff_upload_effect;
|
||||
input_dev->flush = hid_tmff_flush;
|
||||
|
||||
set_bit(EV_FF, input_dev->evbit);
|
||||
input_dev->ff_effects_max = TMFF_EFFECTS;
|
||||
}
|
||||
}
|
||||
|
||||
private->hid = hid;
|
||||
|
||||
spin_lock_init(&private->lock);
|
||||
init_timer(&private->timer);
|
||||
private->timer.data = (unsigned long)private;
|
||||
private->timer.function = hid_tmff_timer;
|
||||
|
||||
/* Event and exit callbacks */
|
||||
hid->ff_exit = hid_tmff_exit;
|
||||
hid->ff_event = hid_tmff_event;
|
||||
error = input_ff_create_memless(input_dev, tmff, hid_tmff_play);
|
||||
if (error) {
|
||||
kfree(tmff);
|
||||
return error;
|
||||
}
|
||||
|
||||
info("Force feedback for ThrustMaster rumble pad devices by Zinx Verituse <zinx@epicsol.org>");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void hid_tmff_exit(struct hid_device *hid)
|
||||
{
|
||||
struct tmff_device *tmff = hid->ff_private;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&tmff->lock, flags);
|
||||
|
||||
set_bit(DEVICE_CLOSING, tmff->flags);
|
||||
del_timer_sync(&tmff->timer);
|
||||
|
||||
spin_unlock_irqrestore(&tmff->lock, flags);
|
||||
|
||||
kfree(tmff);
|
||||
}
|
||||
|
||||
static int hid_tmff_event(struct hid_device *hid, struct input_dev *input,
|
||||
unsigned int type, unsigned int code, int value)
|
||||
{
|
||||
struct tmff_device *tmff = hid->ff_private;
|
||||
struct tmff_effect *effect = &tmff->effects[code];
|
||||
unsigned long flags;
|
||||
|
||||
if (type != EV_FF)
|
||||
return -EINVAL;
|
||||
if (!TMFF_CHECK_ID(code))
|
||||
return -EINVAL;
|
||||
if (!TMFF_CHECK_OWNERSHIP(code, tmff))
|
||||
return -EACCES;
|
||||
if (value < 0)
|
||||
return -EINVAL;
|
||||
|
||||
spin_lock_irqsave(&tmff->lock, flags);
|
||||
|
||||
if (value > 0) {
|
||||
set_bit(EFFECT_STARTED, effect->flags);
|
||||
clear_bit(EFFECT_PLAYING, effect->flags);
|
||||
effect->count = value;
|
||||
effect->play_at = DELAY_CALC(jiffies, effect->effect.replay.delay);
|
||||
} else {
|
||||
clear_bit(EFFECT_STARTED, effect->flags);
|
||||
clear_bit(EFFECT_PLAYING, effect->flags);
|
||||
}
|
||||
|
||||
hid_tmff_recalculate_timer(tmff);
|
||||
|
||||
spin_unlock_irqrestore(&tmff->lock, flags);
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
/* Erase all effects this process owns */
|
||||
|
||||
static int hid_tmff_flush(struct input_dev *dev, struct file *file)
|
||||
{
|
||||
struct hid_device *hid = dev->private;
|
||||
struct tmff_device *tmff = hid->ff_private;
|
||||
int i;
|
||||
|
||||
for (i=0; i<dev->ff_effects_max; ++i)
|
||||
|
||||
/* NOTE: no need to lock here. The only times EFFECT_USED is
|
||||
modified is when effects are uploaded or when an effect is
|
||||
erased. But a process cannot close its dev/input/eventX fd
|
||||
and perform ioctls on the same fd all at the same time */
|
||||
|
||||
if (current->pid == tmff->effects[i].owner
|
||||
&& test_bit(EFFECT_USED, tmff->effects[i].flags))
|
||||
if (hid_tmff_erase(dev, i))
|
||||
warn("erase effect %d failed", i);
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hid_tmff_erase(struct input_dev *dev, int id)
|
||||
{
|
||||
struct hid_device *hid = dev->private;
|
||||
struct tmff_device *tmff = hid->ff_private;
|
||||
unsigned long flags;
|
||||
|
||||
if (!TMFF_CHECK_ID(id))
|
||||
return -EINVAL;
|
||||
if (!TMFF_CHECK_OWNERSHIP(id, tmff))
|
||||
return -EACCES;
|
||||
|
||||
spin_lock_irqsave(&tmff->lock, flags);
|
||||
|
||||
tmff->effects[id].flags[0] = 0;
|
||||
hid_tmff_recalculate_timer(tmff);
|
||||
|
||||
spin_unlock_irqrestore(&tmff->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hid_tmff_upload_effect(struct input_dev *input,
|
||||
struct ff_effect *effect)
|
||||
{
|
||||
struct hid_device *hid = input->private;
|
||||
struct tmff_device *tmff = hid->ff_private;
|
||||
int id;
|
||||
unsigned long flags;
|
||||
|
||||
if (!test_bit(effect->type, input->ffbit))
|
||||
return -EINVAL;
|
||||
if (effect->id != -1 && !TMFF_CHECK_ID(effect->id))
|
||||
return -EINVAL;
|
||||
|
||||
spin_lock_irqsave(&tmff->lock, flags);
|
||||
|
||||
if (effect->id == -1) {
|
||||
/* Find a free effect */
|
||||
for (id = 0; id < TMFF_EFFECTS && test_bit(EFFECT_USED, tmff->effects[id].flags); ++id);
|
||||
|
||||
if (id >= TMFF_EFFECTS) {
|
||||
spin_unlock_irqrestore(&tmff->lock, flags);
|
||||
return -ENOSPC;
|
||||
}
|
||||
|
||||
effect->id = id;
|
||||
tmff->effects[id].owner = current->pid;
|
||||
tmff->effects[id].flags[0] = 0;
|
||||
set_bit(EFFECT_USED, tmff->effects[id].flags);
|
||||
|
||||
} else {
|
||||
/* Re-uploading an owned effect, to change parameters */
|
||||
id = effect->id;
|
||||
clear_bit(EFFECT_PLAYING, tmff->effects[id].flags);
|
||||
}
|
||||
|
||||
tmff->effects[id].effect = *effect;
|
||||
|
||||
hid_tmff_recalculate_timer(tmff);
|
||||
|
||||
spin_unlock_irqrestore(&tmff->lock, flags);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Start the timer for the next start/stop/delay */
|
||||
/* Always call this while tmff->lock is locked */
|
||||
|
||||
static void hid_tmff_recalculate_timer(struct tmff_device *tmff)
|
||||
{
|
||||
int i;
|
||||
int events = 0;
|
||||
unsigned long next_time;
|
||||
|
||||
next_time = 0; /* Shut up compiler's incorrect warning */
|
||||
|
||||
/* Find the next change in an effect's status */
|
||||
for (i = 0; i < TMFF_EFFECTS; ++i) {
|
||||
struct tmff_effect *effect = &tmff->effects[i];
|
||||
unsigned long play_time;
|
||||
|
||||
if (!test_bit(EFFECT_STARTED, effect->flags))
|
||||
continue;
|
||||
|
||||
effect->stop_at = DELAY_CALC(effect->play_at, effect->effect.replay.length);
|
||||
|
||||
if (!test_bit(EFFECT_PLAYING, effect->flags))
|
||||
play_time = effect->play_at;
|
||||
else
|
||||
play_time = effect->stop_at;
|
||||
|
||||
events++;
|
||||
|
||||
if (time_after(jiffies, play_time))
|
||||
play_time = jiffies;
|
||||
|
||||
if (events == 1)
|
||||
next_time = play_time;
|
||||
else {
|
||||
if (time_after(next_time, play_time))
|
||||
next_time = play_time;
|
||||
}
|
||||
}
|
||||
|
||||
if (!events && tmff->effects_playing) {
|
||||
/* Treat all effects turning off as an event */
|
||||
events = 1;
|
||||
next_time = jiffies;
|
||||
}
|
||||
|
||||
if (!events) {
|
||||
/* No events, no time, no need for a timer. */
|
||||
del_timer_sync(&tmff->timer);
|
||||
return;
|
||||
}
|
||||
|
||||
mod_timer(&tmff->timer, next_time);
|
||||
}
|
||||
|
||||
/* Changes values from 0 to 0xffff into values from minimum to maximum */
|
||||
static inline int hid_tmff_scale(unsigned int in, int minimum, int maximum)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = (in * (maximum - minimum) / 0xffff) + minimum;
|
||||
if (ret < minimum)
|
||||
return minimum;
|
||||
if (ret > maximum)
|
||||
return maximum;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void hid_tmff_timer(unsigned long timer_data)
|
||||
{
|
||||
struct tmff_device *tmff = (struct tmff_device *) timer_data;
|
||||
struct hid_device *hid = tmff->hid;
|
||||
unsigned long flags;
|
||||
int left = 0, right = 0; /* Rumbling */
|
||||
int i;
|
||||
|
||||
spin_lock_irqsave(&tmff->lock, flags);
|
||||
|
||||
tmff->effects_playing = 0;
|
||||
|
||||
for (i = 0; i < TMFF_EFFECTS; ++i) {
|
||||
struct tmff_effect *effect = &tmff->effects[i];
|
||||
|
||||
if (!test_bit(EFFECT_STARTED, effect->flags))
|
||||
continue;
|
||||
|
||||
if (!time_after(jiffies, effect->play_at))
|
||||
continue;
|
||||
|
||||
if (time_after(jiffies, effect->stop_at)) {
|
||||
|
||||
dbg("Finished playing once %d", i);
|
||||
clear_bit(EFFECT_PLAYING, effect->flags);
|
||||
|
||||
if (--effect->count <= 0) {
|
||||
dbg("Stopped %d", i);
|
||||
clear_bit(EFFECT_STARTED, effect->flags);
|
||||
continue;
|
||||
} else {
|
||||
dbg("Start again %d", i);
|
||||
effect->play_at = DELAY_CALC(jiffies, effect->effect.replay.delay);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
++tmff->effects_playing;
|
||||
|
||||
set_bit(EFFECT_PLAYING, effect->flags);
|
||||
|
||||
switch (effect->effect.type) {
|
||||
case FF_RUMBLE:
|
||||
right += effect->effect.u.rumble.strong_magnitude;
|
||||
left += effect->effect.u.rumble.weak_magnitude;
|
||||
break;
|
||||
default:
|
||||
BUG();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
left = hid_tmff_scale(left, tmff->rumble->logical_minimum, tmff->rumble->logical_maximum);
|
||||
right = hid_tmff_scale(right, tmff->rumble->logical_minimum, tmff->rumble->logical_maximum);
|
||||
|
||||
if (left != tmff->rumble->value[0] || right != tmff->rumble->value[1]) {
|
||||
tmff->rumble->value[0] = left;
|
||||
tmff->rumble->value[1] = right;
|
||||
dbg("(left,right)=(%08x, %08x)", left, right);
|
||||
hid_submit_report(hid, tmff->report, USB_DIR_OUT);
|
||||
}
|
||||
|
||||
if (!test_bit(DEVICE_CLOSING, tmff->flags))
|
||||
hid_tmff_recalculate_timer(tmff);
|
||||
|
||||
spin_unlock_irqrestore(&tmff->lock, flags);
|
||||
}
|
||||
|
110
drivers/usb/input/hid-zpff.c
Normal file
110
drivers/usb/input/hid-zpff.c
Normal file
@ -0,0 +1,110 @@
|
||||
/*
|
||||
* Force feedback support for Zeroplus based devices
|
||||
*
|
||||
* Copyright (c) 2005, 2006 Anssi Hannula <anssi.hannula@gmail.com>
|
||||
*/
|
||||
|
||||
/*
|
||||
* 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("hid-zpff: " format "\n" , ## arg)
|
||||
|
||||
#include <linux/input.h>
|
||||
#include <linux/usb.h>
|
||||
#include "hid.h"
|
||||
|
||||
struct zpff_device {
|
||||
struct hid_report *report;
|
||||
};
|
||||
|
||||
static int hid_zpff_play(struct input_dev *dev, void *data,
|
||||
struct ff_effect *effect)
|
||||
{
|
||||
struct hid_device *hid = dev->private;
|
||||
struct zpff_device *zpff = data;
|
||||
int left, right;
|
||||
|
||||
/*
|
||||
* The following is specified the other way around in the Zeroplus
|
||||
* datasheet but the order below is correct for the XFX Executioner;
|
||||
* however it is possible that the XFX Executioner is an exception
|
||||
*/
|
||||
|
||||
left = effect->u.rumble.strong_magnitude;
|
||||
right = effect->u.rumble.weak_magnitude;
|
||||
debug("called with 0x%04x 0x%04x", left, right);
|
||||
|
||||
left = left * 0x7f / 0xffff;
|
||||
right = right * 0x7f / 0xffff;
|
||||
|
||||
zpff->report->field[2]->value[0] = left;
|
||||
zpff->report->field[3]->value[0] = right;
|
||||
debug("running with 0x%02x 0x%02x", left, right);
|
||||
hid_submit_report(hid, zpff->report, USB_DIR_OUT);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int hid_zpff_init(struct hid_device *hid)
|
||||
{
|
||||
struct zpff_device *zpff;
|
||||
struct hid_report *report;
|
||||
struct hid_input *hidinput = list_entry(hid->inputs.next,
|
||||
struct hid_input, list);
|
||||
struct list_head *report_list =
|
||||
&hid->report_enum[HID_OUTPUT_REPORT].report_list;
|
||||
struct input_dev *dev = hidinput->input;
|
||||
int error;
|
||||
|
||||
if (list_empty(report_list)) {
|
||||
printk(KERN_ERR "hid-zpff: no output report found\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
report = list_entry(report_list->next, struct hid_report, list);
|
||||
|
||||
if (report->maxfield < 4) {
|
||||
printk(KERN_ERR "hid-zpff: not enough fields in report\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
zpff = kzalloc(sizeof(struct zpff_device), GFP_KERNEL);
|
||||
if (!zpff)
|
||||
return -ENOMEM;
|
||||
|
||||
set_bit(FF_RUMBLE, dev->ffbit);
|
||||
|
||||
error = input_ff_create_memless(dev, zpff, hid_zpff_play);
|
||||
if (error) {
|
||||
kfree(zpff);
|
||||
return error;
|
||||
}
|
||||
|
||||
zpff->report = report;
|
||||
zpff->report->field[0]->value[0] = 0x00;
|
||||
zpff->report->field[1]->value[0] = 0x02;
|
||||
zpff->report->field[2]->value[0] = 0x00;
|
||||
zpff->report->field[3]->value[0] = 0x00;
|
||||
hid_submit_report(hid, zpff->report, USB_DIR_OUT);
|
||||
|
||||
printk(KERN_INFO "Force feedback for Zeroplus based devices by "
|
||||
"Anssi Hannula <anssi.hannula@gmail.com>\n");
|
||||
|
||||
return 0;
|
||||
}
|
@ -449,11 +449,6 @@ struct hid_device { /* device report descriptor */
|
||||
char phys[64]; /* Device physical location */
|
||||
char uniq[64]; /* Device unique identifier (serial #) */
|
||||
|
||||
void *ff_private; /* Private data for the force-feedback driver */
|
||||
void (*ff_exit)(struct hid_device*); /* Called by hid_exit_ff(hid) */
|
||||
int (*ff_event)(struct hid_device *hid, struct input_dev *input,
|
||||
unsigned int type, unsigned int code, int value);
|
||||
|
||||
#ifdef CONFIG_USB_HIDINPUT_POWERBOOK
|
||||
unsigned long pb_pressed_fn[NBITS(KEY_MAX)];
|
||||
unsigned long pb_pressed_numlock[NBITS(KEY_MAX)];
|
||||
@ -521,29 +516,22 @@ void hid_close(struct hid_device *);
|
||||
int hid_set_field(struct hid_field *, unsigned, __s32);
|
||||
void hid_submit_report(struct hid_device *, struct hid_report *, unsigned char dir);
|
||||
void hid_init_reports(struct hid_device *hid);
|
||||
struct hid_field *hid_find_field_by_usage(struct hid_device *hid, __u32 wanted_usage, int type);
|
||||
int hid_wait_io(struct hid_device* hid);
|
||||
|
||||
|
||||
#ifdef CONFIG_HID_FF
|
||||
int hid_ff_init(struct hid_device *hid);
|
||||
|
||||
int hid_lgff_init(struct hid_device *hid);
|
||||
int hid_tmff_init(struct hid_device *hid);
|
||||
int hid_zpff_init(struct hid_device *hid);
|
||||
#ifdef CONFIG_HID_PID
|
||||
int hid_pidff_init(struct hid_device *hid);
|
||||
#else
|
||||
static inline int hid_pidff_init(struct hid_device *hid) { return -ENODEV; }
|
||||
#endif
|
||||
|
||||
#else
|
||||
static inline int hid_ff_init(struct hid_device *hid) { return -1; }
|
||||
#endif
|
||||
static inline void hid_ff_exit(struct hid_device *hid)
|
||||
{
|
||||
if (hid->ff_exit)
|
||||
hid->ff_exit(hid);
|
||||
}
|
||||
static inline int hid_ff_event(struct hid_device *hid, struct input_dev *input,
|
||||
unsigned int type, unsigned int code, int value)
|
||||
{
|
||||
if (hid->ff_event)
|
||||
return hid->ff_event(hid, input, type, code, value);
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
int hid_lgff_init(struct hid_device* hid);
|
||||
int hid_tmff_init(struct hid_device* hid);
|
||||
int hid_pid_init(struct hid_device* hid);
|
||||
|
||||
|
@ -1,295 +0,0 @@
|
||||
/*
|
||||
* PID Force feedback support for hid devices.
|
||||
*
|
||||
* Copyright (c) 2002 Rodrigo Damazio.
|
||||
* Portions by Johann Deneux and Bjorn Augustson
|
||||
*/
|
||||
|
||||
/*
|
||||
* 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 <rdamazio@lsi.usp.br>
|
||||
*/
|
||||
|
||||
#include <linux/config.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/smp_lock.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/usb.h>
|
||||
#include "hid.h"
|
||||
#include "pid.h"
|
||||
|
||||
#define CHECK_OWNERSHIP(i, hid_pid) \
|
||||
((i) < FF_EFFECTS_MAX && i >= 0 && \
|
||||
test_bit(FF_PID_FLAGS_USED, &hid_pid->effects[(i)].flags) && \
|
||||
(current->pid == 0 || \
|
||||
(hid_pid)->effects[(i)].owner == current->pid))
|
||||
|
||||
/* Called when a transfer is completed */
|
||||
static void hid_pid_ctrl_out(struct urb *u, struct pt_regs *regs)
|
||||
{
|
||||
dev_dbg(&u->dev->dev, "hid_pid_ctrl_out - Transfer Completed\n");
|
||||
}
|
||||
|
||||
static void hid_pid_exit(struct hid_device *hid)
|
||||
{
|
||||
struct hid_ff_pid *private = hid->ff_private;
|
||||
|
||||
if (private->urbffout) {
|
||||
usb_kill_urb(private->urbffout);
|
||||
usb_free_urb(private->urbffout);
|
||||
}
|
||||
}
|
||||
|
||||
static int pid_upload_periodic(struct hid_ff_pid *pid, struct ff_effect *effect, int is_update)
|
||||
{
|
||||
dev_info(&pid->hid->dev->dev, "requested periodic force upload\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pid_upload_constant(struct hid_ff_pid *pid, struct ff_effect *effect, int is_update)
|
||||
{
|
||||
dev_info(&pid->hid->dev->dev, "requested constant force upload\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pid_upload_condition(struct hid_ff_pid *pid, struct ff_effect *effect, int is_update)
|
||||
{
|
||||
dev_info(&pid->hid->dev->dev, "requested Condition force upload\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pid_upload_ramp(struct hid_ff_pid *pid, struct ff_effect *effect, int is_update)
|
||||
{
|
||||
dev_info(&pid->hid->dev->dev, "request ramp force upload\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hid_pid_event(struct hid_device *hid, struct input_dev *input,
|
||||
unsigned int type, unsigned int code, int value)
|
||||
{
|
||||
dev_dbg(&hid->dev->dev, "PID event received: type=%d,code=%d,value=%d.\n", type, code, value);
|
||||
|
||||
if (type != EV_FF)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Lock must be held by caller */
|
||||
static void hid_pid_ctrl_playback(struct hid_device *hid, struct hid_pid_effect *effect, int play)
|
||||
{
|
||||
if (play)
|
||||
set_bit(FF_PID_FLAGS_PLAYING, &effect->flags);
|
||||
else
|
||||
clear_bit(FF_PID_FLAGS_PLAYING, &effect->flags);
|
||||
}
|
||||
|
||||
static int hid_pid_erase(struct input_dev *dev, int id)
|
||||
{
|
||||
struct hid_device *hid = dev->private;
|
||||
struct hid_ff_pid *pid = hid->ff_private;
|
||||
struct hid_field *field;
|
||||
unsigned long flags;
|
||||
int ret;
|
||||
|
||||
if (!CHECK_OWNERSHIP(id, pid))
|
||||
return -EACCES;
|
||||
|
||||
/* Find report */
|
||||
field = hid_find_field_by_usage(hid, HID_UP_PID | FF_PID_USAGE_BLOCK_FREE,
|
||||
HID_OUTPUT_REPORT);
|
||||
if (!field) {
|
||||
dev_err(&hid->dev->dev, "couldn't find report\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
ret = hid_set_field(field, 0, pid->effects[id].device_id);
|
||||
if (ret) {
|
||||
dev_err(&hid->dev->dev, "couldn't set field\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
hid_submit_report(hid, field->report, USB_DIR_OUT);
|
||||
|
||||
spin_lock_irqsave(&pid->lock, flags);
|
||||
hid_pid_ctrl_playback(hid, pid->effects + id, 0);
|
||||
pid->effects[id].flags = 0;
|
||||
spin_unlock_irqrestore(&pid->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Erase all effects this process owns */
|
||||
static int hid_pid_flush(struct input_dev *dev, struct file *file)
|
||||
{
|
||||
struct hid_device *hid = dev->private;
|
||||
struct hid_ff_pid *pid = hid->ff_private;
|
||||
int i;
|
||||
|
||||
/*NOTE: no need to lock here. The only times EFFECT_USED is
|
||||
modified is when effects are uploaded or when an effect is
|
||||
erased. But a process cannot close its dev/input/eventX fd
|
||||
and perform ioctls on the same fd all at the same time */
|
||||
/*FIXME: multiple threads, anyone? */
|
||||
for (i = 0; i < dev->ff_effects_max; ++i)
|
||||
if (current->pid == pid->effects[i].owner
|
||||
&& test_bit(FF_PID_FLAGS_USED, &pid->effects[i].flags))
|
||||
if (hid_pid_erase(dev, i))
|
||||
dev_warn(&hid->dev->dev, "erase effect %d failed", i);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hid_pid_upload_effect(struct input_dev *dev,
|
||||
struct ff_effect *effect)
|
||||
{
|
||||
struct hid_ff_pid *pid_private = (struct hid_ff_pid *)(dev->private);
|
||||
int ret;
|
||||
int is_update;
|
||||
unsigned long flags;
|
||||
|
||||
dev_dbg(&pid_private->hid->dev->dev, "upload effect called: effect_type=%x\n", effect->type);
|
||||
/* Check this effect type is supported by this device */
|
||||
if (!test_bit(effect->type, dev->ffbit)) {
|
||||
dev_dbg(&pid_private->hid->dev->dev,
|
||||
"invalid kind of effect requested.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/*
|
||||
* If we want to create a new effect, get a free id
|
||||
*/
|
||||
if (effect->id == -1) {
|
||||
int id = 0;
|
||||
|
||||
// Spinlock so we don`t get a race condition when choosing IDs
|
||||
spin_lock_irqsave(&pid_private->lock, flags);
|
||||
|
||||
while (id < FF_EFFECTS_MAX)
|
||||
if (!test_and_set_bit(FF_PID_FLAGS_USED, &pid_private->effects[id++].flags))
|
||||
break;
|
||||
|
||||
if (id == FF_EFFECTS_MAX) {
|
||||
spin_unlock_irqrestore(&pid_private->lock, flags);
|
||||
// TEMP - We need to get ff_effects_max correctly first: || id >= dev->ff_effects_max) {
|
||||
dev_dbg(&pid_private->hid->dev->dev, "Not enough device memory\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
effect->id = id;
|
||||
dev_dbg(&pid_private->hid->dev->dev, "effect ID is %d.\n", id);
|
||||
pid_private->effects[id].owner = current->pid;
|
||||
pid_private->effects[id].flags = (1 << FF_PID_FLAGS_USED);
|
||||
spin_unlock_irqrestore(&pid_private->lock, flags);
|
||||
|
||||
is_update = FF_PID_FALSE;
|
||||
} else {
|
||||
/* We want to update an effect */
|
||||
if (!CHECK_OWNERSHIP(effect->id, pid_private))
|
||||
return -EACCES;
|
||||
|
||||
/* Parameter type cannot be updated */
|
||||
if (effect->type != pid_private->effects[effect->id].effect.type)
|
||||
return -EINVAL;
|
||||
|
||||
/* Check the effect is not already being updated */
|
||||
if (test_bit(FF_PID_FLAGS_UPDATING, &pid_private->effects[effect->id].flags))
|
||||
return -EAGAIN;
|
||||
|
||||
is_update = FF_PID_TRUE;
|
||||
}
|
||||
|
||||
/*
|
||||
* Upload the effect
|
||||
*/
|
||||
switch (effect->type) {
|
||||
case FF_PERIODIC:
|
||||
ret = pid_upload_periodic(pid_private, effect, is_update);
|
||||
break;
|
||||
|
||||
case FF_CONSTANT:
|
||||
ret = pid_upload_constant(pid_private, effect, is_update);
|
||||
break;
|
||||
|
||||
case FF_SPRING:
|
||||
case FF_FRICTION:
|
||||
case FF_DAMPER:
|
||||
case FF_INERTIA:
|
||||
ret = pid_upload_condition(pid_private, effect, is_update);
|
||||
break;
|
||||
|
||||
case FF_RAMP:
|
||||
ret = pid_upload_ramp(pid_private, effect, is_update);
|
||||
break;
|
||||
|
||||
default:
|
||||
dev_dbg(&pid_private->hid->dev->dev,
|
||||
"invalid type of effect requested - %x.\n",
|
||||
effect->type);
|
||||
return -EINVAL;
|
||||
}
|
||||
/* If a packet was sent, forbid new updates until we are notified
|
||||
* that the packet was updated
|
||||
*/
|
||||
if (ret == 0)
|
||||
set_bit(FF_PID_FLAGS_UPDATING, &pid_private->effects[effect->id].flags);
|
||||
pid_private->effects[effect->id].effect = *effect;
|
||||
return ret;
|
||||
}
|
||||
|
||||
int hid_pid_init(struct hid_device *hid)
|
||||
{
|
||||
struct hid_ff_pid *private;
|
||||
struct hid_input *hidinput = list_entry(hid->inputs.next, struct hid_input, list);
|
||||
struct input_dev *input_dev = hidinput->input;
|
||||
|
||||
private = hid->ff_private = kzalloc(sizeof(struct hid_ff_pid), GFP_KERNEL);
|
||||
if (!private)
|
||||
return -ENOMEM;
|
||||
|
||||
private->hid = hid;
|
||||
|
||||
hid->ff_exit = hid_pid_exit;
|
||||
hid->ff_event = hid_pid_event;
|
||||
|
||||
/* Open output URB */
|
||||
if (!(private->urbffout = usb_alloc_urb(0, GFP_KERNEL))) {
|
||||
kfree(private);
|
||||
return -1;
|
||||
}
|
||||
|
||||
usb_fill_control_urb(private->urbffout, hid->dev, 0,
|
||||
(void *)&private->ffcr, private->ctrl_buffer, 8,
|
||||
hid_pid_ctrl_out, hid);
|
||||
|
||||
input_dev->upload_effect = hid_pid_upload_effect;
|
||||
input_dev->flush = hid_pid_flush;
|
||||
input_dev->ff_effects_max = 8; // A random default
|
||||
set_bit(EV_FF, input_dev->evbit);
|
||||
set_bit(EV_FF_STATUS, input_dev->evbit);
|
||||
|
||||
spin_lock_init(&private->lock);
|
||||
|
||||
printk(KERN_INFO "Force feedback driver for PID devices by Rodrigo Damazio <rdamazio@lsi.usp.br>.\n");
|
||||
|
||||
return 0;
|
||||
}
|
@ -1,62 +0,0 @@
|
||||
/*
|
||||
* PID Force feedback support for hid devices.
|
||||
*
|
||||
* Copyright (c) 2002 Rodrigo Damazio.
|
||||
*/
|
||||
|
||||
/*
|
||||
* 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 <rdamazio@lsi.usp.br>
|
||||
*/
|
||||
|
||||
#define FF_EFFECTS_MAX 64
|
||||
|
||||
#define FF_PID_FLAGS_USED 1 /* If the effect exists */
|
||||
#define FF_PID_FLAGS_UPDATING 2 /* If the effect is being updated */
|
||||
#define FF_PID_FLAGS_PLAYING 3 /* If the effect is currently being played */
|
||||
|
||||
#define FF_PID_FALSE 0
|
||||
#define FF_PID_TRUE 1
|
||||
|
||||
struct hid_pid_effect {
|
||||
unsigned long flags;
|
||||
pid_t owner;
|
||||
unsigned int device_id; /* The device-assigned ID */
|
||||
struct ff_effect effect;
|
||||
};
|
||||
|
||||
struct hid_ff_pid {
|
||||
struct hid_device *hid;
|
||||
unsigned long gain;
|
||||
|
||||
struct urb *urbffout;
|
||||
struct usb_ctrlrequest ffcr;
|
||||
spinlock_t lock;
|
||||
|
||||
unsigned char ctrl_buffer[8];
|
||||
|
||||
struct hid_pid_effect effects[FF_EFFECTS_MAX];
|
||||
};
|
||||
|
||||
/*
|
||||
* Constants from the PID usage table (still far from complete)
|
||||
*/
|
||||
|
||||
#define FF_PID_USAGE_BLOCK_LOAD 0x89UL
|
||||
#define FF_PID_USAGE_BLOCK_FREE 0x90UL
|
||||
#define FF_PID_USAGE_NEW_EFFECT 0xABUL
|
||||
#define FF_PID_USAGE_POOL_REPORT 0x7FUL
|
@ -349,6 +349,9 @@ struct input_absinfo {
|
||||
|
||||
#define KEY_BATTERY 236
|
||||
|
||||
#define KEY_BLUETOOTH 237
|
||||
#define KEY_WLAN 238
|
||||
|
||||
#define KEY_UNKNOWN 240
|
||||
|
||||
#define BTN_MISC 0x100
|
||||
@ -645,6 +648,7 @@ struct input_absinfo {
|
||||
#define BUS_USB 0x03
|
||||
#define BUS_HIL 0x04
|
||||
#define BUS_BLUETOOTH 0x05
|
||||
#define BUS_VIRTUAL 0x06
|
||||
|
||||
#define BUS_ISA 0x10
|
||||
#define BUS_I8042 0x11
|
||||
@ -667,98 +671,167 @@ struct input_absinfo {
|
||||
|
||||
/*
|
||||
* Structures used in ioctls to upload effects to a device
|
||||
* The first structures are not passed directly by using ioctls.
|
||||
* They are sub-structures of the actually sent structure (called ff_effect)
|
||||
* They are pieces of a bigger structure (called ff_effect)
|
||||
*/
|
||||
|
||||
/*
|
||||
* All duration values are expressed in ms. Values above 32767 ms (0x7fff)
|
||||
* should not be used and have unspecified results.
|
||||
*/
|
||||
|
||||
/**
|
||||
* struct ff_replay - defines scheduling of the effect
|
||||
* @length: duration of the effect
|
||||
* @delay: delay before effect should start playing
|
||||
*/
|
||||
struct ff_replay {
|
||||
__u16 length; /* Duration of an effect in ms. All other times are also expressed in ms */
|
||||
__u16 delay; /* Time to wait before to start playing an effect */
|
||||
__u16 length;
|
||||
__u16 delay;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct ff_trigger - defines what triggers the effect
|
||||
* @button: number of the button triggering the effect
|
||||
* @interval: controls how soon the effect can be re-triggered
|
||||
*/
|
||||
struct ff_trigger {
|
||||
__u16 button; /* Number of button triggering an effect */
|
||||
__u16 interval; /* Time to wait before an effect can be re-triggered (ms) */
|
||||
__u16 button;
|
||||
__u16 interval;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct ff_envelope - generic effect envelope
|
||||
* @attack_length: duration of the attack (ms)
|
||||
* @attack_level: level at the beginning of the attack
|
||||
* @fade_length: duration of fade (ms)
|
||||
* @fade_level: level at the end of fade
|
||||
*
|
||||
* The @attack_level and @fade_level are absolute values; when applying
|
||||
* envelope force-feedback core will convert to positive/negative
|
||||
* value based on polarity of the default level of the effect.
|
||||
* Valid range for the attack and fade levels is 0x0000 - 0x7fff
|
||||
*/
|
||||
struct ff_envelope {
|
||||
__u16 attack_length; /* Duration of attack (ms) */
|
||||
__u16 attack_level; /* Level at beginning of attack */
|
||||
__u16 fade_length; /* Duration of fade (ms) */
|
||||
__u16 fade_level; /* Level at end of fade */
|
||||
__u16 attack_length;
|
||||
__u16 attack_level;
|
||||
__u16 fade_length;
|
||||
__u16 fade_level;
|
||||
};
|
||||
|
||||
/* FF_CONSTANT */
|
||||
/**
|
||||
* struct ff_constant_effect - defines parameters of a constant effect
|
||||
* @level: strength of the effect; may be negative
|
||||
* @envelope: envelope data
|
||||
*/
|
||||
struct ff_constant_effect {
|
||||
__s16 level; /* Strength of effect. Negative values are OK */
|
||||
__s16 level;
|
||||
struct ff_envelope envelope;
|
||||
};
|
||||
|
||||
/* FF_RAMP */
|
||||
/**
|
||||
* struct ff_ramp_effect - defines parameters of a ramp effect
|
||||
* @start_level: beginning strength of the effect; may be negative
|
||||
* @end_level: final strength of the effect; may be negative
|
||||
* @envelope: envelope data
|
||||
*/
|
||||
struct ff_ramp_effect {
|
||||
__s16 start_level;
|
||||
__s16 end_level;
|
||||
struct ff_envelope envelope;
|
||||
};
|
||||
|
||||
/* FF_SPRING of FF_FRICTION */
|
||||
/**
|
||||
* struct ff_condition_effect - defines a spring or friction effect
|
||||
* @right_saturation: maximum level when joystick moved all way to the right
|
||||
* @left_saturation: same for the left side
|
||||
* @right_coeff: controls how fast the force grows when the joystick moves
|
||||
* to the right
|
||||
* @left_coeff: same for the left side
|
||||
* @deadband: size of the dead zone, where no force is produced
|
||||
* @center: position of the dead zone
|
||||
*/
|
||||
struct ff_condition_effect {
|
||||
__u16 right_saturation; /* Max level when joystick is on the right */
|
||||
__u16 left_saturation; /* Max level when joystick in on the left */
|
||||
__u16 right_saturation;
|
||||
__u16 left_saturation;
|
||||
|
||||
__s16 right_coeff; /* Indicates how fast the force grows when the
|
||||
joystick moves to the right */
|
||||
__s16 left_coeff; /* Same for left side */
|
||||
|
||||
__u16 deadband; /* Size of area where no force is produced */
|
||||
__s16 center; /* Position of dead zone */
|
||||
__s16 right_coeff;
|
||||
__s16 left_coeff;
|
||||
|
||||
__u16 deadband;
|
||||
__s16 center;
|
||||
};
|
||||
|
||||
/* FF_PERIODIC */
|
||||
/**
|
||||
* struct ff_periodic_effect - defines parameters of a periodic effect
|
||||
* @waveform: kind of the effect (wave)
|
||||
* @period: period of the wave (ms)
|
||||
* @magnitude: peak value
|
||||
* @offset: mean value of the wave (roughly)
|
||||
* @phase: 'horizontal' shift
|
||||
* @envelope: envelope data
|
||||
* @custom_len: number of samples (FF_CUSTOM only)
|
||||
* @custom_data: buffer of samples (FF_CUSTOM only)
|
||||
*
|
||||
* Known waveforms - FF_SQUARE, FF_TRIANGLE, FF_SINE, FF_SAW_UP,
|
||||
* FF_SAW_DOWN, FF_CUSTOM. The exact syntax FF_CUSTOM is undefined
|
||||
* for the time being as no driver supports it yet.
|
||||
*
|
||||
* Note: the data pointed by custom_data is copied by the driver.
|
||||
* You can therefore dispose of the memory after the upload/update.
|
||||
*/
|
||||
struct ff_periodic_effect {
|
||||
__u16 waveform; /* Kind of wave (sine, square...) */
|
||||
__u16 period; /* in ms */
|
||||
__s16 magnitude; /* Peak value */
|
||||
__s16 offset; /* Mean value of wave (roughly) */
|
||||
__u16 phase; /* 'Horizontal' shift */
|
||||
__u16 waveform;
|
||||
__u16 period;
|
||||
__s16 magnitude;
|
||||
__s16 offset;
|
||||
__u16 phase;
|
||||
|
||||
struct ff_envelope envelope;
|
||||
|
||||
/* Only used if waveform == FF_CUSTOM */
|
||||
__u32 custom_len; /* Number of samples */
|
||||
__s16 *custom_data; /* Buffer of samples */
|
||||
/* Note: the data pointed by custom_data is copied by the driver. You can
|
||||
* therefore dispose of the memory after the upload/update */
|
||||
__u32 custom_len;
|
||||
__s16 *custom_data;
|
||||
};
|
||||
|
||||
/* FF_RUMBLE */
|
||||
/* Some rumble pads have two motors of different weight.
|
||||
strong_magnitude represents the magnitude of the vibration generated
|
||||
by the heavy motor.
|
||||
*/
|
||||
/**
|
||||
* struct ff_rumble_effect - defines parameters of a periodic effect
|
||||
* @strong_magnitude: magnitude of the heavy motor
|
||||
* @weak_magnitude: magnitude of the light one
|
||||
*
|
||||
* Some rumble pads have two motors of different weight. Strong_magnitude
|
||||
* represents the magnitude of the vibration generated by the heavy one.
|
||||
*/
|
||||
struct ff_rumble_effect {
|
||||
__u16 strong_magnitude; /* Magnitude of the heavy motor */
|
||||
__u16 weak_magnitude; /* Magnitude of the light one */
|
||||
__u16 strong_magnitude;
|
||||
__u16 weak_magnitude;
|
||||
};
|
||||
|
||||
/*
|
||||
* Structure sent through ioctl from the application to the driver
|
||||
/**
|
||||
* struct ff_effect - defines force feedback effect
|
||||
* @type: type of the effect (FF_CONSTANT, FF_PERIODIC, FF_RAMP, FF_SPRING,
|
||||
* FF_FRICTION, FF_DAMPER, FF_RUMBLE, FF_INERTIA, or FF_CUSTOM)
|
||||
* @id: an unique id assigned to an effect
|
||||
* @direction: direction of the effect
|
||||
* @trigger: trigger conditions (struct ff_trigger)
|
||||
* @replay: scheduling of the effect (struct ff_replay)
|
||||
* @u: effect-specific structure (one of ff_constant_effect, ff_ramp_effect,
|
||||
* ff_periodic_effect, ff_condition_effect, ff_rumble_effect) further
|
||||
* defining effect parameters
|
||||
*
|
||||
* This structure is sent through ioctl from the application to the driver.
|
||||
* To create a new effect aplication should set its @id to -1; the kernel
|
||||
* will return assigned @id which can later be used to update or delete
|
||||
* this effect.
|
||||
*
|
||||
* Direction of the effect is encoded as follows:
|
||||
* 0 deg -> 0x0000 (down)
|
||||
* 90 deg -> 0x4000 (left)
|
||||
* 180 deg -> 0x8000 (up)
|
||||
* 270 deg -> 0xC000 (right)
|
||||
*/
|
||||
struct ff_effect {
|
||||
__u16 type;
|
||||
/* Following field denotes the unique id assigned to an effect.
|
||||
* If user sets if to -1, a new effect is created, and its id is returned in the same field
|
||||
* Else, the user sets it to the effect id it wants to update.
|
||||
*/
|
||||
__s16 id;
|
||||
|
||||
__u16 direction; /* Direction. 0 deg -> 0x0000 (down)
|
||||
90 deg -> 0x4000 (left)
|
||||
180 deg -> 0x8000 (up)
|
||||
270 deg -> 0xC000 (right)
|
||||
*/
|
||||
|
||||
__u16 direction;
|
||||
struct ff_trigger trigger;
|
||||
struct ff_replay replay;
|
||||
|
||||
@ -784,6 +857,9 @@ struct ff_effect {
|
||||
#define FF_INERTIA 0x56
|
||||
#define FF_RAMP 0x57
|
||||
|
||||
#define FF_EFFECT_MIN FF_RUMBLE
|
||||
#define FF_EFFECT_MAX FF_RAMP
|
||||
|
||||
/*
|
||||
* Force feedback periodic effect types
|
||||
*/
|
||||
@ -795,6 +871,9 @@ struct ff_effect {
|
||||
#define FF_SAW_DOWN 0x5c
|
||||
#define FF_CUSTOM 0x5d
|
||||
|
||||
#define FF_WAVEFORM_MIN FF_SQUARE
|
||||
#define FF_WAVEFORM_MAX FF_CUSTOM
|
||||
|
||||
/*
|
||||
* Set ff device properties
|
||||
*/
|
||||
@ -864,12 +943,13 @@ struct input_dev {
|
||||
unsigned long sndbit[NBITS(SND_MAX)];
|
||||
unsigned long ffbit[NBITS(FF_MAX)];
|
||||
unsigned long swbit[NBITS(SW_MAX)];
|
||||
int ff_effects_max;
|
||||
|
||||
unsigned int keycodemax;
|
||||
unsigned int keycodesize;
|
||||
void *keycode;
|
||||
|
||||
struct ff_device *ff;
|
||||
|
||||
unsigned int repeat_key;
|
||||
struct timer_list timer;
|
||||
|
||||
@ -895,8 +975,6 @@ struct input_dev {
|
||||
void (*close)(struct input_dev *dev);
|
||||
int (*flush)(struct input_dev *dev, struct file *file);
|
||||
int (*event)(struct input_dev *dev, unsigned int type, unsigned int code, int value);
|
||||
int (*upload_effect)(struct input_dev *dev, struct ff_effect *effect);
|
||||
int (*erase_effect)(struct input_dev *dev, int effect_id);
|
||||
|
||||
struct input_handle *grab;
|
||||
|
||||
@ -904,9 +982,6 @@ struct input_dev {
|
||||
unsigned int users;
|
||||
|
||||
struct class_device cdev;
|
||||
struct device *dev; /* will be removed soon */
|
||||
|
||||
int dynalloc; /* temporarily */
|
||||
|
||||
struct list_head h_list;
|
||||
struct list_head node;
|
||||
@ -985,16 +1060,16 @@ struct input_handler {
|
||||
void *private;
|
||||
|
||||
void (*event)(struct input_handle *handle, unsigned int type, unsigned int code, int value);
|
||||
struct input_handle* (*connect)(struct input_handler *handler, struct input_dev *dev, struct input_device_id *id);
|
||||
struct input_handle* (*connect)(struct input_handler *handler, struct input_dev *dev, const struct input_device_id *id);
|
||||
void (*disconnect)(struct input_handle *handle);
|
||||
void (*start)(struct input_handle *handle);
|
||||
|
||||
const struct file_operations *fops;
|
||||
int minor;
|
||||
char *name;
|
||||
const char *name;
|
||||
|
||||
struct input_device_id *id_table;
|
||||
struct input_device_id *blacklist;
|
||||
const struct input_device_id *id_table;
|
||||
const struct input_device_id *blacklist;
|
||||
|
||||
struct list_head h_list;
|
||||
struct list_head node;
|
||||
@ -1005,7 +1080,7 @@ struct input_handle {
|
||||
void *private;
|
||||
|
||||
int open;
|
||||
char *name;
|
||||
const char *name;
|
||||
|
||||
struct input_dev *dev;
|
||||
struct input_handler *handler;
|
||||
@ -1019,12 +1094,6 @@ struct input_handle {
|
||||
#define to_handle(n) container_of(n,struct input_handle,d_node)
|
||||
#define to_handle_h(n) container_of(n,struct input_handle,h_node)
|
||||
|
||||
static inline void init_input_dev(struct input_dev *dev)
|
||||
{
|
||||
INIT_LIST_HEAD(&dev->h_list);
|
||||
INIT_LIST_HEAD(&dev->node);
|
||||
}
|
||||
|
||||
struct input_dev *input_allocate_device(void);
|
||||
void input_free_device(struct input_dev *dev);
|
||||
|
||||
@ -1041,7 +1110,7 @@ static inline void input_put_device(struct input_dev *dev)
|
||||
int input_register_device(struct input_dev *);
|
||||
void input_unregister_device(struct input_dev *);
|
||||
|
||||
void input_register_handler(struct input_handler *);
|
||||
int input_register_handler(struct input_handler *);
|
||||
void input_unregister_handler(struct input_handler *);
|
||||
|
||||
int input_grab_device(struct input_handle *);
|
||||
@ -1070,11 +1139,6 @@ static inline void input_report_abs(struct input_dev *dev, unsigned int code, in
|
||||
input_event(dev, EV_ABS, code, value);
|
||||
}
|
||||
|
||||
static inline void input_report_ff(struct input_dev *dev, unsigned int code, int value)
|
||||
{
|
||||
input_event(dev, EV_FF, code, value);
|
||||
}
|
||||
|
||||
static inline void input_report_ff_status(struct input_dev *dev, unsigned int code, int value)
|
||||
{
|
||||
input_event(dev, EV_FF_STATUS, code, value);
|
||||
@ -1108,5 +1172,61 @@ static inline void input_set_abs_params(struct input_dev *dev, int axis, int min
|
||||
|
||||
extern struct class input_class;
|
||||
|
||||
/**
|
||||
* struct ff_device - force-feedback part of an input device
|
||||
* @upload: Called to upload an new effect into device
|
||||
* @erase: Called to erase an effect from device
|
||||
* @playback: Called to request device to start playing specified effect
|
||||
* @set_gain: Called to set specified gain
|
||||
* @set_autocenter: Called to auto-center device
|
||||
* @destroy: called by input core when parent input device is being
|
||||
* destroyed
|
||||
* @private: driver-specific data, will be freed automatically
|
||||
* @ffbit: bitmap of force feedback capabilities truly supported by
|
||||
* device (not emulated like ones in input_dev->ffbit)
|
||||
* @mutex: mutex for serializing access to the device
|
||||
* @max_effects: maximum number of effects supported by device
|
||||
* @effects: pointer to an array of effects currently loaded into device
|
||||
* @effect_owners: array of effect owners; when file handle owning
|
||||
* an effect gets closed the effcet is automatically erased
|
||||
*
|
||||
* Every force-feedback device must implement upload() and playback()
|
||||
* methods; erase() is optional. set_gain() and set_autocenter() need
|
||||
* only be implemented if driver sets up FF_GAIN and FF_AUTOCENTER
|
||||
* bits.
|
||||
*/
|
||||
struct ff_device {
|
||||
int (*upload)(struct input_dev *dev, struct ff_effect *effect,
|
||||
struct ff_effect *old);
|
||||
int (*erase)(struct input_dev *dev, int effect_id);
|
||||
|
||||
int (*playback)(struct input_dev *dev, int effect_id, int value);
|
||||
void (*set_gain)(struct input_dev *dev, u16 gain);
|
||||
void (*set_autocenter)(struct input_dev *dev, u16 magnitude);
|
||||
|
||||
void (*destroy)(struct ff_device *);
|
||||
|
||||
void *private;
|
||||
|
||||
unsigned long ffbit[NBITS(FF_MAX)];
|
||||
|
||||
struct mutex mutex;
|
||||
|
||||
int max_effects;
|
||||
struct ff_effect *effects;
|
||||
struct file *effect_owners[];
|
||||
};
|
||||
|
||||
int input_ff_create(struct input_dev *dev, int max_effects);
|
||||
void input_ff_destroy(struct input_dev *dev);
|
||||
|
||||
int input_ff_event(struct input_dev *dev, unsigned int type, unsigned int code, int value);
|
||||
|
||||
int input_ff_upload(struct input_dev *dev, struct ff_effect *effect, struct file *file);
|
||||
int input_ff_erase(struct input_dev *dev, int effect_id, struct file *file);
|
||||
|
||||
int input_ff_create_memless(struct input_dev *dev, void *data,
|
||||
int (*play_effect)(struct input_dev *, void *, struct ff_effect *));
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
@ -47,5 +47,6 @@ int ps2_schedule_command(struct ps2dev *ps2dev, unsigned char *param, int comman
|
||||
int ps2_handle_ack(struct ps2dev *ps2dev, unsigned char data);
|
||||
int ps2_handle_response(struct ps2dev *ps2dev, unsigned char data);
|
||||
void ps2_cmd_aborted(struct ps2dev *ps2dev);
|
||||
int ps2_is_keyboard_id(char id);
|
||||
|
||||
#endif /* _LIBPS2_H */
|
||||
|
@ -217,5 +217,8 @@ static inline void serio_unpin_driver(struct serio *serio)
|
||||
#define SERIO_LKKBD 0x28
|
||||
#define SERIO_ELO 0x29
|
||||
#define SERIO_MICROTOUCH 0x30
|
||||
#define SERIO_PENMOUNT 0x31
|
||||
#define SERIO_TOUCHRIGHT 0x32
|
||||
#define SERIO_TOUCHWIN 0x33
|
||||
|
||||
#endif
|
||||
|
@ -22,12 +22,18 @@
|
||||
* Author: Aristeu Sergio Rozanski Filho <aris@cathedrallabs.org>
|
||||
*
|
||||
* Changes/Revisions:
|
||||
* 0.3 24/05/2006 (Anssi Hannula <anssi.hannulagmail.com>)
|
||||
* - update ff support for the changes in kernel interface
|
||||
* - add UINPUT_VERSION
|
||||
* 0.2 16/10/2004 (Micah Dowty <micah@navi.cx>)
|
||||
* - added force feedback support
|
||||
* - added UI_SET_PHYS
|
||||
* 0.1 20/06/2002
|
||||
* - first public version
|
||||
*/
|
||||
|
||||
#define UINPUT_VERSION 3
|
||||
|
||||
#ifdef __KERNEL__
|
||||
#define UINPUT_MINOR 223
|
||||
#define UINPUT_NAME "uinput"
|
||||
@ -45,7 +51,10 @@ struct uinput_request {
|
||||
|
||||
union {
|
||||
int effect_id;
|
||||
struct ff_effect* effect;
|
||||
struct {
|
||||
struct ff_effect *effect;
|
||||
struct ff_effect *old;
|
||||
} upload;
|
||||
} u;
|
||||
};
|
||||
|
||||
@ -58,6 +67,7 @@ struct uinput_device {
|
||||
unsigned char head;
|
||||
unsigned char tail;
|
||||
struct input_event buff[UINPUT_BUFFER_SIZE];
|
||||
int ff_effects_max;
|
||||
|
||||
struct uinput_request *requests[UINPUT_NUM_REQUESTS];
|
||||
wait_queue_head_t requests_waitq;
|
||||
@ -69,6 +79,7 @@ struct uinput_ff_upload {
|
||||
int request_id;
|
||||
int retval;
|
||||
struct ff_effect effect;
|
||||
struct ff_effect old;
|
||||
};
|
||||
|
||||
struct uinput_ff_erase {
|
||||
@ -98,33 +109,33 @@ struct uinput_ff_erase {
|
||||
#define UI_BEGIN_FF_ERASE _IOWR(UINPUT_IOCTL_BASE, 202, struct uinput_ff_erase)
|
||||
#define UI_END_FF_ERASE _IOW(UINPUT_IOCTL_BASE, 203, struct uinput_ff_erase)
|
||||
|
||||
/* To write a force-feedback-capable driver, the upload_effect
|
||||
/*
|
||||
* To write a force-feedback-capable driver, the upload_effect
|
||||
* and erase_effect callbacks in input_dev must be implemented.
|
||||
* The uinput driver will generate a fake input event when one of
|
||||
* these callbacks are invoked. The userspace code then uses
|
||||
* ioctls to retrieve additional parameters and send the return code.
|
||||
* The callback blocks until this return code is sent.
|
||||
*
|
||||
* The described callback mechanism is only used if EV_FF is set.
|
||||
* Otherwise, default implementations of upload_effect and erase_effect
|
||||
* are used.
|
||||
* The described callback mechanism is only used if ff_effects_max
|
||||
* is set.
|
||||
*
|
||||
* To implement upload_effect():
|
||||
* 1. Wait for an event with type==EV_UINPUT and code==UI_FF_UPLOAD.
|
||||
* 1. Wait for an event with type == EV_UINPUT and code == UI_FF_UPLOAD.
|
||||
* A request ID will be given in 'value'.
|
||||
* 2. Allocate a uinput_ff_upload struct, fill in request_id with
|
||||
* the 'value' from the EV_UINPUT event.
|
||||
* 3. Issue a UI_BEGIN_FF_UPLOAD ioctl, giving it the
|
||||
* uinput_ff_upload struct. It will be filled in with the
|
||||
* ff_effect passed to upload_effect().
|
||||
* 4. Perform the effect upload, and place the modified ff_effect
|
||||
* and a return code back into the uinput_ff_upload struct.
|
||||
* ff_effects passed to upload_effect().
|
||||
* 4. Perform the effect upload, and place a return code back into
|
||||
the uinput_ff_upload struct.
|
||||
* 5. Issue a UI_END_FF_UPLOAD ioctl, also giving it the
|
||||
* uinput_ff_upload_effect struct. This will complete execution
|
||||
* of our upload_effect() handler.
|
||||
*
|
||||
* To implement erase_effect():
|
||||
* 1. Wait for an event with type==EV_UINPUT and code==UI_FF_ERASE.
|
||||
* 1. Wait for an event with type == EV_UINPUT and code == UI_FF_ERASE.
|
||||
* A request ID will be given in 'value'.
|
||||
* 2. Allocate a uinput_ff_erase struct, fill in request_id with
|
||||
* the 'value' from the EV_UINPUT event.
|
||||
@ -133,13 +144,13 @@ struct uinput_ff_erase {
|
||||
* effect ID passed to erase_effect().
|
||||
* 4. Perform the effect erasure, and place a return code back
|
||||
* into the uinput_ff_erase struct.
|
||||
* and a return code back into the uinput_ff_erase struct.
|
||||
* 5. Issue a UI_END_FF_ERASE ioctl, also giving it the
|
||||
* uinput_ff_erase_effect struct. This will complete execution
|
||||
* of our erase_effect() handler.
|
||||
*/
|
||||
|
||||
/* This is the new event type, used only by uinput.
|
||||
/*
|
||||
* This is the new event type, used only by uinput.
|
||||
* 'code' is UI_FF_UPLOAD or UI_FF_ERASE, and 'value'
|
||||
* is the unique request ID. This number was picked
|
||||
* arbitrarily, above EV_MAX (since the input system
|
||||
|
Loading…
Reference in New Issue
Block a user