📄 iforce.c
字号:
/* * $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 3struct 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 0xff01static 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; 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;} 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.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; 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
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -