[PATCH] Add Force Feedback interface to joydev

From: Anssi Hannula
Date: Fri Apr 08 2005 - 12:36:44 EST


This patch adds Force Feedback interface to joydev. I felt this necessary because games usually don't run as root while evdev usually can't be read or written by anyone else. Patch is against 2.6.12-rc2. If there is a reason this can't be applied or needs modifications, please say it :)

If I have enough time and skills, I will start developing userspace Force Feedback library, which would do (among other things) all necessary force convertings between joystick, gamepad and wheel controllers.

Does anyone have any thoughts about this?

Signed-off-by: Anssi Hannula <anssi.hannula@xxxxxxxx>

diff -Nurp linux-2.6.11/Documentation/input/ff.txt linux-2.6.11-ffjoy/Documentation/input/ff.txt
--- linux-2.6.11/Documentation/input/ff.txt 2005-03-02 09:38:12.000000000 +0200
+++ linux-2.6.11-ffjoy/Documentation/input/ff.txt 2005-04-08 00:07:50.000000000 +0300
@@ -1,5 +1,6 @@
Force feedback for Linux.
By Johann Deneux <deneux@xxxxxxxxxxx> on 2001/04/22.
+Updated & cleaned on 2005/04/04 by Anssi Hannula <anssi.hannula@xxxxxxxxx>
You may redistribute this file. Please remember to include shape.fig and
interactive.fig as well.
----------------------------------------------------------------------------
@@ -10,18 +11,11 @@ This document describes how to use force
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.
+Please read joystick.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.
+Here are instructions on how to compile and use the driver.

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".
@@ -29,19 +23,18 @@ To stop this annoying behaviour, move yo
should keep a hand on your device, in order to avoid it to brake down if
something goes wrong.

-At the kernel's compilation:
- - Enable IForce/Serial
- - Enable Event interface
+At the kernel's compilation enable the Force Feedback support for devices you
+have.

Compile the modules, install them.

-You also need inputattach.
+For serial devices 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
+% modprobe iforce # Example of FF driver
+% modprobe evdev # For evdev-interface
% ./inputattach -ifor $2 & # Only for serial
If you are using USB, you don't need the inputattach step.

@@ -66,23 +59,37 @@ mknod input/event3 c 13 67
2.1 Does it work ?
~~~~~~~~~~~~~~~~~~
There is an utility called fftest that will allow you to test the driver.
+% fftest /dev/input/jsXX
+
+You can test the evdev interface with:
% fftest /dev/input/eventXX

+Please note that joydev (jsXX) interface is recommended over evdev due to
+security issues involved in allowing users to access evdev devices.
+
3. Instructions to the developper
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- 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.
+All interactions are done using the joydev API. That is, you can use ioctl()
+and write() on /dev/input/jsXX. There is also an evdev interface on
+/dev/input/eventXX.
+
+Joydev FF API is present in JS_VERSION >= 0x020200.
+Evdev FF API is present in all 2.6 series kernels.
+Use the joydev API instead of evdev whenever possible.
+
+This information is subject to change.

3.1 Querying device capabilities
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#include <linux/input.h>
+#include <linux/joystick.h>
#include <sys/ioctl.h>

unsigned long features[1 + FF_MAX/sizeof(unsigned long)];
int ioctl(int file_descriptor, int request, unsigned long *features);

-"request" must be EVIOCGBIT(EV_FF, size of features array in bytes )
+"request" must be JSIOCGFFBIT(size of features array in bytes).
+The evdev version is EVIOCGBIT(EV_FF, size of features array in bytes ).

Returns the features supported by the device. features is a bitfield with the
following bits:
@@ -100,6 +107,7 @@ following bits:
- FF_INERTIA can simulate inertia


+int ioctl(int fd, JSIOCGEFFECTS, int *n);
int ioctl(int fd, EVIOCGEFFECTS, int *n);

Returns the number of effects the device can keep in its memory.
@@ -107,11 +115,12 @@ Returns the number of effects the device
3.2 Uploading effects to the device
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#include <linux/input.h>
+#include <linux/joystick.h>
#include <sys/ioctl.h>

int ioctl(int file_descriptor, int request, struct ff_effect *effect);

-"request" must be EVIOCSFF.
+"request" must be JSIOCSFF (EVIOCSFF for evdev).

"effect" points to a structure describing the effect to upload. The effect is
uploaded, but not played.
@@ -126,6 +135,7 @@ You need xfig to visualize these files.

3.3 Removing an effect from the device
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+int ioctl(int fd, JSIOCRMFF, effect.id);
int ioctl(int fd, EVIOCRMFF, effect.id);

This makes room for new effects in the device's memory. Please note this won't
@@ -136,6 +146,7 @@ stop the effect if it was playing.
Control of playing is done with write(). Below is an example:

#include <linux/input.h>
+#include <linux/joystick.h>
#include <unistd.h>

