提交 a12f66fc 编写于 作者: L Linus Torvalds

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
  ...
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;
err = dev->upload_effect(dev, &effect);
if (put_user(effect.id, &(((struct ff_effect __user *)p)->id)))
return -EFAULT;
return err;
} else
return -ENOSYS;
if (copy_from_user(&effect, p, sizeof(effect)))
return -EFAULT;
case EVIOCRMFF:
if (!dev->erase_effect)
return -ENOSYS;
error = input_ff_upload(dev, &effect, file);
return dev->erase_effect(dev, (int)(unsigned long) p);
if (put_user(effect.id, &(((struct ff_effect __user *)p)->id)))
return -EFAULT;
return error;
case EVIOCRMFF:
return input_ff_erase(dev, (int)(unsigned long) p, file);
case EVIOCGEFFECTS:
if (put_user(dev->ff_effects_max, ip))
i = test_bit(EV_FF, dev->evbit) ? dev->ff->max_effects : 0;
if (put_user(i, ip))
return -EFAULT;
return 0;
......@@ -604,7 +601,7 @@ static long evdev_ioctl_compat(struct file *file, unsigned int cmd, unsigned lon
}
#endif
static struct file_operations evdev_fops = {
static const struct file_operations evdev_fops = {
.owner = THIS_MODULE,
.read = evdev_read,
.write = evdev_write,
......@@ -619,7 +616,8 @@ static struct file_operations evdev_fops = {
.flush = evdev_flush
};
static struct input_handle *evdev_connect(struct input_handler *handler, struct input_dev *dev, struct input_device_id *id)
static struct input_handle *evdev_connect(struct input_handler *handler, struct input_dev *dev,
const struct input_device_id *id)
{
struct evdev *evdev;
struct class_device *cdev;
......@@ -669,6 +667,7 @@ static void evdev_disconnect(struct input_handle *handle)
evdev->exist = 0;
if (evdev->open) {
input_flush_device(handle, NULL);
input_close_device(handle);
wake_up_interruptible(&evdev->wait);
list_for_each_entry(list, &evdev->list, node)
......@@ -677,7 +676,7 @@ static void evdev_disconnect(struct input_handle *handle)
evdev_free(evdev);
}
static struct input_device_id evdev_ids[] = {
static const struct input_device_id evdev_ids[] = {
{ .driver_info = 1 }, /* Matches all devices */
{ }, /* Terminating zero entry */
};
......@@ -696,8 +695,7 @@ static struct input_handler evdev_handler = {
static int __init evdev_init(void)
{
input_register_handler(&evdev_handler);
return 0;
return input_register_handler(&evdev_handler);
}
static void __exit evdev_exit(void)
......
/*
* 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);
/*
* 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_input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
static int iforce_playback(struct input_dev *dev, int effect_id, int value)
{
struct iforce* iforce = dev->private;
unsigned char data[3];
if (type != EV_FF)
return -1;
switch (code) {
case FF_GAIN:
data[0] = value >> 9;
iforce_send_packet(iforce, FF_CMD_GAIN, data);
return 0;
case FF_AUTOCENTER:
struct iforce_core_effect *core_effect = &iforce->core_effects[effect_id];
data[0] = 0x03;
data[1] = value >> 9;
iforce_send_packet(iforce, FF_CMD_AUTOCENTER, data);
if (value > 0)
set_bit(FF_CORE_SHOULD_PLAY, core_effect->flags);
else
clear_bit(FF_CORE_SHOULD_PLAY, core_effect->flags);
data[0] = 0x04;
data[1] = 0x01;
iforce_send_packet(iforce, FF_CMD_AUTOCENTER, data);
iforce_control_playback(iforce, effect_id, value);
return 0;
}
return 0;
static void iforce_set_gain(struct input_dev *dev, u16 gain)
{
struct iforce* iforce = dev->private;
unsigned char data[3];
default: /* Play or stop an effect */
data[0] = gain >> 9;
iforce_send_packet(iforce, FF_CMD_GAIN, data);
}
if (!CHECK_OWNERSHIP(code, iforce)) {
return -1;
}
if (value > 0) {
set_bit(FF_CORE_SHOULD_PLAY, iforce->core_effects[code].flags);
}
else {
clear_bit(FF_CORE_SHOULD_PLAY, iforce->core_effects[code].flags);
}
static void iforce_set_autocenter(struct input_dev *dev, u16 magnitude)
{
struct iforce* iforce = dev->private;
unsigned char data[3];
iforce_control_playback(iforce, code, value);
return 0;
}
data[0] = 0x03;
data[1] = magnitude >> 9;
iforce_send_packet(iforce, FF_CMD_AUTOCENTER, data);
return -1;
data[0] = 0x04;
data[1] = 0x01;
iforce_send_packet(iforce, FF_CMD_AUTOCENTER, data);
}
/*
* Function called when an ioctl is performed on the event dev entry.
* It uploads an effect to the device
*/
static int iforce_upload_effect(struct input_dev *dev, struct ff_effect *effect)
static int iforce_upload_effect(struct input_dev *dev, struct ff_effect *effect, struct ff_effect *old)
{
struct iforce* iforce = dev->private;
int id;
struct iforce_core_effect *core_effect = &iforce->core_effects[effect->id];
int ret;
int is_update;
/* Check this effect type is supported by this device */
if (!test_bit(effect->type, iforce->dev->ffbit))
return -EINVAL;
/*
* If we want to create a new effect, get a free id
*/
if (effect->id == -1) {
for (id = 0; id < FF_EFFECTS_MAX; ++id)
if (!test_and_set_bit(FF_CORE_IS_USED, iforce->core_effects[id].flags))
break;
if (id == FF_EFFECTS_MAX || id >= iforce->dev->ff_effects_max)
return -ENOMEM;
effect->id = id;
iforce->core_effects[id].owner = current->pid;
iforce->core_effects[id].flags[0] = (1 << FF_CORE_IS_USED); /* Only IS_USED bit must be set */
is_update = FALSE;
}
else {
/* We want to update an effect */
if (!CHECK_OWNERSHIP(effect->id, iforce))
return -EACCES;
/* Parameter type cannot be updated */
if (effect->type != iforce->core_effects[effect->id].effect.type)
return -EINVAL;
if (__test_and_set_bit(FF_CORE_IS_USED, core_effect->flags)) {
/* Check the effect is not already being updated */
if (test_bit(FF_CORE_UPDATE, iforce->core_effects[effect->id].flags))
if (test_bit(FF_CORE_UPDATE, core_effect->flags))
return -EAGAIN;
is_update = TRUE;
}
/*
......@@ -188,28 +142,28 @@ static int iforce_upload_effect(struct input_dev *dev, struct ff_effect *effect)
switch (effect->type) {
case FF_PERIODIC:
ret = iforce_upload_periodic(iforce, effect, is_update);
ret = iforce_upload_periodic(iforce, effect, old);
break;
case FF_CONSTANT:
ret = iforce_upload_constant(iforce, effect, is_update);
ret = iforce_upload_constant(iforce, effect, old);
break;
case FF_SPRING:
case FF_DAMPER:
ret = iforce_upload_condition(iforce, effect, is_update);
ret = iforce_upload_condition(iforce, effect, old);
break;
default:
return -EINVAL;
}
if (ret == 0) {
/* A packet was sent, forbid new updates until we are notified
* that the packet was updated
*/
set_bit(FF_CORE_UPDATE, iforce->core_effects[effect->id].flags);
set_bit(FF_CORE_UPDATE, core_effect->flags);
}
iforce->core_effects[effect->id].effect = *effect;
return ret;
}
......@@ -219,20 +173,9 @@ static int iforce_upload_effect(struct input_dev *dev, struct ff_effect *effect)
*/
static int iforce_erase_effect(struct input_dev *dev, int effect_id)
{
struct iforce* iforce = dev->private;
struct iforce *iforce = dev->private;
struct iforce_core_effect *core_effect = &iforce->core_effects[effect_id];
int err = 0;
struct iforce_core_effect* core_effect;
if (effect_id < 0 || effect_id >= FF_EFFECTS_MAX)
return -EINVAL;
core_effect = &iforce->core_effects[effect_id];
/* Check who is trying to erase this effect */
if (core_effect->owner != current->pid) {
printk(KERN_WARNING "iforce-main.c: %d tried to erase an effect belonging to %d\n", current->pid, core_effect->owner);
return -EACCES;
}
if (test_bit(FF_MOD1_IS_USED, core_effect->flags))
err = release_resource(&core_effect->mod1_chunk);
......@@ -240,7 +183,7 @@ static int iforce_erase_effect(struct input_dev *dev, int effect_id)
if (!err && test_bit(FF_MOD2_IS_USED, core_effect->flags))
err = release_resource(&core_effect->mod2_chunk);
/*TODO: remember to change that if more FF_MOD* bits are added */
/* TODO: remember to change that if more FF_MOD* bits are added */
core_effect->flags[0] = 0;
return err;
......@@ -260,52 +203,31 @@ static int iforce_open(struct input_dev *dev)
#endif
}
/* Enable force feedback */
iforce_send_packet(iforce, FF_CMD_ENABLE, "\004");
if (test_bit(EV_FF, dev->evbit)) {
/* Enable force feedback */
iforce_send_packet(iforce, FF_CMD_ENABLE, "\004");
}
return 0;
}
static int iforce_flush(struct input_dev *dev, struct file *file)
static void iforce_release(struct input_dev *dev)
{
struct iforce *iforce = dev->private;
int i;
/* Erase all effects this process owns */
for (i=0; i<dev->ff_effects_max; ++i) {
if (test_bit(FF_CORE_IS_USED, iforce->core_effects[i].flags) &&
current->pid == iforce->core_effects[i].owner) {
/* Stop effect */
input_report_ff(dev, i, 0);
/* Free ressources assigned to effect */
if (iforce_erase_effect(dev, i)) {
printk(KERN_WARNING "iforce_flush: erase effect %d failed\n", i);
if (test_bit(EV_FF, dev->evbit)) {
/* Check: no effects should be present in memory */
for (i = 0; i < dev->ff->max_effects; i++) {
if (test_bit(FF_CORE_IS_USED, iforce->core_effects[i].flags)) {
printk(KERN_WARNING "iforce_release: Device still owns effects\n");
break;
}
}
/* Disable force feedback playback */
iforce_send_packet(iforce, FF_CMD_ENABLE, "\001");
}
return 0;
}
static void iforce_release(struct input_dev *dev)
{
struct iforce *iforce = dev->private;
int i;
/* Check: no effect should be present in memory */
for (i=0; i<dev->ff_effects_max; ++i) {
if (test_bit(FF_CORE_IS_USED, iforce->core_effects[i].flags))
break;
}
if (i<dev->ff_effects_max) {
printk(KERN_WARNING "iforce_release: Device still owns effects\n");
}
/* Disable force feedback playback */
iforce_send_packet(iforce, FF_CMD_ENABLE, "\001");
switch (iforce->bus) {
#ifdef CONFIG_JOYSTICK_IFORCE_USB
......@@ -342,8 +264,10 @@ void iforce_delete_device(struct iforce *iforce)
int iforce_init_device(struct iforce *iforce)
{
struct input_dev *input_dev;
struct ff_device *ff;
unsigned char c[] = "CEOV";
int i;
int i, error;
int ff_effects = 0;
input_dev = input_allocate_device();
if (!input_dev)
......@@ -378,11 +302,6 @@ int iforce_init_device(struct iforce *iforce)
input_dev->name = "Unknown I-Force device";
input_dev->open = iforce_open;
input_dev->close = iforce_release;
input_dev->flush = iforce_flush;
input_dev->event = iforce_input_event;
input_dev->upload_effect = iforce_upload_effect;
input_dev->erase_effect = iforce_erase_effect;
input_dev->ff_effects_max = 10;
/*
* On-device memory allocation.
......@@ -430,15 +349,15 @@ int iforce_init_device(struct iforce *iforce)
printk(KERN_WARNING "iforce-main.c: Device does not respond to id packet B\n");
if (!iforce_get_id_packet(iforce, "N"))
iforce->dev->ff_effects_max = iforce->edata[1];
ff_effects = iforce->edata[1];
else
printk(KERN_WARNING "iforce-main.c: Device does not respond to id packet N\n");
/* Check if the device can store more effects than the driver can really handle */
if (iforce->dev->ff_effects_max > FF_EFFECTS_MAX) {
printk(KERN_WARNING "input??: Device can handle %d effects, but N_EFFECTS_MAX is set to %d in iforce.h\n",
iforce->dev->ff_effects_max, FF_EFFECTS_MAX);
iforce->dev->ff_effects_max = FF_EFFECTS_MAX;
if (ff_effects > IFORCE_EFFECTS_MAX) {
printk(KERN_WARNING "iforce: Limiting number of effects to %d (device reports %d)\n",
IFORCE_EFFECTS_MAX, ff_effects);
ff_effects = IFORCE_EFFECTS_MAX;
}
/*
......@@ -472,12 +391,10 @@ int iforce_init_device(struct iforce *iforce)
* Set input device bitfields and ranges.
*/
input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_ABS) | BIT(EV_FF) | BIT(EV_FF_STATUS);
input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_ABS) | BIT(EV_FF_STATUS);
for (i = 0; iforce->type->btn[i] >= 0; i++) {
signed short t = iforce->type->btn[i];
set_bit(t, input_dev->keybit);
}
for (i = 0; iforce->type->btn[i] >= 0; i++)
set_bit(iforce->type->btn[i], input_dev->keybit);
set_bit(BTN_DEAD, input_dev->keybit);
for (i = 0; iforce->type->abs[i] >= 0; i++) {
......@@ -516,9 +433,24 @@ int iforce_init_device(struct iforce *iforce)
}
}
for (i = 0; iforce->type->ff[i] >= 0; i++)
set_bit(iforce->type->ff[i], input_dev->ffbit);
if (ff_effects) {
for (i = 0; iforce->type->ff[i] >= 0; i++)
set_bit(iforce->type->ff[i], input_dev->ffbit);
error = input_ff_create(input_dev, ff_effects);
if (error) {
input_free_device(input_dev);
return error;
}
ff = input_dev->ff;
ff->upload = iforce_upload_effect;
ff->erase = iforce_erase_effect;
ff->set_gain = iforce_set_gain;
ff->set_autocenter = iforce_set_autocenter;
ff->playback = iforce_playback;
}
/*
* Register input device.
*/
......
......@@ -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];
......
/*
* 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 @@ int alps_init(struct psmouse *psmouse)
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, &params[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, &params[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"),
},
},
{ }
};
......
此差异已折叠。
......@@ -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;
}
......
/*
* 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);
/*
* 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);
此差异已折叠。
......@@ -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)
{
......
此差异已折叠。
......@@ -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;
......
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册