iforce.c
上传用户:jlfgdled
上传日期:2013-04-10
资源大小:33168k
文件大小:29k
- /*
- * $Id: iforce.c,v 1.56 2001/05/27 14:41:26 jdeneux Exp $
- *
- * Copyright (c) 2000-2001 Vojtech Pavlik <vojtech@suse.cz>
- * Copyright (c) 2001 Johann Deneux <deneux@ifrance.com>
- *
- * USB/RS232 I-Force joysticks and wheels.
- *
- * Sponsored by SuSE
- */
- /*
- * 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 <vojtech@suse.cz>, or by paper mail:
- * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic
- */
- #include <linux/kernel.h>
- #include <linux/slab.h>
- #include <linux/input.h>
- #include <linux/module.h>
- #include <linux/init.h>
- #include <linux/spinlock.h>
- #include <linux/usb.h>
- #include <linux/serio.h>
- #include <linux/config.h>
- /* 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>
- 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
- #if defined(CONFIG_INPUT_IFORCE_232) || defined(CONFIG_INPUT_IFORCE_232_MODULE)
- #define IFORCE_232 1
- #endif
- #if defined(CONFIG_INPUT_IFORCE_USB) || defined(CONFIG_INPUT_IFORCE_USB_MODULE)
- #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 {
- 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;
- struct usb_ctrlrequest 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;
- } iforce_hat_to_axis[16] = {{ 0,-1}, { 1,-1}, { 1, 0}, { 1, 1}, { 0, 1}, {-1, 1}, {-1, 0}, {-1,-1}};
- /* 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 dump_packet(char *msg, u16 cmd, unsigned char *data)
- {
- int i;
- printk(KERN_DEBUG "iforce.c: %s ( cmd = %04x, data = ", msg, cmd);
- for (i = 0; i < LO(cmd); i++)
- printk("%02x ", data[i]);
- printk(")n");
- }
- /*
- * 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]);
- } else {
- input_report_abs(dev, ABS_WHEEL, (__s16) (((__s16)data[1] << 8) | data[0]));
- input_report_abs(dev, ABS_GAS, 255 - data[2]);
- input_report_abs(dev, ABS_BRAKE, 255 - data[3]);
- }
- 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);
- 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 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.bRequest = 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;
- 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;
- }
- static void iforce_close(struct input_dev *dev)
- {
- struct iforce *iforce = dev->private;
- switch (iforce->bus) {
- #ifdef IFORCE_USB
- case IFORCE_USB:
- if (!--iforce->open)
- usb_unlink_urb(&iforce->irq);
- break;
- #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);
- 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);
- 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;
- }
- 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);
- 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 effectn");
- 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 effectn");
- 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 effectn");
- /*
- * 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 %dn", 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;
- 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, "