struct input_event play;
@@ -143,7 +154,7 @@ Control of playing is done with write().
struct ff_effect effect;
int fd;
...
- fd = open("/dev/input/eventXX", O_RDWR);
+ fd = open("/dev/input/jsXX", O_RDWR);
...
/* Play three times */
play.type = EV_FF;
@@ -207,8 +218,11 @@ case, the driver stops the effect, up-lo

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:
+Every time the status of an effect is changed, an event is sent.
+
+NOTE: This appears to not be implemented in all drivers at the moment.
+
+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;
diff -Nurp linux-2.6.11/drivers/input/joydev.c linux-2.6.11-ffjoy/drivers/input/joydev.c
--- linux-2.6.11/drivers/input/joydev.c 2005-04-07 23:57:21.000000000 +0300
+++ linux-2.6.11-ffjoy/drivers/input/joydev.c 2005-04-08 00:04:40.000000000 +0300
@@ -141,6 +141,13 @@ static int joydev_fasync(int fd, struct
return retval < 0 ? retval : 0;
}

+static int joydev_flush(struct file * file)
+{
+ struct joydev_list *list = file->private_data;
+ if (!list->joydev->exist) return -ENODEV;
+ return input_flush_device(&list->joydev->handle, file);
+}
+
static void joydev_free(struct joydev *joydev)
{
joydev_table[joydev->minor] = NULL;
@@ -191,7 +198,21 @@ static int joydev_open(struct inode *ino

static ssize_t joydev_write(struct file * file, const char __user * buffer, size_t count, loff_t *ppos)
{
- return -EINVAL;
+ struct joydev_list *list = file->private_data;
+ struct input_event event;
+ int retval = 0;
+
+ if (!list->joydev->exist) return -ENODEV;
+
+ while (retval < count) {
+
+ if (copy_from_user(&event, buffer + retval, sizeof(struct input_event)))
+ return -EFAULT;
+ input_event(list->joydev->handle.dev, event.type, event.code, event.value);
+ retval += sizeof(struct input_event);
+ }
+
+ return retval;
}

static ssize_t joydev_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
@@ -358,6 +379,26 @@ static int joydev_ioctl(struct inode *in
case JSIOCGBTNMAP:
return copy_to_user(argp, joydev->keypam,
sizeof(__u16) * (KEY_MAX - BTN_MISC + 1)) ? -EFAULT : 0;
+ case JSIOCSFF:
+ if (dev->upload_effect) {
+ struct ff_effect effect;
+ int err;
+ if (copy_from_user(&effect, argp, sizeof(effect)))
+ return -EFAULT;
+ err = dev->upload_effect(dev, &effect);
+ if (put_user(effect.id, &(((struct ff_effect __user *)arg)->id)))
+ return -EFAULT;
+ return err;
+ }
+ else return -ENOSYS;
+ case JSIOCRMFF:
+ if (dev->erase_effect)
+ return dev->erase_effect(dev, (int)arg);
+ return -ENOSYS;
+ case JSIOCGEFFECTS:
+ if (put_user(dev->ff_effects_max, (int __user *)arg))
+ return -EFAULT;
+ return 0;
default:
if ((cmd & ~(_IOC_SIZEMASK << _IOC_SIZESHIFT)) == JSIOCGNAME(0)) {
int len;
@@ -367,6 +408,15 @@ static int joydev_ioctl(struct inode *in
if (copy_to_user(argp, dev->name, len)) return -EFAULT;
return len;
}
+ if (_IOC_NR(cmd) == _IOC_NR(JSIOCGFFBIT(0))) {
+ int len;
+ len = NBITS(FF_MAX) * sizeof(long);
+ if (len > _IOC_SIZE(cmd))
+ len = _IOC_SIZE(cmd);
+ if (copy_to_user(argp, dev->ffbit, len))
+ return -EFAULT;
+ return len;
+ }
}
return -EINVAL;
}
@@ -380,6 +430,7 @@ static struct file_operations joydev_fop
.release = joydev_release,
.ioctl = joydev_ioctl,
.fasync = joydev_fasync,
+ .flush = joydev_flush,
};

static struct input_handle *joydev_connect(struct input_handler *handler, struct input_dev *dev, struct input_device_id *id)
diff -Nurp linux-2.6.11/include/linux/joystick.h linux-2.6.11-ffjoy/include/linux/joystick.h
--- linux-2.6.11/include/linux/joystick.h 2005-04-07 23:57:42.000000000 +0300
+++ linux-2.6.11-ffjoy/include/linux/joystick.h 2005-04-07 23:59:59.000000000 +0300
@@ -36,7 +36,7 @@
* Version
*/

-#define JS_VERSION 0x020100
+#define JS_VERSION 0x020200

/*
* Types and constants for reading from /dev/js
@@ -71,6 +71,13 @@ struct js_event {
#define JSIOCSBTNMAP _IOW('j', 0x33, __u16[KEY_MAX - BTN_MISC + 1]) /* set button mapping */
#define JSIOCGBTNMAP _IOR('j', 0x34, __u16[KEY_MAX - BTN_MISC + 1]) /* get button mapping */

+#define JSIOCGFFBIT(len) _IOC(_IOC_READ, 'j', 0x41, len) /* get force effect bits */
+#define JSIOCSFF _IOW('j', 0x42, struct ff_effect)
+ /* upload a force effect to a force feedback device */
+#define JSIOCRMFF _IOW('j', 0x43, int) /* erase a force effect */
+#define JSIOCGEFFECTS _IOR('j', 0x44, int)
+ /* get number of effects playable at the same time */
+
/*
* Types and constants for get/set correction
*/