From: johann d. <jd...@us...> - 2001-10-07 17:06:52
|
Update of /cvsroot/linuxconsole/ruby/linux/drivers/input In directory usw-pr-cvs1:/tmp/cvs-serv6680 Modified Files: Tag: iforce-split Makefile Added Files: Tag: iforce-split iforce-ff.c iforce-main.c iforce-packets.c iforce-serio.c iforce-usb.c iforce.h Removed Files: Tag: iforce-split iforce.c Log Message: Split iforce.c into several smaller files. --- NEW FILE: iforce-ff.c --- /* * $Id: iforce-ff.c,v 1.1.2.1 2001/10/07 17:06:49 jdeneux Exp $ * * Copyright (c) 2000-2001 Vojtech Pavlik <vo...@uc...> * Copyright (c) 2001 Johann Deneux <de...@if...> * * USB/RS232 I-Force joysticks and wheels. */ /* * 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 <vo...@uc...>, or by paper mail: * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic */ #include "iforce.h" /* * 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, int no_alloc, __s16 level) { unsigned char data[3]; if (!no_alloc) { down(&iforce->mem_mutex); if (allocate_resource(&(iforce->device_memory), mod_chunk, 2, iforce->device_memory.start, iforce->device_memory.end, 2L, NULL, NULL)) { up(&iforce->mem_mutex); return -ENOMEM; } up(&iforce->mem_mutex); } data[0] = LO(mod_chunk->start); data[1] = HI(mod_chunk->start); data[2] = HIFIX80(level); iforce_send_packet(iforce, FF_CMD_MAGNITUDE, data); iforce_dump_packet("magnitude: ", 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, int no_alloc, __s16 magnitude, __s16 offset, u16 period, u16 phase) { unsigned char data[7]; period = TIME_SCALE(period); if (!no_alloc) { down(&iforce->mem_mutex); if (allocate_resource(&(iforce->device_memory), mod_chunk, 0x0c, iforce->device_memory.start, iforce->device_memory.end, 2L, NULL, NULL)) { up(&iforce->mem_mutex); return -ENOMEM; } up(&iforce->mem_mutex); } data[0] = LO(mod_chunk->start); data[1] = HI(mod_chunk->start); data[2] = HIFIX80(magnitude); data[3] = HIFIX80(offset); data[4] = HI(phase); data[5] = LO(period); data[6] = HI(period); iforce_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, int no_alloc, 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 (!no_alloc) { down(&iforce->mem_mutex); if (allocate_resource(&(iforce->device_memory), mod_chunk, 0x0e, iforce->device_memory.start, iforce->device_memory.end, 2L, NULL, NULL)) { up(&iforce->mem_mutex); return -ENOMEM; } up(&iforce->mem_mutex); } data[0] = LO(mod_chunk->start); data[1] = HI(mod_chunk->start); data[2] = LO(attack_duration); data[3] = HI(attack_duration); data[4] = HIFIX80(initial_level); data[5] = LO(fade_duration); data[6] = HI(fade_duration); data[7] = HIFIX80(final_level); iforce_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, int no_alloc, __s16 rsat, __s16 lsat, __s16 rk, __s16 lk, u16 db, __s16 center) { unsigned char data[10]; if (!no_alloc) { down(&iforce->mem_mutex); if (allocate_resource(&(iforce->device_memory), mod_chunk, 8, iforce->device_memory.start, iforce->device_memory.end, 2L, NULL, NULL)) { up(&iforce->mem_mutex); return -ENOMEM; } up(&iforce->mem_mutex); } data[0] = LO(mod_chunk->start); data[1] = HI(mod_chunk->start); data[2] = HIFIX80(rk); data[3] = HIFIX80(lk); data[4] = LO(center); data[5] = HI(center); data[6] = LO(db); data[7] = HI(db); data[8] = HIFIX80(rsat); data[9] = HIFIX80(lsat); iforce_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; } /* * Analyse the changes in an effect, and tell if we need to send an interactive * parameter packet */ static int need_interactive_modifier(struct iforce* iforce, struct ff_effect* new) { int id = new->id; struct ff_effect* old = &iforce->core_effects[id].effect; if (new->type != FF_SPRING && new->type != FF_FRICTION) { printk(KERN_WARNING "iforce.c: bad effect type in need_interactive_modifier\n"); return FALSE; } return (old->u.interactive.right_saturation != new->u.interactive.right_saturation || old->u.interactive.left_saturation != new->u.interactive.left_saturation || old->u.interactive.right_coeff != new->u.interactive.right_coeff || old->u.interactive.left_coeff != new->u.interactive.left_coeff || old->u.interactive.deadband != new->u.interactive.deadband || old->u.interactive.center != new->u.interactive.center); } /* * 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) { 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_shape_modifier\n"); return FALSE; } return (old->u.constant.level != effect->u.constant.level); } /* * Analyse the changes in an effect, and tell if we need to send a shape * parameter packet */ static int need_shape_modifier(struct iforce* iforce, 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.shape.attack_length != effect->u.constant.shape.attack_length || old->u.constant.shape.attack_level != effect->u.constant.shape.attack_level || old->u.constant.shape.fade_length != effect->u.constant.shape.fade_length || old->u.constant.shape.fade_level != effect->u.constant.shape.fade_level) return TRUE; break; case FF_PERIODIC: if (old->u.periodic.shape.attack_length != effect->u.periodic.shape.attack_length || old->u.periodic.shape.attack_level != effect->u.periodic.shape.attack_level || old->u.periodic.shape.fade_length != effect->u.periodic.shape.fade_length || old->u.periodic.shape.fade_level != effect->u.periodic.shape.fade_level) return TRUE; break; default: printk(KERN_WARNING "iforce.c: bad effect type in need_shape_modifier\n"); } return FALSE; } /* * 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) { 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; } 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 || old->u.periodic.phase != new->u.periodic.phase); } /* * 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) { 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 FALSE; } /* * 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); iforce_send_packet(iforce, FF_CMD_EFFECT, data); return 0; } /* * 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) { 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 = 1; if (!is_update || need_period_modifier(iforce, effect)) { err = make_period_modifier(iforce, mod1_chunk, is_update, 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); } if (!is_update || need_shape_modifier(iforce, effect)) { err = make_shape_modifier(iforce, mod2_chunk, is_update, 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; } if (!is_update || need_core(iforce, effect)) { 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->direction); } else { printk(KERN_DEBUG "iforce.c: no effect packet was needed\n"); } return err; } /* * Upload a constant force effect * Return value: * <0 Error code * 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 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 ret = 1; printk(KERN_DEBUG "iforce.c: make constant effect\n"); if (!is_update || need_magnitude_modifier(iforce, effect)) { ret = make_magnitude_modifier(iforce, mod1_chunk, is_update, effect->u.constant.level); if (ret) return ret; set_bit(FF_MOD1_IS_USED, core_effect->flags); } if (!is_update || need_shape_modifier(iforce, effect)) { ret = make_shape_modifier(iforce, mod2_chunk, is_update, 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 (ret) return ret; set_bit(FF_MOD2_IS_USED, core_effect->flags); } if (!is_update || need_core(iforce, effect)) { ret = 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->direction); } else { printk(KERN_DEBUG "iforce.c: no effect packet was needed\n"); } return ret; } /* * Upload an interactive effect. Those are for example friction, inertia, springs... */ int iforce_upload_interactive(struct iforce* iforce, struct ff_effect* effect, int is_update) { 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 = 1; 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; } if (!is_update || need_interactive_modifier(iforce, effect)) { err = make_interactive_modifier(iforce, mod_chunk, is_update, 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->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; } if (!is_update || need_core(iforce, effect)) { 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; } --- NEW FILE: iforce-main.c --- /* * $Id: iforce-main.c,v 1.1.2.1 2001/10/07 17:06:49 jdeneux Exp $ * * Copyright (c) 2000-2001 Vojtech Pavlik <vo...@uc...> * Copyright (c) 2001 Johann Deneux <de...@if...> * * USB/RS232 I-Force joysticks and wheels. */ /* * 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 <vo...@uc...>, or by paper mail: * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic */ #include "iforce.h" #include "usbpath.h" MODULE_AUTHOR("Vojtech Pavlik <vo...@uc...>, Johann Deneux <de...@if...>"); MODULE_DESCRIPTION("USB/RS232 I-Force joysticks and wheels driver"); MODULE_LICENSE("GPL"); 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 btn_avb_tw[] = { BTN_TRIGGER, BTN_THUMB, BTN_TOP, BTN_TOP2, BTN_BASE, BTN_BASE2, BTN_BASE3, BTN_BASE4, -1 }; static signed short abs_joystick[] = { ABS_X, ABS_Y, ABS_THROTTLE, ABS_HAT0X, ABS_HAT0Y, -1 }; static signed short abs_joystick2[] = { ABS_X, ABS_Y, ABS_THROTTLE, ABS_RUDDER, 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 iforce_device[] = { { 0x044f, 0xa01c, "Thrustmaster Motor Sport GT", btn_wheel, abs_wheel, ff_iforce }, { 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_joystick2, ff_iforce }, { 0x05ef, 0x8884, "AVB Mag Turbo Force", btn_wheel, abs_wheel, ff_iforce }, { 0x05ef, 0x8888, "AVB Top Shot Force Feedback Racing Wheel", btn_avb_tw, 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 } }; 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; iforce_send_packet(iforce, FF_CMD_GAIN, data); return 0; case FF_AUTOCENTER: data[0] = 0x03; data[1] = value >> 9; iforce_send_packet(iforce, FF_CMD_AUTOCENTER, data); data[0] = 0x04; data[1] = 0x01; iforce_send_packet(iforce, FF_CMD_AUTOCENTER, data); return 0; default: /* Play or stop an effect */ if (!CHECK_OWNERSHIP(code, iforce)) { return -1; } if (value > 0) { set_bit(FF_CORE_SHOULD_PLAY, iforce->core_effects[code].flags); } else { clear_bit(FF_CORE_SHOULD_PLAY, iforce->core_effects[code].flags); } data[0] = LO(code); data[1] = (value > 0) ? ((value > 1) ? 0x41 : 0x01) : 0; data[2] = LO(value); iforce_send_packet(iforce, FF_CMD_PLAY, data); return 0; } return -1; } /* * 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; int ret; int is_update; printk(KERN_DEBUG "iforce.c: upload effect\n"); /* * 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; /* Check the effect is not already being updated */ if (test_bit(FF_CORE_UPDATE, iforce->core_effects[effect->id].flags)) { printk(KERN_DEBUG "iforce.c: update too frequent refused\n"); return -EAGAIN; } is_update = TRUE; } /* * Upload the effect */ switch (effect->type) { case FF_PERIODIC: ret = iforce_upload_periodic(iforce, effect, is_update); break; case FF_CONSTANT: ret = iforce_upload_constant(iforce, effect, is_update); break; case FF_SPRING: case FF_FRICTION: ret = iforce_upload_interactive(iforce, effect, is_update); 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); } iforce->core_effects[effect->id].effect = *effect; return ret; } /* * 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; /* Check who is trying to erase this effect */ if (iforce->core_effects[effect_id].owner != current->pid) { printk(KERN_WARNING "iforce.c: %d tried to erase an effect belonging to %d\n", current->pid, iforce->core_effects[effect_id].owner); return -EACCES; } 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_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 } /* Reset device */ iforce_send_packet(iforce, FF_CMD_ENABLE, "\004"); return 0; } static int iforce_flush(struct input_dev *dev, struct file *file) { struct iforce *iforce = dev->private; int i; /* Erase all effects this process owns */ for (i=0; i<dev->ff_effects_max; ++i) { if (test_bit(FF_CORE_IS_USED, iforce->core_effects[i].flags) && current->pid == iforce->core_effects[i].owner) { /* Stop effect */ iforce_input_event(dev, EV_FF, i, 0); /* Free ressources assigned to effect */ if (iforce_erase_effect(dev, i)) { printk(KERN_WARNING "iforce_flush: (%s) erase effect %d failed\n", dev->phys, i); } } } return 0; } static void iforce_close(struct input_dev *dev) { struct iforce *iforce = dev->private; printk(KERN_DEBUG "iforce.c: in iforce_close\n"); /* Disable force feedback playback */ iforce_send_packet(iforce, FF_CMD_ENABLE, "\001"); switch (iforce->bus) { #ifdef IFORCE_USB case IFORCE_USB: if (!--iforce->open) usb_unlink_urb(&iforce->irq); break; #endif } } int iforce_init_device(struct iforce *iforce) { unsigned char c[] = "CEOV"; char path[64]; int i; init_waitqueue_head(&iforce->wait); spin_lock_init(&iforce->xmit_lock); init_MUTEX(&iforce->mem_mutex); iforce->xmit.buf = iforce->xmit_data; iforce->dev.ff_effects_max = 10; switch (iforce->bus) { #ifdef IFORCE_232 case IFORCE_232: strcpy(path, iforce->serio->phys); break; #endif #ifdef IFORCE_USB case IFORCE_USB: usb_make_path(iforce->usbdev, path, 64); break; #endif } sprintf(iforce->phys, "%s/input0", path); /* * Input device fields. */ iforce->dev.idbus = BUS_USB; iforce->dev.private = iforce; iforce->dev.name = iforce->name; iforce->dev.name = iforce->phys; iforce->dev.open = iforce_open; iforce->dev.close = iforce_close; iforce->dev.flush = iforce_flush; 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 (!iforce_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 (!iforce_get_id_packet(iforce, "M")) iforce->dev.idvendor = (iforce->edata[2] << 8) | iforce->edata[1]; if (!iforce_get_id_packet(iforce, "P")) iforce->dev.idproduct = (iforce->edata[2] << 8) | iforce->edata[1]; if (!iforce_get_id_packet(iforce, "B")) iforce->device_memory.end = (iforce->edata[2] << 8) | iforce->edata[1]; if (!iforce_get_id_packet(iforce, "N")) iforce->dev.ff_effects_max = iforce->edata[1]; /* 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.c\n", iforce->dev.ff_effects_max, FF_EFFECTS_MAX); iforce->dev.ff_effects_max = FF_EFFECTS_MAX; } /* * Display additional info. */ for (i = 0; c[i]; i++) if (!iforce_get_id_packet(iforce, c + i)) iforce_dump_packet("info", iforce->ecmd, iforce->edata); /* * Disable spring, enable force feedback. * FIXME: We should use iforce_set_autocenter() et al here. */ iforce_send_packet(iforce, FF_CMD_AUTOCENTER, "\004\000"); /* * 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) | BIT(EV_FF_STATUS); 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_RUDDER: iforce->dev.absmax[t] = 127; iforce->dev.absmin[t] = -128; 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); printk(KERN_INFO "input: %s [%d effects, %ld bytes memory] on %s\n", iforce->dev.name, iforce->dev.ff_effects_max, iforce->device_memory.end, path); return 0; } static int __init iforce_init(void) { #ifdef IFORCE_USB usb_register(&iforce_usb_driver); #endif #ifdef IFORCE_232 serio_register_device(&iforce_serio_dev); #endif return 0; } static void __exit iforce_exit(void) { #ifdef IFORCE_USB usb_deregister(&iforce_usb_driver); #endif #ifdef IFORCE_232 serio_unregister_device(&iforce_serio_dev); #endif } module_init(iforce_init); module_exit(iforce_exit); --- NEW FILE: iforce-packets.c --- /* * $Id: iforce-packets.c,v 1.1.2.1 2001/10/07 17:06:49 jdeneux Exp $ * * Copyright (c) 2000-2001 Vojtech Pavlik <vo...@uc...> * Copyright (c) 2001 Johann Deneux <de...@if...> * * USB/RS232 I-Force joysticks and wheels. */ /* * 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 <vo...@uc...>, or by paper mail: * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic */ #include "iforce.h" 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}}; void iforce_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 */ int iforce_send_packet(struct iforce *iforce, u16 cmd, unsigned char* data) { /* Copy data to buffer */ int n = LO(cmd); int c; int empty; int head, tail; unsigned long flags; /* * Update head and tail of xmit buffer */ spin_lock_irqsave(&iforce->xmit_lock, flags); head = iforce->xmit.head; tail = iforce->xmit.tail; if (CIRC_SPACE(head, tail, XMIT_SIZE) < n+2) { printk(KERN_WARNING "iforce.c: not enough space in xmit buffer to send new packet\n"); spin_unlock_irqrestore(&iforce->xmit_lock, flags); return -1; } empty = head == tail; XMIT_INC(iforce->xmit.head, n+2); /* * Store packet in xmit buffer */ iforce->xmit.buf[head] = HI(cmd); XMIT_INC(head, 1); iforce->xmit.buf[head] = LO(cmd); XMIT_INC(head, 1); c = CIRC_SPACE_TO_END(head, tail, XMIT_SIZE); if (n < c) c=n; memcpy(&iforce->xmit.buf[head], data, c); if (n != c) { memcpy(&iforce->xmit.buf[0], data + c, n - c); } XMIT_INC(head, n); spin_unlock_irqrestore(&iforce->xmit_lock, flags); /* * If necessary, start the transmission */ switch (iforce->bus) { #ifdef IFORCE_232 case IFORCE_232: if (empty) iforce_serial_xmit(iforce); break; #endif #ifdef IFORCE_USB case IFORCE_USB: if (empty & !iforce->out.status) { iforce_usb_xmit(iforce); } break; #endif } return 0; } /* Mark an effect that was being updated as ready. That means it can be updated * again */ 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 (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)) { clear_bit(FF_CORE_UPDATE, iforce->core_effects[i].flags); printk(KERN_DEBUG "iforce.c: marked effect %d as ready\n", i); return 0; } } printk(KERN_DEBUG "iforce.c: unused effect %04x updated !!!\n", addr); return -1; } void iforce_process_packet(struct iforce *iforce, u16 cmd, unsigned char *data) { struct input_dev *dev = &iforce->dev; int i; static int being_used = 0; if (being_used) printk(KERN_WARNING "iforce.c: re-entrant call to iforce_process %d\n", being_used); being_used++; #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) { being_used--; 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]); if (LO(cmd) >= 8 && test_bit(ABS_RUDDER ,dev->absbit)) input_report_abs(dev, ABS_RUDDER, (__s8)data[7]); } 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); /* Check if an effect was just started or stopped */ 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); printk(KERN_DEBUG "iforce.c: effect %d started to play\n", i); } } else { if (!test_bit(FF_CORE_SHOULD_PLAY, iforce->core_effects[i].flags)) { 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); printk(KERN_DEBUG "iforce.c: effect %d stopped to play\n", i); } } else { printk(KERN_WARNING "iforce.c: effect %d stopped, while it should not\nStarting again\n", i); } } if (LO(cmd) > 3) { int j; for (j=3; j<LO(cmd); j+=2) { if (mark_core_as_ready(iforce, data[j] | (data[j+1]<<8))) iforce_dump_packet("ff status", cmd, data); } } break; } being_used--; } int iforce_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; iforce_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]); } --- NEW FILE: iforce-serio.c --- /* * $Id: iforce-serio.c,v 1.1.2.1 2001/10/07 17:06:49 jdeneux Exp $ * * Copyright (c) 2000-2001 Vojtech Pavlik <vo...@uc...> * Copyright (c) 2001 Johann Deneux <de...@if...> * * USB/RS232 I-Force joysticks and wheels. */ /* * 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 <vo...@uc...>, or by paper mail: * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic */ #include "iforce.h" void iforce_serial_xmit(struct iforce *iforce) { unsigned char cs; int i; unsigned long flags; if (test_and_set_bit(IFORCE_XMIT_RUNNING, iforce->xmit_flags)) { set_bit(IFORCE_XMIT_AGAIN, iforce->xmit_flags); return; } spin_lock_irqsave(&iforce->xmit_lock, flags); again: if (iforce->xmit.head == iforce->xmit.tail) { clear_bit(IFORCE_XMIT_RUNNING, iforce->xmit_flags); spin_unlock_irqrestore(&iforce->xmit_lock, flags); return; } cs = 0x2b; serio_write(iforce->serio, 0x2b); serio_write(iforce->serio, iforce->xmit.buf[iforce->xmit.tail]); cs ^= iforce->xmit.buf[iforce->xmit.tail]; XMIT_INC(iforce->xmit.tail, 1); for (i=iforce->xmit.buf[iforce->xmit.tail]; i >= 0; --i) { serio_write(iforce->serio, iforce->xmit.buf[iforce->xmit.tail]); cs ^= iforce->xmit.buf[iforce->xmit.tail]; XMIT_INC(iforce->xmit.tail, 1); } serio_write(iforce->serio, cs); if (test_and_clear_bit(IFORCE_XMIT_AGAIN, iforce->xmit_flags)) goto again; clear_bit(IFORCE_XMIT_RUNNING, iforce->xmit_flags); spin_unlock_irqrestore(&iforce->xmit_lock, flags); } static void iforce_serio_write_wakeup(struct serio *serio) { iforce_serial_xmit((struct iforce *)serio->private); } static void iforce_serio_irq(struct serio *serio, unsigned char data, unsigned int flags) { struct iforce* iforce = serio->private; if (!iforce->pkt) { if (data != 0x2b) { return; } iforce->pkt = 1; return; } if (!iforce->id) { if (data > 3 && data != 0xff) { iforce->pkt = 0; return; } iforce->id = data; return; } if (!iforce->len) { if (data > IFORCE_MAX_LENGTH) { iforce->pkt = 0; iforce->id = 0; return; } iforce->len = data; return; } if (iforce->idx < iforce->len) { iforce->csum += iforce->data[iforce->idx++] = data; return; } 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->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->bus = IFORCE_232; iforce->serio = serio; serio->private = iforce; if (serio_open(serio, dev)) { kfree(iforce); return; } if (iforce_init_device(iforce)) { serio_close(serio); kfree(iforce); return; } } static void iforce_serio_disconnect(struct serio *serio) { struct iforce* iforce = serio->private; input_unregister_device(&iforce->dev); serio_close(serio); kfree(iforce); } struct serio_dev iforce_serio_dev = { write_wakeup: iforce_serio_write_wakeup, interrupt: iforce_serio_irq, connect: iforce_serio_connect, disconnect: iforce_serio_disconnect, }; --- NEW FILE: iforce-usb.c --- /* * $Id: iforce-usb.c,v 1.1.2.1 2001/10/07 17:06:49 jdeneux Exp $ * * Copyright (c) 2000-2001 Vojtech Pavlik <vo...@uc...> * Copyright (c) 2001 Johann Deneux <de...@if...> * * USB/RS232 I-Force joysticks and wheels. */ /* * 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 <vo...@uc...>, or by paper mail: * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic */ #include "iforce.h" void iforce_usb_xmit(struct iforce *iforce) { int n, c; unsigned long flags; spin_lock_irqsave(&iforce->xmit_lock, flags); if (iforce->xmit.head == iforce->xmit.tail) { spin_unlock_irqrestore(&iforce->xmit_lock, flags); return; } ((char *)iforce->out.transfer_buffer)[0] = iforce->xmit.buf[iforce->xmit.tail]; XMIT_INC(iforce->xmit.tail, 1); n = iforce->xmit.buf[iforce->xmit.tail]; XMIT_INC(iforce->xmit.tail, 1); iforce->out.transfer_buffer_length = n + 2; iforce->out.dev = iforce->usbdev; /* Copy rest of data then */ c = CIRC_CNT_TO_END(iforce->xmit.head, iforce->xmit.tail, XMIT_SIZE); if (n < c) c=n; memcpy(iforce->out.transfer_buffer + 1, &iforce->xmit.buf[iforce->xmit.tail], c); if (n != c) { memcpy(iforce->out.transfer_buffer + 1 + c, &iforce->xmit.buf[0], n-c); } XMIT_INC(iforce->xmit.tail, n); if ( (n=usb_submit_urb(&iforce->out)) ) { printk(KERN_WARNING "iforce.c: iforce_usb_xmit: usb_submit_urb failed %d\n", n); } spin_unlock_irqrestore(&iforce->xmit_lock, flags); } static void iforce_usb_irq(struct urb *urb) { struct iforce *iforce = urb->context; if (urb->status) return; 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; iforce_usb_xmit(iforce); 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) { struct usb_endpoint_descriptor *epirq, *epout; struct iforce *iforce; 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) + 32, GFP_KERNEL))) return NULL; memset(iforce, 0, sizeof(struct iforce)); 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_BULK_URB(&iforce->out, dev, usb_sndbulkpipe(dev, epout->bEndpointAddress), iforce + 1, 32, iforce_usb_out, iforce); FILL_CONTROL_URB(&iforce->ctrl, dev, usb_rcvctrlpipe(dev, 0), (void*) &iforce->dr, iforce->edata, 16, iforce_usb_ctrl, iforce); if (iforce_init_device(iforce)) { kfree(iforce); return NULL; } return iforce; } static void iforce_usb_disconnect(struct usb_device *dev, void *ptr) { struct iforce *iforce = ptr; usb_unlink_urb(&iforce->irq); input_unregister_device(&iforce->dev); kfree(iforce); } static struct usb_device_id iforce_usb_ids [] = { { USB_DEVICE(0x044f, 0xa01c) }, /* Thrustmaster Motor Sport GT */ { 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(0x05ef, 0x8888) }, /* AVB Top Shot FFB Racing Wheel */ { USB_DEVICE(0x06f8, 0x0001) }, /* Guillemot Race Leader Force Feedback */ { } /* Terminating entry */ }; MODULE_DEVICE_TABLE (usb, iforce_usb_ids); struct usb_driver iforce_usb_driver = { name: "iforce", probe: iforce_usb_probe, disconnect: iforce_usb_disconnect, id_table: iforce_usb_ids, }; --- NEW FILE: iforce.h --- /* * $Id: iforce.h,v 1.1.2.1 2001/10/07 17:06:49 jdeneux Exp $ * * Copyright (c) 2000-2001 Vojtech Pavlik <vo...@uc...> * Copyright (c) 2001 Johann Deneux <de...@if...> * * USB/RS232 I-Force joysticks and wheels. */ /* * 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 <vo...@uc...>, or by paper mail: * Vojtech Pavlik, Simunkova 1594, 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> #include <linux/circ_buf.h> #include <asm/semaphore.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> #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 FALSE 0 #define TRUE 1 #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 /* Effect is actually being played */ #define FF_CORE_SHOULD_PLAY 4 /* User wants the effect to be played */ #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 #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 /* Buffer for async write */ #define XMIT_SIZE 256 #define XMIT_INC(var, n) (var)+=n; (var)&= XMIT_SIZE -1 /* iforce::xmit_flags */ #define IFORCE_XMIT_RUNNING 0 #define IFORCE_XMIT_AGAIN 1 struct iforce_device { u16 idvendor; u16 idproduct; char *name; signed short *btn; signed short *abs; signed short *ff; }; struct iforce { struct input_dev dev; /* Input device interface */ struct iforce_device *type; char name[64]; char phys[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 spinlock_t xmit_lock; /* Buffer used for asynchronous sending of bytes to the device */ struct circ_buf xmit; unsigned char xmit_data[XMIT_SIZE]; long xmit_flags[1]; /* Force Feedback */ wait_queue_head_t wait; struct resource device_memory; struct iforce_core_effect core_effects[FF_EFFECTS_MAX]; struct semaphore mem_mutex; }; /* Get hi and low bytes of a 16-bits int */ #define HI(a) ((unsigned char)((a) >> 8)) #define LO(a) ((unsigned char)((a) & 0xff)) /* For many parameters, it seems that 0x80 is a special value that should * be avoided. Instead, we replace this value by 0x7f */ #define HIFIX80(a) ((unsigned char)(((a)<0? (a)+255 : (a))>>8)) /* Encode a time value */ #define TIME_SCALE(a) ((a) == 0xffff ? 0xffff : (a) * 1000 / 256) /* Public functions */ /* iforce-serio.c */ void iforce_serial_xmit(struct iforce *iforce); /* iforce-usb.c */ void iforce_usb_xmit(struct iforce *iforce); /* iforce-main.c */ int iforce_init_device(struct iforce *iforce); /* iforce-packets.c */ void iforce_process_packet(struct iforce *iforce, u16 cmd, unsigned char *data); int iforce_send_packet(struct iforce *iforce, u16 cmd, unsigned char* data); 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_interactive(struct iforce*, struct ff_effect*, int is_update); /* Public variables */ struct serio_dev iforce_serio_dev; struct usb_driver iforce_usb_driver; Index: Makefile =================================================================== RCS file: /cvsroot/linuxconsole/ruby/linux/drivers/input/Makefile,v retrieving revision 1.42 retrieving revision 1.42.2.1 diff -u -d -r1.42 -r1.42.2.1 --- Makefile 2001/08/27 11:48:38 1.42 +++ Makefile 2001/10/07 17:06:49 1.42.2.1 @@ -28,6 +28,14 @@ hid-objs += hiddev.o endif +iforce-objs := iforce-packets.o iforce-ff.o iforce-main.o +ifeq ($(CONFIG_IFORCE_SERIAL),y) + iforce-objs += iforce-serio.o +endif +ifeq ($(CONFIG_IFORCE_USB),y) + iforce-objs += iforce-usb.o +endif + # Object file lists. obj-y := @@ -147,3 +155,6 @@ hid.o: $(hid-objs) $(LD) -r -o $@ $(hid-objs) + +iforce.o: $(iforce-objs) + $(LD) -r -o $@ $(iforce-objs) --- iforce.c DELETED --- |