|
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 ---
|