patch-2.4.10 linux/drivers/char/joystick/iforce.c
Next file: linux/drivers/char/joystick/interact.c
Previous file: linux/drivers/char/joystick/grip.c
Back to the patch index
Back to the overall index
- Lines: 1240
- Date:
Wed Sep 12 15:34:06 2001
- Orig file:
v2.4.9/linux/drivers/char/joystick/iforce.c
- Orig date:
Wed Apr 11 19:02:30 2001
diff -u --recursive --new-file v2.4.9/linux/drivers/char/joystick/iforce.c linux/drivers/char/joystick/iforce.c
@@ -1,7 +1,8 @@
/*
- * $Id: iforce.c,v 1.7 2000/06/04 14:03:36 vojtech Exp $
+ * $Id: iforce.c,v 1.56 2001/05/27 14:41:26 jdeneux Exp $
*
- * Copyright (c) 2000 Vojtech Pavlik
+ * Copyright (c) 2000-2001 Vojtech Pavlik <vojtech@suse.cz>
+ * Copyright (c) 2001 Johann Deneux <deneux@ifrance.com>
*
* USB/RS232 I-Force joysticks and wheels.
*
@@ -11,18 +12,18 @@
/*
* 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
+ * 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 <vojtech@suse.cz>, or by paper mail:
* Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic
@@ -38,46 +39,216 @@
#include <linux/serio.h>
#include <linux/config.h>
-MODULE_AUTHOR("Vojtech Pavlik <vojtech@suse.cz>");
-MODULE_DESCRIPTION("USB/RS232 I-Force joysticks and wheels driver");
+/* FF: This module provides arbitrary resource management routines.
+ * I use it to manage the device's memory.
+ * Despite the name of this module, I am *not* going to access the ioports.
+ */
+#include <linux/ioport.h>
-#define USB_VENDOR_ID_LOGITECH 0x046d
-#define USB_DEVICE_ID_LOGITECH_WMFORCE 0xc281
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@suse.cz>, Johann Deneux <deneux@ifrance.com>");
+MODULE_DESCRIPTION("USB/RS232 I-Force joysticks and wheels driver");
+MODULE_LICENSE("GPL");
-#define IFORCE_MAX_LENGTH 16
+#define IFORCE_MAX_LENGTH 16
#if defined(CONFIG_INPUT_IFORCE_232) || defined(CONFIG_INPUT_IFORCE_232_MODULE)
-#define IFORCE_232
+#define IFORCE_232 1
#endif
#if defined(CONFIG_INPUT_IFORCE_USB) || defined(CONFIG_INPUT_IFORCE_USB_MODULE)
-#define IFORCE_USB
+#define IFORCE_USB 2
#endif
+#define FF_EFFECTS_MAX 32
+
+/* Each force feedback effect is made of one core effect, which can be
+ * associated to at most to effect modifiers
+ */
+#define FF_MOD1_IS_USED 0
+#define FF_MOD2_IS_USED 1
+#define FF_CORE_IS_USED 2
+#define FF_CORE_IS_PLAYED 3
+#define FF_MODCORE_MAX 3
+
+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)];
+};
+
+#define FF_CMD_EFFECT 0x010e
+#define FF_CMD_SHAPE 0x0208
+#define FF_CMD_MAGNITUDE 0x0303
+#define FF_CMD_PERIOD 0x0407
+#define FF_CMD_INTERACT 0x050a
+
+#define FF_CMD_AUTOCENTER 0x4002
+#define FF_CMD_PLAY 0x4103
+#define FF_CMD_ENABLE 0x4201
+#define FF_CMD_GAIN 0x4301
+
+#define FF_CMD_QUERY 0xff01
+
+static signed short btn_joystick[] = { BTN_TRIGGER, BTN_TOP, BTN_THUMB, BTN_TOP2, BTN_BASE,
+ BTN_BASE2, BTN_BASE3, BTN_BASE4, BTN_BASE5, BTN_A, BTN_B, BTN_C, BTN_DEAD, -1 };
+static signed short btn_wheel[] = { BTN_TRIGGER, BTN_TOP, BTN_THUMB, BTN_TOP2, BTN_BASE,
+ BTN_BASE2, BTN_BASE3, BTN_BASE4, BTN_BASE5, BTN_A, BTN_B, BTN_C, -1 };
+static signed short abs_joystick[] = { ABS_X, ABS_Y, ABS_THROTTLE, ABS_HAT0X, ABS_HAT0Y, -1 };
+static signed short abs_wheel[] = { ABS_WHEEL, ABS_GAS, ABS_BRAKE, ABS_HAT0X, ABS_HAT0Y, -1 };
+static signed short ff_iforce[] = { FF_PERIODIC, FF_CONSTANT, FF_SPRING, FF_FRICTION,
+ FF_SQUARE, FF_TRIANGLE, FF_SINE, FF_SAW_UP, FF_SAW_DOWN, FF_GAIN, FF_AUTOCENTER, -1 };
+
+static struct iforce_device {
+ u16 idvendor;
+ u16 idproduct;
+ char *name;
+ signed short *btn;
+ signed short *abs;
+ signed short *ff;
+} iforce_device[] = {
+ { 0x046d, 0xc281, "Logitech WingMan Force", btn_joystick, abs_joystick, ff_iforce },
+ { 0x046d, 0xc291, "Logitech WingMan Formula Force", btn_wheel, abs_wheel, ff_iforce },
+ { 0x05ef, 0x020a, "AVB Top Shot Pegasus", btn_joystick, abs_joystick, ff_iforce },
+ { 0x05ef, 0x8884, "AVB Mag Turbo Force", btn_wheel, abs_wheel, ff_iforce },
+ { 0x06f8, 0x0001, "Guillemot Race Leader Force Feedback", btn_wheel, abs_wheel, ff_iforce },
+ { 0x0000, 0x0000, "Unknown I-Force Device [%04x:%04x]", btn_joystick, abs_joystick, ff_iforce }
+};
+
struct iforce {
- signed char data[IFORCE_MAX_LENGTH];
- struct usb_device *usbdev;
- struct input_dev dev;
- struct urb irq;
+ struct input_dev dev; /* Input device interface */
+ struct iforce_device *type;
+ char name[64];
int open;
+ int bus;
+
+ unsigned char data[IFORCE_MAX_LENGTH];
+ unsigned char edata[IFORCE_MAX_LENGTH];
+ u16 ecmd;
+ u16 expect_packet;
+
+#ifdef IFORCE_232
+ struct serio *serio; /* RS232 transfer */
int idx, pkt, len, id;
unsigned char csum;
+#endif
+#ifdef IFORCE_USB
+ struct usb_device *usbdev; /* USB transfer */
+ struct urb irq, out, ctrl;
+ devrequest dr;
+#endif
+ /* Force Feedback */
+ wait_queue_head_t wait;
+ struct resource device_memory;
+ struct iforce_core_effect core_effects[FF_EFFECTS_MAX];
};
static struct {
- __s32 x;
- __s32 y;
+ __s32 x;
+ __s32 y;
} iforce_hat_to_axis[16] = {{ 0,-1}, { 1,-1}, { 1, 0}, { 1, 1}, { 0, 1}, {-1, 1}, {-1, 0}, {-1,-1}};
-static char *iforce_name = "I-Force joystick/wheel";
+/* Get hi and low bytes of a 16-bits int */
+#define HI(a) ((unsigned char)((a) >> 8))
+#define LO(a) ((unsigned char)((a) & 0xff))
+
+/* Encode a time value */
+#define TIME_SCALE(a) ((a) == 0xffff ? 0xffff : (a) * 1000 / 256)
-static void iforce_process_packet(struct input_dev *dev, unsigned char id, int idx, unsigned char *data)
+static void dump_packet(char *msg, u16 cmd, unsigned char *data)
{
- switch (id) {
+ int i;
- case 1: /* joystick position data */
- case 3: /* wheel position data */
+ printk(KERN_DEBUG "iforce.c: %s ( cmd = %04x, data = ", msg, cmd);
+ for (i = 0; i < LO(cmd); i++)
+ printk("%02x ", data[i]);
+ printk(")\n");
+}
- if (id == 1) {
+/*
+ * Send a packet of bytes to the device
+ */
+static void send_packet(struct iforce *iforce, u16 cmd, unsigned char* data)
+{
+ switch (iforce->bus) {
+
+#ifdef IFORCE_232
+ case IFORCE_232: {
+
+ int i;
+ unsigned char csum = 0x2b ^ HI(cmd) ^ LO(cmd);
+
+ serio_write(iforce->serio, 0x2b);
+ serio_write(iforce->serio, HI(cmd));
+ serio_write(iforce->serio, LO(cmd));
+
+ for (i = 0; i < LO(cmd); i++) {
+ serio_write(iforce->serio, data[i]);
+ csum = csum ^ data[i];
+ }
+
+ serio_write(iforce->serio, csum);
+ return;
+ }
+#endif
+#ifdef IFORCE_USB
+ case IFORCE_USB: {
+
+ DECLARE_WAITQUEUE(wait, current);
+ int timeout = HZ; /* 1 second */
+
+ memcpy(iforce->out.transfer_buffer + 1, data, LO(cmd));
+ ((char*)iforce->out.transfer_buffer)[0] = HI(cmd);
+ iforce->out.transfer_buffer_length = LO(cmd) + 2;
+ iforce->out.dev = iforce->usbdev;
+
+ set_current_state(TASK_INTERRUPTIBLE);
+ add_wait_queue(&iforce->wait, &wait);
+
+ if (usb_submit_urb(&iforce->out)) {
+ set_current_state(TASK_RUNNING);
+ remove_wait_queue(&iforce->wait, &wait);
+ return;
+ }
+
+ while (timeout && iforce->out.status == -EINPROGRESS)
+ timeout = schedule_timeout(timeout);
+
+ set_current_state(TASK_RUNNING);
+ remove_wait_queue(&iforce->wait, &wait);
+
+ if (!timeout)
+ usb_unlink_urb(&iforce->out);
+
+ return;
+ }
+#endif
+ }
+}
+
+static void iforce_process_packet(struct iforce *iforce, u16 cmd, unsigned char *data)
+{
+ struct input_dev *dev = &iforce->dev;
+ int i;
+
+#ifdef IFORCE_232
+ if (HI(iforce->expect_packet) == HI(cmd)) {
+ iforce->expect_packet = 0;
+ iforce->ecmd = cmd;
+ memcpy(iforce->edata, data, IFORCE_MAX_LENGTH);
+ if (waitqueue_active(&iforce->wait))
+ wake_up(&iforce->wait);
+ }
+#endif
+
+ if (!iforce->type)
+ return;
+
+ switch (HI(cmd)) {
+
+ case 0x01: /* joystick position data */
+ case 0x03: /* wheel position data */
+
+ if (HI(cmd) == 1) {
input_report_abs(dev, ABS_X, (__s16) (((__s16)data[1] << 8) | data[0]));
input_report_abs(dev, ABS_Y, (__s16) (((__s16)data[3] << 8) | data[2]));
input_report_abs(dev, ABS_THROTTLE, 255 - data[4]);
@@ -90,37 +261,95 @@
input_report_abs(dev, ABS_HAT0X, iforce_hat_to_axis[data[6] >> 4].x);
input_report_abs(dev, ABS_HAT0Y, iforce_hat_to_axis[data[6] >> 4].y);
- input_report_key(dev, BTN_TRIGGER, data[5] & 0x01);
- input_report_key(dev, BTN_TOP, data[5] & 0x02);
- input_report_key(dev, BTN_THUMB, data[5] & 0x04);
- input_report_key(dev, BTN_TOP2, data[5] & 0x08);
- input_report_key(dev, BTN_BASE, data[5] & 0x10);
- input_report_key(dev, BTN_BASE2, data[5] & 0x20);
- input_report_key(dev, BTN_BASE3, data[5] & 0x40);
- input_report_key(dev, BTN_BASE4, data[5] & 0x80);
- input_report_key(dev, BTN_BASE5, data[6] & 0x01);
- input_report_key(dev, BTN_A, data[6] & 0x02);
- input_report_key(dev, BTN_B, data[6] & 0x04);
- input_report_key(dev, BTN_C, data[6] & 0x08);
+ for (i = 0; iforce->type->btn[i] >= 0; i++)
+ input_report_key(dev, iforce->type->btn[i], data[(i >> 3) + 5] & (1 << (i & 7)));
+
break;
- case 2: /* force feedback effect status */
+ case 0x02: /* status report */
+
+ input_report_key(dev, BTN_DEAD, data[0] & 0x02);
break;
}
}
+static int get_id_packet(struct iforce *iforce, char *packet)
+{
+ DECLARE_WAITQUEUE(wait, current);
+ int timeout = HZ; /* 1 second */
+
+ switch (iforce->bus) {
+
#ifdef IFORCE_USB
+ case IFORCE_USB:
+
+ iforce->dr.request = packet[0];
+ iforce->ctrl.dev = iforce->usbdev;
+
+ set_current_state(TASK_INTERRUPTIBLE);
+ add_wait_queue(&iforce->wait, &wait);
+
+ if (usb_submit_urb(&iforce->ctrl)) {
+ set_current_state(TASK_RUNNING);
+ remove_wait_queue(&iforce->wait, &wait);
+ return -1;
+ }
+
+ while (timeout && iforce->ctrl.status == -EINPROGRESS)
+ timeout = schedule_timeout(timeout);
+
+ set_current_state(TASK_RUNNING);
+ remove_wait_queue(&iforce->wait, &wait);
+
+ if (!timeout) {
+ usb_unlink_urb(&iforce->ctrl);
+ return -1;
+ }
+
+ break;
+#endif
+#ifdef IFORCE_232
+ case IFORCE_232:
+
+ iforce->expect_packet = FF_CMD_QUERY;
+ send_packet(iforce, FF_CMD_QUERY, packet);
+
+ set_current_state(TASK_INTERRUPTIBLE);
+ add_wait_queue(&iforce->wait, &wait);
+
+ while (timeout && iforce->expect_packet)
+ timeout = schedule_timeout(timeout);
+
+ set_current_state(TASK_RUNNING);
+ remove_wait_queue(&iforce->wait, &wait);
+
+ if (!timeout) {
+ iforce->expect_packet = 0;
+ return -1;
+ }
+
+ break;
+#endif
+ }
+
+ return -(iforce->edata[0] != packet[0]);
+}
static int iforce_open(struct input_dev *dev)
{
struct iforce *iforce = dev->private;
- if (dev->idbus == BUS_USB && !iforce->open++) {
- iforce->irq.dev = iforce->usbdev;
- if (usb_submit_urb(&iforce->irq))
- return -EIO;
+ switch (iforce->bus) {
+#ifdef IFORCE_USB
+ case IFORCE_USB:
+ if (iforce->open++)
+ break;
+ iforce->irq.dev = iforce->usbdev;
+ if (usb_submit_urb(&iforce->irq))
+ return -EIO;
+ break;
+#endif
}
-
return 0;
}
@@ -128,48 +357,632 @@
{
struct iforce *iforce = dev->private;
- if (dev->idbus == BUS_USB && !--iforce->open)
- usb_unlink_urb(&iforce->irq);
+ switch (iforce->bus) {
+#ifdef IFORCE_USB
+ case IFORCE_USB:
+ if (!--iforce->open)
+ usb_unlink_urb(&iforce->irq);
+ break;
+#endif
+ }
}
-#endif
+/*
+ * Start or stop playing an effect
+ */
+
+static int iforce_input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
+{
+ struct iforce* iforce = (struct iforce*)(dev->private);
+ unsigned char data[3];
+
+ printk(KERN_DEBUG "iforce.c: input_event(type = %d, code = %d, value = %d)\n", type, code, value);
+
+ if (type != EV_FF)
+ return -1;
+
+ switch (code) {
+
+ case FF_GAIN:
+
+ data[0] = value >> 9;
+ send_packet(iforce, FF_CMD_GAIN, data);
+
+ return 0;
+
+ case FF_AUTOCENTER:
+
+ data[0] = 0x03;
+ data[1] = value >> 9;
+ send_packet(iforce, FF_CMD_AUTOCENTER, data);
+
+ data[0] = 0x04;
+ data[1] = 0x01;
+ send_packet(iforce, FF_CMD_AUTOCENTER, data);
+
+ return 0;
+
+ default: /* Play an effect */
+
+ if (code >= iforce->dev.ff_effects_max)
+ return -1;
+
+ data[0] = LO(code);
+ data[1] = (value > 0) ? ((value > 1) ? 0x41 : 0x01) : 0;
+ data[2] = LO(value);
+ send_packet(iforce, FF_CMD_PLAY, data);
+
+ return 0;
+ }
+
+ return -1;
+}
+
+/*
+ * Set the magnitude of a constant force effect
+ * Return error code
+ *
+ * Note: caller must ensure exclusive access to device
+ */
+
+static int make_magnitude_modifier(struct iforce* iforce,
+ struct resource* mod_chunk, __s16 level)
+{
+ unsigned char data[3];
+
+ if (allocate_resource(&(iforce->device_memory), mod_chunk, 2,
+ iforce->device_memory.start, iforce->device_memory.end, 2L,
+ NULL, NULL)) {
+ return -ENOMEM;
+ }
+
+ data[0] = LO(mod_chunk->start);
+ data[1] = HI(mod_chunk->start);
+ data[2] = HI(level);
+
+ send_packet(iforce, FF_CMD_MAGNITUDE, data);
+
+ return 0;
+}
+
+/*
+ * Upload the component of an effect dealing with the period, phase and magnitude
+ */
+
+static int make_period_modifier(struct iforce* iforce, struct resource* mod_chunk,
+ __s16 magnitude, __s16 offset, u16 period, u16 phase)
+{
+ unsigned char data[7];
+
+ period = TIME_SCALE(period);
+
+ if (allocate_resource(&(iforce->device_memory), mod_chunk, 0x0c,
+ iforce->device_memory.start, iforce->device_memory.end, 2L,
+ NULL, NULL)) {
+ return -ENOMEM;
+ }
+
+ data[0] = LO(mod_chunk->start);
+ data[1] = HI(mod_chunk->start);
+
+ data[2] = HI(magnitude);
+ data[3] = HI(offset);
+ data[4] = HI(phase);
+
+ data[5] = LO(period);
+ data[6] = HI(period);
+
+ send_packet(iforce, FF_CMD_PERIOD, data);
+
+ return 0;
+}
+
+/*
+ * Uploads the part of an effect setting the shape of the force
+ */
+
+static int make_shape_modifier(struct iforce* iforce, struct resource* mod_chunk,
+ u16 attack_duration, __s16 initial_level,
+ u16 fade_duration, __s16 final_level)
+{
+ unsigned char data[8];
+
+ attack_duration = TIME_SCALE(attack_duration);
+ fade_duration = TIME_SCALE(fade_duration);
+
+ if (allocate_resource(&(iforce->device_memory), mod_chunk, 0x0e,
+ iforce->device_memory.start, iforce->device_memory.end, 2L,
+ NULL, NULL)) {
+ return -ENOMEM;
+ }
+
+ data[0] = LO(mod_chunk->start);
+ data[1] = HI(mod_chunk->start);
+
+ data[2] = LO(attack_duration);
+ data[3] = HI(attack_duration);
+ data[4] = HI(initial_level);
+
+ data[5] = LO(fade_duration);
+ data[6] = HI(fade_duration);
+ data[7] = HI(final_level);
+
+ send_packet(iforce, FF_CMD_SHAPE, data);
+
+ return 0;
+}
+
+/*
+ * Component of spring, friction, inertia... effects
+ */
+
+static int make_interactive_modifier(struct iforce* iforce,
+ struct resource* mod_chunk,
+ __s16 rsat, __s16 lsat, __s16 rk, __s16 lk, u16 db, __s16 center)
+{
+ unsigned char data[10];
+
+ if (allocate_resource(&(iforce->device_memory), mod_chunk, 8,
+ iforce->device_memory.start, iforce->device_memory.end, 2L,
+ NULL, NULL)) {
+ return -ENOMEM;
+ }
+
+ data[0] = LO(mod_chunk->start);
+ data[1] = HI(mod_chunk->start);
+
+ data[2] = HI(rk);
+ data[3] = HI(lk);
+
+ data[4] = LO(center);
+ data[5] = HI(center);
+
+ data[6] = LO(db);
+ data[7] = HI(db);
-static void iforce_input_setup(struct iforce *iforce)
+ data[8] = HI(rsat);
+ data[9] = HI(lsat);
+
+ send_packet(iforce, FF_CMD_INTERACT, data);
+
+ return 0;
+}
+
+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;
+ return 0;
+}
+
+/*
+ * Send the part common to all effects to the device
+ */
+
+static int make_core(struct iforce* iforce, u16 id, u16 mod_id1, u16 mod_id2,
+ u8 effect_type, u8 axes, u16 duration, u16 delay, u16 button,
+ u16 interval, u16 direction)
+{
+ unsigned char data[14];
+
+ duration = TIME_SCALE(duration);
+ delay = TIME_SCALE(delay);
+ interval = TIME_SCALE(interval);
+
+ data[0] = LO(id);
+ data[1] = effect_type;
+ data[2] = LO(axes) | find_button(iforce, button);
+
+ data[3] = LO(duration);
+ data[4] = HI(duration);
+
+ data[5] = HI(direction);
+
+ data[6] = LO(interval);
+ data[7] = HI(interval);
- iforce->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_ABS);
- iforce->dev.keybit[LONG(BTN_JOYSTICK)] |= BIT(BTN_TRIGGER) | BIT(BTN_TOP) | BIT(BTN_THUMB) | BIT(BTN_TOP2) |
- BIT(BTN_BASE) | BIT(BTN_BASE2) | BIT(BTN_BASE3) | BIT(BTN_BASE4) | BIT(BTN_BASE5);
- iforce->dev.keybit[LONG(BTN_GAMEPAD)] |= BIT(BTN_A) | BIT(BTN_B) | BIT(BTN_C);
- iforce->dev.absbit[0] = BIT(ABS_X) | BIT(ABS_Y) | BIT(ABS_THROTTLE) | BIT(ABS_HAT0X) | BIT(ABS_HAT0Y)
- | BIT(ABS_WHEEL) | BIT(ABS_GAS) | BIT(ABS_BRAKE);
-
- for (i = ABS_X; i <= ABS_Y; i++) {
- iforce->dev.absmax[i] = 1920;
- iforce->dev.absmin[i] = -1920;
- iforce->dev.absflat[i] = 128;
- iforce->dev.absfuzz[i] = 16;
- }
-
- for (i = ABS_THROTTLE; i <= ABS_RUDDER; i++) {
- iforce->dev.absmax[i] = 255;
- iforce->dev.absmin[i] = 0;
- }
-
- for (i = ABS_HAT0X; i <= ABS_HAT0Y; i++) {
- iforce->dev.absmax[i] = 1;
- iforce->dev.absmin[i] = -1;
+ data[8] = LO(mod_id1);
+ data[9] = HI(mod_id1);
+ data[10] = LO(mod_id2);
+ data[11] = HI(mod_id2);
+
+ data[12] = LO(delay);
+ data[13] = HI(delay);
+
+ send_packet(iforce, FF_CMD_EFFECT, data);
+
+ return 0;
+}
+
+/*
+ * Upload a periodic effect to the device
+ */
+
+static int iforce_upload_periodic(struct iforce* iforce, struct ff_effect* effect)
+{
+ u8 wave_code;
+ int core_id = effect->id;
+ struct iforce_core_effect* core_effect = iforce->core_effects + core_id;
+ struct resource* mod1_chunk = &(iforce->core_effects[core_id].mod1_chunk);
+ struct resource* mod2_chunk = &(iforce->core_effects[core_id].mod2_chunk);
+ int err = 0;
+
+ err = make_period_modifier(iforce, mod1_chunk,
+ effect->u.periodic.magnitude, effect->u.periodic.offset,
+ effect->u.periodic.period, effect->u.periodic.phase);
+ if (err) return err;
+ set_bit(FF_MOD1_IS_USED, core_effect->flags);
+
+ err = make_shape_modifier(iforce, mod2_chunk,
+ effect->u.periodic.shape.attack_length,
+ effect->u.periodic.shape.attack_level,
+ effect->u.periodic.shape.fade_length,
+ effect->u.periodic.shape.fade_level);
+ if (err) return err;
+ set_bit(FF_MOD2_IS_USED, core_effect->flags);
+
+ switch (effect->u.periodic.waveform) {
+ case FF_SQUARE: wave_code = 0x20; break;
+ case FF_TRIANGLE: wave_code = 0x21; break;
+ case FF_SINE: wave_code = 0x22; break;
+ case FF_SAW_UP: wave_code = 0x23; break;
+ case FF_SAW_DOWN: wave_code = 0x24; break;
+ default: wave_code = 0x20; break;
}
- iforce->dev.private = iforce;
+ err = make_core(iforce, effect->id,
+ mod1_chunk->start,
+ mod2_chunk->start,
+ wave_code,
+ 0x20,
+ effect->replay.length,
+ effect->replay.delay,
+ effect->trigger.button,
+ effect->trigger.interval,
+ effect->u.periodic.direction);
-#ifdef IFORCE_USB
+ return err;
+}
+
+/*
+ * Upload a constant force effect
+ */
+static int iforce_upload_constant(struct iforce* iforce, struct ff_effect* effect)
+{
+ int core_id = effect->id;
+ struct iforce_core_effect* core_effect = iforce->core_effects + core_id;
+ struct resource* mod1_chunk = &(iforce->core_effects[core_id].mod1_chunk);
+ struct resource* mod2_chunk = &(iforce->core_effects[core_id].mod2_chunk);
+ int err = 0;
+
+ printk(KERN_DEBUG "iforce.c: make constant effect\n");
+
+ err = make_magnitude_modifier(iforce, mod1_chunk, effect->u.constant.level);
+ if (err) return err;
+ set_bit(FF_MOD1_IS_USED, core_effect->flags);
+
+ err = make_shape_modifier(iforce, mod2_chunk,
+ effect->u.constant.shape.attack_length,
+ effect->u.constant.shape.attack_level,
+ effect->u.constant.shape.fade_length,
+ effect->u.constant.shape.fade_level);
+ if (err) return err;
+ set_bit(FF_MOD2_IS_USED, core_effect->flags);
+
+ err = make_core(iforce, effect->id,
+ mod1_chunk->start,
+ mod2_chunk->start,
+ 0x00,
+ 0x20,
+ effect->replay.length,
+ effect->replay.delay,
+ effect->trigger.button,
+ effect->trigger.interval,
+ effect->u.constant.direction);
+
+ return err;
+}
+
+/*
+ * Upload an interactive effect. Those are for example friction, inertia, springs...
+ */
+static int iforce_upload_interactive(struct iforce* iforce, struct ff_effect* effect)
+{
+ int core_id = effect->id;
+ struct iforce_core_effect* core_effect = iforce->core_effects + core_id;
+ struct resource* mod_chunk = &(core_effect->mod1_chunk);
+ u8 type, axes;
+ u16 mod1, mod2, direction;
+ int err = 0;
+
+ printk(KERN_DEBUG "iforce.c: make interactive effect\n");
+
+ switch (effect->type) {
+ case FF_SPRING: type = 0x40; break;
+ case FF_FRICTION: type = 0x41; break;
+ default: return -1;
+ }
+
+ err = make_interactive_modifier(iforce, mod_chunk,
+ effect->u.interactive.right_saturation,
+ effect->u.interactive.left_saturation,
+ effect->u.interactive.right_coeff,
+ effect->u.interactive.left_coeff,
+ effect->u.interactive.deadband,
+ effect->u.interactive.center);
+ if (err) return err;
+ set_bit(FF_MOD1_IS_USED, core_effect->flags);
+
+ switch ((test_bit(ABS_X, &effect->u.interactive.axis) ||
+ test_bit(ABS_WHEEL, &effect->u.interactive.axis)) |
+ (!!test_bit(ABS_Y, &effect->u.interactive.axis) << 1)) {
+
+ case 0: /* Only one axis, choose orientation */
+ mod1 = mod_chunk->start;
+ mod2 = 0xffff;
+ direction = effect->u.interactive.direction;
+ axes = 0x20;
+ break;
+
+ case 1: /* Only X axis */
+ mod1 = mod_chunk->start;
+ mod2 = 0xffff;
+ direction = 0x5a00;
+ axes = 0x40;
+ break;
+
+ case 2: /* Only Y axis */
+ mod1 = 0xffff;
+ mod2 = mod_chunk->start;
+ direction = 0xb400;
+ axes = 0x80;
+ break;
+
+ case 3: /* Both X and Y axes */
+ /* TODO: same setting for both axes is not mandatory */
+ mod1 = mod_chunk->start;
+ mod2 = mod_chunk->start;
+ direction = 0x6000;
+ axes = 0xc0;
+ break;
+
+ default:
+ return -1;
+ }
+
+ err = make_core(iforce, effect->id,
+ mod1, mod2,
+ type, axes,
+ effect->replay.length, effect->replay.delay,
+ effect->trigger.button, effect->trigger.interval,
+ direction);
+
+ return err;
+}
+
+/*
+ * 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)
+{
+ struct iforce* iforce = (struct iforce*)(dev->private);
+ int id;
+
+ printk(KERN_DEBUG "iforce.c: upload effect\n");
+
+/*
+ * Get a free id
+ */
+
+ for (id=0; id < FF_EFFECTS_MAX; ++id)
+ if (!test_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;
+ set_bit(FF_CORE_IS_USED, iforce->core_effects[id].flags);
+
+/*
+ * Upload the effect
+ */
+
+ switch (effect->type) {
+
+ case FF_PERIODIC:
+ return iforce_upload_periodic(iforce, effect);
+
+ case FF_CONSTANT:
+ return iforce_upload_constant(iforce, effect);
+
+ case FF_SPRING:
+ case FF_FRICTION:
+ return iforce_upload_interactive(iforce, effect);
+
+ default:
+ return -1;
+ }
+}
+
+/*
+ * Erases an effect: it frees the effect id and mark as unused the memory
+ * allocated for the parameters
+ */
+static int iforce_erase_effect(struct input_dev *dev, int effect_id)
+{
+ struct iforce* iforce = (struct iforce*)(dev->private);
+ int err = 0;
+ struct iforce_core_effect* core_effect;
+
+ printk(KERN_DEBUG "iforce.c: erase effect %d\n", effect_id);
+
+ if (effect_id < 0 || effect_id >= FF_EFFECTS_MAX)
+ return -EINVAL;
+
+ core_effect = iforce->core_effects + effect_id;
+
+ if (test_bit(FF_MOD1_IS_USED, core_effect->flags))
+ err = release_resource(&(iforce->core_effects[effect_id].mod1_chunk));
+
+ if (!err && test_bit(FF_MOD2_IS_USED, core_effect->flags))
+ err = release_resource(&(iforce->core_effects[effect_id].mod2_chunk));
+
+ /*TODO: remember to change that if more FF_MOD* bits are added */
+ core_effect->flags[0] = 0;
+
+ return err;
+}
+static int iforce_init_device(struct iforce *iforce)
+{
+ unsigned char c[] = "CEOV";
+ int i;
+
+ init_waitqueue_head(&iforce->wait);
+ iforce->dev.ff_effects_max = 10;
+
+/*
+ * Input device fields.
+ */
+
+ iforce->dev.idbus = BUS_USB;
+ iforce->dev.private = iforce;
+ iforce->dev.name = iforce->name;
iforce->dev.open = iforce_open;
iforce->dev.close = iforce_close;
-#endif
+ iforce->dev.event = iforce_input_event;
+ iforce->dev.upload_effect = iforce_upload_effect;
+ iforce->dev.erase_effect = iforce_erase_effect;
+
+/*
+ * On-device memory allocation.
+ */
+
+ iforce->device_memory.name = "I-Force device effect memory";
+ iforce->device_memory.start = 0;
+ iforce->device_memory.end = 200;
+ iforce->device_memory.flags = IORESOURCE_MEM;
+ iforce->device_memory.parent = NULL;
+ iforce->device_memory.child = NULL;
+ iforce->device_memory.sibling = NULL;
+
+/*
+ * Wait until device ready - until it sends its first response.
+ */
+
+ for (i = 0; i < 20; i++)
+ if (!get_id_packet(iforce, "O"))
+ break;
+
+ if (i == 20) { /* 5 seconds */
+ printk(KERN_ERR "iforce.c: Timeout waiting for response from device.\n");
+ iforce_close(&iforce->dev);
+ return -1;
+ }
+
+/*
+ * Get device info.
+ */
+
+ if (!get_id_packet(iforce, "M"))
+ iforce->dev.idvendor = (iforce->edata[2] << 8) | iforce->edata[1];
+ if (!get_id_packet(iforce, "P"))
+ iforce->dev.idproduct = (iforce->edata[2] << 8) | iforce->edata[1];
+ if (!get_id_packet(iforce, "B"))
+ iforce->device_memory.end = (iforce->edata[2] << 8) | iforce->edata[1];
+ if (!get_id_packet(iforce, "N"))
+ iforce->dev.ff_effects_max = iforce->edata[1];
+
+/*
+ * Display additional info.
+ */
+
+ for (i = 0; c[i]; i++)
+ if (!get_id_packet(iforce, c + i))
+ dump_packet("info", iforce->ecmd, iforce->edata);
+
+/*
+ * Disable spring, enable force feedback.
+ * FIXME: We should use iforce_set_autocenter() et al here.
+ */
+
+ send_packet(iforce, FF_CMD_AUTOCENTER, "\004\000");
+ send_packet(iforce, FF_CMD_ENABLE, "\004");
+
+/*
+ * Find appropriate device entry
+ */
+
+ for (i = 0; iforce_device[i].idvendor; i++)
+ if (iforce_device[i].idvendor == iforce->dev.idvendor &&
+ iforce_device[i].idproduct == iforce->dev.idproduct)
+ break;
+
+ iforce->type = iforce_device + i;
+
+ sprintf(iforce->name, iforce->type->name,
+ iforce->dev.idproduct, iforce->dev.idvendor);
+
+/*
+ * Set input device bitfields and ranges.
+ */
+
+ iforce->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_ABS) | BIT(EV_FF);
+
+ for (i = 0; iforce->type->btn[i] >= 0; i++) {
+ signed short t = iforce->type->btn[i];
+ set_bit(t, iforce->dev.keybit);
+ if (t != BTN_DEAD)
+ set_bit(FF_BTN(t), iforce->dev.ffbit);
+ }
+
+ for (i = 0; iforce->type->abs[i] >= 0; i++) {
+
+ signed short t = iforce->type->abs[i];
+ set_bit(t, iforce->dev.absbit);
+
+ switch (t) {
+
+ case ABS_X:
+ case ABS_Y:
+ case ABS_WHEEL:
+
+ iforce->dev.absmax[t] = 1920;
+ iforce->dev.absmin[t] = -1920;
+ iforce->dev.absflat[t] = 128;
+ iforce->dev.absfuzz[t] = 16;
+
+ set_bit(FF_ABS(t), iforce->dev.ffbit);
+ break;
+
+ case ABS_THROTTLE:
+ case ABS_GAS:
+ case ABS_BRAKE:
+
+ iforce->dev.absmax[t] = 255;
+ iforce->dev.absmin[t] = 0;
+ break;
+
+ case ABS_HAT0X:
+ case ABS_HAT0Y:
+ iforce->dev.absmax[t] = 1;
+ iforce->dev.absmin[t] = -1;
+ break;
+ }
+ }
+
+ for (i = 0; iforce->type->ff[i] >= 0; i++)
+ set_bit(iforce->type->ff[i], iforce->dev.ffbit);
+
+/*
+ * Register input device.
+ */
input_register_device(&iforce->dev);
+
+ return 0;
}
#ifdef IFORCE_USB
@@ -178,33 +991,63 @@
{
struct iforce *iforce = urb->context;
if (urb->status) return;
- iforce_process_packet(&iforce->dev, iforce->data[0], 8, iforce->data + 1);
+ iforce_process_packet(iforce,
+ (iforce->data[0] << 8) | (urb->actual_length - 1), iforce->data + 1);
+}
+
+static void iforce_usb_out(struct urb *urb)
+{
+ struct iforce *iforce = urb->context;
+ if (urb->status) return;
+ if (waitqueue_active(&iforce->wait))
+ wake_up(&iforce->wait);
+}
+
+static void iforce_usb_ctrl(struct urb *urb)
+{
+ struct iforce *iforce = urb->context;
+ if (urb->status) return;
+ iforce->ecmd = 0xff00 | urb->actual_length;
+ if (waitqueue_active(&iforce->wait))
+ wake_up(&iforce->wait);
}
static void *iforce_usb_probe(struct usb_device *dev, unsigned int ifnum,
- const struct usb_device_id *id)
+ const struct usb_device_id *id)
{
- struct usb_endpoint_descriptor *endpoint;
+ struct usb_endpoint_descriptor *epirq, *epout;
struct iforce *iforce;
- endpoint = dev->config[0].interface[ifnum].altsetting[0].endpoint + 0;
+ epirq = dev->config[0].interface[ifnum].altsetting[0].endpoint + 0;
+ epout = dev->config[0].interface[ifnum].altsetting[0].endpoint + 1;
- if (!(iforce = kmalloc(sizeof(struct iforce), GFP_KERNEL))) return NULL;
+ if (!(iforce = kmalloc(sizeof(struct iforce) + 32, GFP_KERNEL))) return NULL;
memset(iforce, 0, sizeof(struct iforce));
- iforce->dev.name = iforce_name;
- iforce->dev.idbus = BUS_USB;
- iforce->dev.idvendor = dev->descriptor.idVendor;
- iforce->dev.idproduct = dev->descriptor.idProduct;
- iforce->dev.idversion = dev->descriptor.bcdDevice;
+ iforce->bus = IFORCE_USB;
+ iforce->usbdev = dev;
+
+ iforce->dr.requesttype = USB_TYPE_VENDOR | USB_DIR_IN | USB_RECIP_INTERFACE;
+ iforce->dr.index = 0;
+ iforce->dr.length = 16;
+
+ FILL_INT_URB(&iforce->irq, dev, usb_rcvintpipe(dev, epirq->bEndpointAddress),
+ iforce->data, 16, iforce_usb_irq, iforce, epirq->bInterval);
- FILL_INT_URB(&iforce->irq, dev, usb_rcvintpipe(dev, endpoint->bEndpointAddress),
- iforce->data, 8, iforce_usb_irq, iforce, endpoint->bInterval);
+ FILL_BULK_URB(&iforce->out, dev, usb_sndbulkpipe(dev, epout->bEndpointAddress),
+ iforce + 1, 32, iforce_usb_out, iforce);
- iforce_input_setup(iforce);
+ FILL_CONTROL_URB(&iforce->ctrl, dev, usb_rcvctrlpipe(dev, 0),
+ (void*) &iforce->dr, iforce->edata, 16, iforce_usb_ctrl, iforce);
- printk(KERN_INFO "input%d: %s on usb%d:%d.%d\n",
- iforce->dev.number, iforce_name, dev->bus->busnum, dev->devnum, ifnum);
+ if (iforce_init_device(iforce)) {
+ kfree(iforce);
+ return NULL;
+ }
+
+ printk(KERN_INFO "input%d: %s [%d effects, %ld bytes memory] on usb%d:%d.%d\n",
+ iforce->dev.number, iforce->dev.name, iforce->dev.ff_effects_max,
+ iforce->device_memory.end, dev->bus->busnum, dev->devnum, ifnum);
return iforce;
}
@@ -218,8 +1061,12 @@
}
static struct usb_device_id iforce_usb_ids [] = {
- { USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WMFORCE) },
- { } /* Terminating entry */
+ { USB_DEVICE(0x046d, 0xc281) }, /* Logitech WingMan Force */
+ { USB_DEVICE(0x046d, 0xc291) }, /* Logitech WingMan Formula Force */
+ { USB_DEVICE(0x05ef, 0x020a) }, /* AVB Top Shot Pegasus */
+ { USB_DEVICE(0x05ef, 0x8884) }, /* AVB Mag Turbo Force */
+ { USB_DEVICE(0x06f8, 0x0001) }, /* Guillemot Race Leader Force Feedback */
+ { } /* Terminating entry */
};
MODULE_DEVICE_TABLE (usb, iforce_usb_ids);
@@ -237,7 +1084,7 @@
static void iforce_serio_irq(struct serio *serio, unsigned char data, unsigned int flags)
{
- struct iforce* iforce = serio->private;
+ struct iforce* iforce = serio->private;
if (!iforce->pkt) {
if (data != 0x2b) {
@@ -247,14 +1094,14 @@
return;
}
- if (!iforce->id) {
- if (data > 3) {
+ if (!iforce->id) {
+ if (data > 3 && data != 0xff) {
iforce->pkt = 0;
return;
}
- iforce->id = data;
+ iforce->id = data;
return;
- }
+ }
if (!iforce->len) {
if (data > IFORCE_MAX_LENGTH) {
@@ -266,37 +1113,32 @@
return;
}
- if (iforce->idx < iforce->len) {
- iforce->csum += iforce->data[iforce->idx++] = data;
+ if (iforce->idx < iforce->len) {
+ iforce->csum += iforce->data[iforce->idx++] = data;
return;
}
- if (iforce->idx == iforce->len) {
- iforce_process_packet(&iforce->dev, iforce->id, iforce->idx, iforce->data);
+ if (iforce->idx == iforce->len) {
+ iforce_process_packet(iforce, (iforce->id << 8) | iforce->idx, iforce->data);
iforce->pkt = 0;
iforce->id = 0;
- iforce->len = 0;
- iforce->idx = 0;
+ iforce->len = 0;
+ iforce->idx = 0;
iforce->csum = 0;
- }
+ }
}
static void iforce_serio_connect(struct serio *serio, struct serio_dev *dev)
{
struct iforce *iforce;
-
if (serio->type != (SERIO_RS232 | SERIO_IFORCE))
return;
if (!(iforce = kmalloc(sizeof(struct iforce), GFP_KERNEL))) return;
memset(iforce, 0, sizeof(struct iforce));
- iforce->dev.name = iforce_name;
- iforce->dev.idbus = BUS_RS232;
- iforce->dev.idvendor = SERIO_IFORCE;
- iforce->dev.idproduct = 0x0001;
- iforce->dev.idversion = 0x0100;
-
+ iforce->bus = IFORCE_232;
+ iforce->serio = serio;
serio->private = iforce;
if (serio_open(serio, dev)) {
@@ -304,15 +1146,21 @@
return;
}
- iforce_input_setup(iforce);
+ if (iforce_init_device(iforce)) {
+ serio_close(serio);
+ kfree(iforce);
+ return;
+ }
- printk(KERN_INFO "input%d: %s on serio%d\n",
- iforce->dev.number, iforce_name, serio->number);
+ printk(KERN_INFO "input%d: %s [%d effects, %ld bytes memory] on serio%d\n",
+ iforce->dev.number, iforce->dev.name, iforce->dev.ff_effects_max,
+ iforce->device_memory.end, serio->number);
}
static void iforce_serio_disconnect(struct serio *serio)
{
struct iforce* iforce = serio->private;
+
input_unregister_device(&iforce->dev);
serio_close(serio);
kfree(iforce);
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)