Commit 94d8a6be authored by Johann Deneux's avatar Johann Deneux Committed by Greg Kroah-Hartman

[PATCH] Merged hid-lgff.c and hid-lg3d.c

Here is a patch wich moves hid-lg3d.c into hid-lgff.c. This allows to
share the init code.
A side-effect of this merge is that rumble pads are now handled the same
way joysticks are. Instead of having one timer per effect, executing
only when needed, we have one timer executing at regular intervals going
over each effect.
parent 095067da
...@@ -30,10 +30,13 @@ CONFIG_HID_FF ...@@ -30,10 +30,13 @@ CONFIG_HID_FF
If unsure, say N. If unsure, say N.
CONFIG_LOGITECH_RUMBLE CONFIG_LOGITECH_FF
Say Y here if you have a Logitech WingMan Cordless rumble pad and if you Say Y here if you have one of these devices:
want to enable force feedback. Note: if you say N here, this device will - Logitech WingMan Cordless RumblePad
still be supported, but without force feedback. - Logitech WingMan Force 3D
and if you want to enable force feedback for them.
Note: if you say N here, this device will still be supported, but without
force feedback.
CONFIG_HID_PID CONFIG_HID_PID
Say Y here if you have a PID-compliant joystick and wish to enable force Say Y here if you have a PID-compliant joystick and wish to enable force
......
...@@ -11,8 +11,7 @@ fi ...@@ -11,8 +11,7 @@ fi
dep_mbool ' HID input layer support' CONFIG_USB_HIDINPUT $CONFIG_INPUT $CONFIG_USB_HID dep_mbool ' HID input layer support' CONFIG_USB_HIDINPUT $CONFIG_INPUT $CONFIG_USB_HID
dep_mbool ' Force feedback support (EXPERIMENTAL)' CONFIG_HID_FF $CONFIG_USB_HIDINPUT $CONFIG_EXPERIMENTAL dep_mbool ' Force feedback support (EXPERIMENTAL)' CONFIG_HID_FF $CONFIG_USB_HIDINPUT $CONFIG_EXPERIMENTAL
dep_mbool ' PID Devices' CONFIG_HID_PID $CONFIG_USB_HID $CONFIG_HID_FF dep_mbool ' PID Devices' CONFIG_HID_PID $CONFIG_USB_HID $CONFIG_HID_FF
dep_mbool ' Logitech RumblePad support' CONFIG_LOGITECH_RUMBLE $CONFIG_USB_HID $CONFIG_HID_FF dep_mbool ' Logitech WingMan *3D support' CONFIG_LOGITECH_FF $CONFIG_USB_HID $CONFIG_HID_FF
dep_mbool ' Logitech WingMan Force 3D support' CONFIG_LOGITECH_3D $CONFIG_USB_HID $CONFIG_HID_FF
dep_mbool ' /dev/hiddev raw HID device support' CONFIG_USB_HIDDEV $CONFIG_USB_HID dep_mbool ' /dev/hiddev raw HID device support' CONFIG_USB_HIDDEV $CONFIG_USB_HID
if [ "$CONFIG_USB_HID" != "y" ]; then if [ "$CONFIG_USB_HID" != "y" ]; then
......
...@@ -16,12 +16,9 @@ endif ...@@ -16,12 +16,9 @@ endif
ifeq ($(CONFIG_HID_PID),y) ifeq ($(CONFIG_HID_PID),y)
hid-objs += pid.o hid-objs += pid.o
endif endif
ifeq ($(CONFIG_LOGITECH_RUMBLE),y) ifeq ($(CONFIG_LOGITECH_FF),y)
hid-objs += hid-lgff.o hid-objs += hid-lgff.o
endif endif
ifeq ($(CONFIG_LOGITECH_3D),y)
hid-objs += hid-lg3dff.o
endif
ifeq ($(CONFIG_HID_FF),y) ifeq ($(CONFIG_HID_FF),y)
hid-objs += hid-ff.o hid-objs += hid-ff.o
endif endif
......
/* /*
* $Id: hid-ff.c,v 1.3 2002/06/09 11:06:38 jdeneux Exp $ * $Id: hid-ff.c,v 1.2 2002/04/18 22:02:47 jdeneux Exp $
* *
* Force feedback support for hid devices. * Force feedback support for hid devices.
* Not all hid devices use the same protocol. For example, some use PID, * Not all hid devices use the same protocol. For example, some use PID,
...@@ -44,17 +44,15 @@ extern int hid_pid_init(struct hid_device* hid); ...@@ -44,17 +44,15 @@ extern int hid_pid_init(struct hid_device* hid);
* devices, you need to add the USB vendor and product ids here. * devices, you need to add the USB vendor and product ids here.
*/ */
struct hid_ff_initializer { struct hid_ff_initializer {
__u16 idVendor; u16 idVendor;
__u16 idProduct; u16 idProduct;
int (*init)(struct hid_device*); int (*init)(struct hid_device*);
}; };
static struct hid_ff_initializer inits[] = { static struct hid_ff_initializer inits[] = {
#ifdef CONFIG_LOGITECH_RUMBLE #ifdef CONFIG_LOGITECH_FF
{0x46d, 0xc211, hid_lgff_init}, {0x46d, 0xc211, hid_lgff_init},
#endif {0x46d, 0xc283, hid_lgff_init},
#ifdef CONFIG_LOGITECH_3D
{0x46d, 0xc283, hid_lg3d_init},
#endif #endif
#ifdef CONFIG_HID_PID #ifdef CONFIG_HID_PID
{0x45e, 0x001b, hid_pid_init}, {0x45e, 0x001b, hid_pid_init},
......
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
* Force feedback support for hid-compliant for some of the devices from * Force feedback support for hid-compliant for some of the devices from
* Logitech, namely: * Logitech, namely:
* - WingMan Cordless RumblePad * - WingMan Cordless RumblePad
* - WingMan Force 3D
* *
* Copyright (c) 2002 Johann Deneux * Copyright (c) 2002 Johann Deneux
*/ */
...@@ -36,6 +37,11 @@ ...@@ -36,6 +37,11 @@
#include <linux/circ_buf.h> #include <linux/circ_buf.h>
#include "hid.h" #include "hid.h"
#include "fixp-arith.h"
/* Periodicity of the update */
#define PERIOD (HZ/10)
#define RUN_AT(t) (jiffies + (t)) #define RUN_AT(t) (jiffies + (t))
...@@ -48,145 +54,243 @@ ...@@ -48,145 +54,243 @@
#define EFFECT_PLAYING 1 /* Effect is being played */ #define EFFECT_PLAYING 1 /* Effect is being played */
#define EFFECT_USED 2 #define EFFECT_USED 2
// For lgff_device::flags
#define DEVICE_CLOSING 0 /* The driver is being unitialised */
/* Check that the current process can access an effect */ /* Check that the current process can access an effect */
#define CHECK_OWNERSHIP(effect) (current->pid == 0 \ #define CHECK_OWNERSHIP(effect) (current->pid == 0 \
|| effect.owner == current->pid) || effect.owner == current->pid)
/* **************************************************************************/
/* Implements the protocol used by the Logitech WingMan Cordless RumblePad */
/* **************************************************************************/
#define LGFF_CHECK_OWNERSHIP(i, l) \ #define LGFF_CHECK_OWNERSHIP(i, l) \
(i>=0 && i<LGFF_EFFECTS \ (i>=0 && i<LGFF_EFFECTS \
&& test_bit(EFFECT_USED, l->effects[i].flags) \ && test_bit(EFFECT_USED, l->effects[i].flags) \
&& CHECK_OWNERSHIP(l->effects[i])) && CHECK_OWNERSHIP(l->effects[i]))
#define LGFF_BUFFER_SIZE 64
#define LGFF_EFFECTS 8 #define LGFF_EFFECTS 8
struct lgff_magnitudes { struct device_type {
unsigned char left; u16 idVendor;
unsigned char right; u16 idProduct;
signed short *ff;
}; };
struct lgff_effect { struct lgff_effect {
int id;
struct hid_ff_logitech* lgff;
pid_t owner; pid_t owner;
unsigned char left; /* Magnitude of vibration for left motor */
unsigned char right; /* Magnitude of vibration for right motor */ struct ff_effect effect;
struct ff_replay replay;
unsigned int count; /* Number of times to play */
struct timer_list timer;
unsigned long flags[1]; unsigned long flags[1];
unsigned int count; /* Number of times left to play */
unsigned long started_at; /* When the effect started to play */
}; };
struct hid_ff_logitech { struct lgff_device {
struct hid_device* hid; struct hid_device* hid;
struct urb* urbffout; /* Output URB used to send ff commands */ struct hid_report* constant;
struct usb_ctrlrequest ffcr; /* ff commands use control URBs */ struct hid_report* rumble;
char buf[8]; struct hid_report* condition;
spinlock_t xmit_lock;
unsigned int xmit_head, xmit_tail;
struct lgff_magnitudes xmit_data[LGFF_BUFFER_SIZE];
long xmit_flags[1];
struct lgff_effect effects[LGFF_EFFECTS]; struct lgff_effect effects[LGFF_EFFECTS];
spinlock_t lock; /* device-level lock. Having locks on spinlock_t lock; /* device-level lock. Having locks on
a per-effect basis could be nice, but a per-effect basis could be nice, but
isn't really necessary */ isn't really necessary */
unsigned long flags[1]; /* Contains various information about the
state of the driver for this device */
struct timer_list timer;
}; };
static void hid_lgff_ctrl_out(struct urb *urb); /* Callbacks */
static void hid_lgff_exit(struct hid_device* hid); static void hid_lgff_exit(struct hid_device* hid);
static int hid_lgff_event(struct hid_device *hid, struct input_dev *input, static int hid_lgff_event(struct hid_device *hid, struct input_dev *input,
unsigned int type, unsigned int code, int value); unsigned int type, unsigned int code, int value);
static void hid_lgff_make_rumble(struct hid_device* hid);
static int hid_lgff_flush(struct input_dev *input, struct file *file); static int hid_lgff_flush(struct input_dev *input, struct file *file);
static int hid_lgff_upload_effect(struct input_dev *input, static int hid_lgff_upload_effect(struct input_dev *input,
struct ff_effect *effect); struct ff_effect *effect);
static int hid_lgff_erase(struct input_dev *input, int id); static int hid_lgff_erase(struct input_dev *input, int id);
static void hid_lgff_ctrl_playback(struct hid_device* hid, struct lgff_effect*,
int play); /* Local functions */
static void hid_lgff_input_init(struct hid_device* hid);
static void hid_lgff_timer(unsigned long timer_data); static void hid_lgff_timer(unsigned long timer_data);
static struct hid_report* hid_lgff_duplicate_report(struct hid_report*);
static void hid_lgff_delete_report(struct hid_report*);
static signed short ff_rumble[] = {
FF_RUMBLE,
-1
};
static signed short ff_joystick[] = {
FF_CONSTANT,
-1
};
static struct device_type devices[] = {
{0x046d, 0xc211, ff_rumble},
{0x046d, 0xc283, ff_joystick},
{0x0000, 0x0000, ff_joystick}
};
int hid_lgff_init(struct hid_device* hid) int hid_lgff_init(struct hid_device* hid)
{ {
struct hid_ff_logitech *private; struct lgff_device *private;
int i; struct hid_report* report;
struct hid_field* field;
/* Private data */
private = kmalloc(sizeof(struct hid_ff_logitech), GFP_KERNEL);
if (!private) return -1;
memset(private, 0, sizeof(struct hid_ff_logitech)); /* Find the report to use */
if (list_empty(&hid->report_enum[HID_OUTPUT_REPORT].report_list)) {
err("No output report found");
return -1;
}
/* Check that the report looks ok */
report = (struct hid_report*)hid->report_enum[HID_OUTPUT_REPORT].report_list.next;
if (!report) {
err("NULL output report");
return -1;
}
field = report->field[0];
if (!field) {
err("NULL field");
return -1;
}
private = kmalloc(sizeof(struct lgff_device), GFP_KERNEL);
if (!private) return -1;
memset(private, 0, sizeof(struct lgff_device));
hid->ff_private = private; hid->ff_private = private;
private->hid = hid; /* Input init */
spin_lock_init(&private->lock); hid_lgff_input_init(hid);
spin_lock_init(&private->xmit_lock);
private->buf[0] = 0x03;
private->buf[1] = 0x42;
for (i=0; i<LGFF_EFFECTS; ++i) { private->constant = hid_lgff_duplicate_report(report);
struct lgff_effect* effect = &private->effects[i]; if (!private->constant) {
struct timer_list* timer = &effect->timer; kfree(private);
return -1;
}
private->constant->field[0]->value[0] = 0x51;
private->constant->field[0]->value[1] = 0x08;
private->constant->field[0]->value[2] = 0x7f;
private->constant->field[0]->value[3] = 0x7f;
private->rumble = hid_lgff_duplicate_report(report);
if (!private->rumble) {
hid_lgff_delete_report(private->constant);
kfree(private);
return -1;
}
private->rumble->field[0]->value[0] = 0x03;
private->rumble->field[0]->value[1] = 0x42;
init_timer(timer);
effect->id = i; private->condition = hid_lgff_duplicate_report(report);
effect->lgff = private; if (!private->condition) {
timer->data = (unsigned long)effect; hid_lgff_delete_report(private->rumble);
timer->function = hid_lgff_timer; hid_lgff_delete_report(private->constant);
kfree(private);
return -1;
} }
private->hid = hid;
spin_lock_init(&private->lock);
init_timer(&private->timer);
private->timer.data = (unsigned long)private;
private->timer.function = hid_lgff_timer;
/* Event and exit callbacks */ /* Event and exit callbacks */
hid->ff_exit = hid_lgff_exit; hid->ff_exit = hid_lgff_exit;
hid->ff_event = hid_lgff_event; hid->ff_event = hid_lgff_event;
/* USB init */ /* Start the update task */
if (!(private->urbffout = usb_alloc_urb(0, GFP_KERNEL))) { private->timer.expires = RUN_AT(PERIOD);
kfree(hid->ff_private); add_timer(&private->timer); /*TODO: only run the timer when at least
return -1; one effect is playing */
printk(KERN_INFO "Force feedback for Logitech force feedback devices by Johann Deneux <deneux@ifrance.com>\n");
return 0;
}
static struct hid_report* hid_lgff_duplicate_report(struct hid_report* report)
{
struct hid_report* ret;
ret = kmalloc(sizeof(struct lgff_device), GFP_KERNEL);
if (!ret) return NULL;
*ret = *report;
ret->field[0] = kmalloc(sizeof(struct hid_field), GFP_KERNEL);
if (!ret->field[0]) {
kfree(ret);
return NULL;
} }
*ret->field[0] = *report->field[0];
usb_fill_control_urb(private->urbffout, hid->dev, 0, ret->field[0]->value = kmalloc(sizeof(s32[8]), GFP_KERNEL);
(void*) &private->ffcr, private->buf, 8, if (!ret->field[0]->value) {
hid_lgff_ctrl_out, hid); kfree(ret->field[0]);
dbg("Created ff output control urb"); kfree(ret);
return NULL;
}
memset(ret->field[0]->value, 0, sizeof(s32[8]));
return ret;
}
static void hid_lgff_delete_report(struct hid_report* report)
{
if (report) {
kfree(report->field[0]->value);
kfree(report->field[0]);
kfree(report);
}
}
static void hid_lgff_input_init(struct hid_device* hid)
{
struct device_type* dev = devices;
signed short* ff;
u16 idVendor = hid->dev->descriptor.idVendor;
u16 idProduct = hid->dev->descriptor.idProduct;
while (dev->idVendor && (idVendor != dev->idVendor || idProduct != dev->idProduct))
dev++;
ff = dev->ff;
while (*ff >= 0) {
set_bit(*ff, hid->input.ffbit);
++ff;
}
/* Input init */
hid->input.upload_effect = hid_lgff_upload_effect; hid->input.upload_effect = hid_lgff_upload_effect;
hid->input.flush = hid_lgff_flush; hid->input.flush = hid_lgff_flush;
set_bit(FF_RUMBLE, hid->input.ffbit);
set_bit(EV_FF, hid->input.evbit); set_bit(EV_FF, hid->input.evbit);
hid->input.ff_effects_max = LGFF_EFFECTS; hid->input.ff_effects_max = LGFF_EFFECTS;
printk(KERN_INFO "Force feedback for Logitech rumble devices by Johann Deneux <deneux@ifrance.com>\n");
return 0;
} }
static void hid_lgff_exit(struct hid_device* hid) static void hid_lgff_exit(struct hid_device* hid)
{ {
struct hid_ff_logitech *lgff = hid->ff_private; struct lgff_device *lgff = hid->ff_private;
if (lgff->urbffout) { set_bit(DEVICE_CLOSING, lgff->flags);
usb_unlink_urb(lgff->urbffout); del_timer_sync(&lgff->timer);
usb_free_urb(lgff->urbffout);
} hid_lgff_delete_report(lgff->condition);
hid_lgff_delete_report(lgff->rumble);
hid_lgff_delete_report(lgff->constant);
kfree(lgff);
} }
static int hid_lgff_event(struct hid_device *hid, struct input_dev* input, static int hid_lgff_event(struct hid_device *hid, struct input_dev* input,
unsigned int type, unsigned int code, int value) unsigned int type, unsigned int code, int value)
{ {
struct hid_ff_logitech *lgff = hid->ff_private; struct lgff_device *lgff = hid->ff_private;
struct lgff_effect *effect = lgff->effects + code; struct lgff_effect *effect = lgff->effects + code;
unsigned long flags; unsigned long flags;
...@@ -208,27 +312,16 @@ static int hid_lgff_event(struct hid_device *hid, struct input_dev* input, ...@@ -208,27 +312,16 @@ static int hid_lgff_event(struct hid_device *hid, struct input_dev* input,
effect->count = value; effect->count = value;
if (effect->replay.delay) { if (effect->effect.replay.delay) {
set_bit(EFFECT_STARTED, effect->flags); set_bit(EFFECT_STARTED, effect->flags);
effect->timer.expires = RUN_AT(effect->replay.delay * HZ / 1000);
} else { } else {
hid_lgff_ctrl_playback(hid, effect, value); set_bit(EFFECT_PLAYING, effect->flags);
effect->timer.expires = RUN_AT(effect->replay.length * HZ / 1000);
} }
effect->started_at = jiffies;
add_timer(&effect->timer);
} }
else { /* value == 0 */ else { /* value == 0 */
if (test_and_clear_bit(EFFECT_STARTED, effect->flags)) { clear_bit(EFFECT_STARTED, effect->flags);
del_timer(&effect->timer); clear_bit(EFFECT_PLAYING, effect->flags);
} else if (test_and_clear_bit(EFFECT_PLAYING, effect->flags)) {
del_timer(&effect->timer);
hid_lgff_ctrl_playback(hid, effect, value);
}
if (test_bit(EFFECT_PLAYING, effect->flags))
warn("Effect %d still playing", code);
} }
spin_unlock_irqrestore(&lgff->lock, flags); spin_unlock_irqrestore(&lgff->lock, flags);
...@@ -241,7 +334,7 @@ static int hid_lgff_event(struct hid_device *hid, struct input_dev* input, ...@@ -241,7 +334,7 @@ static int hid_lgff_event(struct hid_device *hid, struct input_dev* input,
static int hid_lgff_flush(struct input_dev *dev, struct file *file) static int hid_lgff_flush(struct input_dev *dev, struct file *file)
{ {
struct hid_device *hid = dev->private; struct hid_device *hid = dev->private;
struct hid_ff_logitech *lgff = hid->ff_private; struct lgff_device *lgff = hid->ff_private;
int i; int i;
for (i=0; i<dev->ff_effects_max; ++i) { for (i=0; i<dev->ff_effects_max; ++i) {
...@@ -265,13 +358,12 @@ static int hid_lgff_flush(struct input_dev *dev, struct file *file) ...@@ -265,13 +358,12 @@ static int hid_lgff_flush(struct input_dev *dev, struct file *file)
static int hid_lgff_erase(struct input_dev *dev, int id) static int hid_lgff_erase(struct input_dev *dev, int id)
{ {
struct hid_device *hid = dev->private; struct hid_device *hid = dev->private;
struct hid_ff_logitech *lgff = hid->ff_private; struct lgff_device *lgff = hid->ff_private;
unsigned long flags; unsigned long flags;
if (!LGFF_CHECK_OWNERSHIP(id, lgff)) return -EACCES; if (!LGFF_CHECK_OWNERSHIP(id, lgff)) return -EACCES;
spin_lock_irqsave(&lgff->lock, flags); spin_lock_irqsave(&lgff->lock, flags);
hid_lgff_ctrl_playback(hid, lgff->effects + id, 0);
lgff->effects[id].flags[0] = 0; lgff->effects[id].flags[0] = 0;
spin_unlock_irqrestore(&lgff->lock, flags); spin_unlock_irqrestore(&lgff->lock, flags);
...@@ -282,7 +374,7 @@ static int hid_lgff_upload_effect(struct input_dev* input, ...@@ -282,7 +374,7 @@ static int hid_lgff_upload_effect(struct input_dev* input,
struct ff_effect* effect) struct ff_effect* effect)
{ {
struct hid_device *hid = input->private; struct hid_device *hid = input->private;
struct hid_ff_logitech *lgff = hid->ff_private; struct lgff_device *lgff = hid->ff_private;
struct lgff_effect new; struct lgff_effect new;
int id; int id;
unsigned long flags; unsigned long flags;
...@@ -291,8 +383,6 @@ static int hid_lgff_upload_effect(struct input_dev* input, ...@@ -291,8 +383,6 @@ static int hid_lgff_upload_effect(struct input_dev* input,
if (!test_bit(effect->type, input->ffbit)) return -EINVAL; if (!test_bit(effect->type, input->ffbit)) return -EINVAL;
if (effect->type != FF_RUMBLE) return -EINVAL;
spin_lock_irqsave(&lgff->lock, flags); spin_lock_irqsave(&lgff->lock, flags);
if (effect->id == -1) { if (effect->id == -1) {
...@@ -317,25 +407,20 @@ static int hid_lgff_upload_effect(struct input_dev* input, ...@@ -317,25 +407,20 @@ static int hid_lgff_upload_effect(struct input_dev* input,
id = effect->id; id = effect->id;
new = lgff->effects[id]; new = lgff->effects[id];
new.right = effect->u.rumble.strong_magnitude >> 9; new.effect = *effect;
new.left = effect->u.rumble.weak_magnitude >> 9;
new.replay = effect->replay;
/* If we updated an effect that was being played, we need to remake
the rumble effect */
if (test_bit(EFFECT_STARTED, lgff->effects[id].flags) if (test_bit(EFFECT_STARTED, lgff->effects[id].flags)
|| test_bit(EFFECT_STARTED, lgff->effects[id].flags)) { || test_bit(EFFECT_STARTED, lgff->effects[id].flags)) {
/* Changing replay parameters is not allowed (for the time /* Changing replay parameters is not allowed (for the time
being) */ being) */
if (new.replay.delay != lgff->effects[id].replay.delay if (new.effect.replay.delay != lgff->effects[id].effect.replay.delay
|| new.replay.length != lgff->effects[id].replay.length) { || new.effect.replay.length != lgff->effects[id].effect.replay.length) {
spin_unlock_irqrestore(&lgff->lock, flags); spin_unlock_irqrestore(&lgff->lock, flags);
return -ENOSYS; return -ENOSYS;
} }
lgff->effects[id] = new; lgff->effects[id] = new;
hid_lgff_make_rumble(hid);
} else { } else {
lgff->effects[id] = new; lgff->effects[id] = new;
...@@ -345,150 +430,98 @@ static int hid_lgff_upload_effect(struct input_dev* input, ...@@ -345,150 +430,98 @@ static int hid_lgff_upload_effect(struct input_dev* input,
return 0; return 0;
} }
static void hid_lgff_xmit(struct hid_device* hid) static void hid_lgff_timer(unsigned long timer_data)
{ {
struct hid_ff_logitech *lgff = hid->ff_private; struct lgff_device *lgff = (struct lgff_device*)timer_data;
int err; struct hid_device *hid = lgff->hid;
int tail;
unsigned long flags; unsigned long flags;
int x = 0x7f, y = 0x7f; // Coordinates of constant effects
spin_lock_irqsave(&lgff->xmit_lock, flags); unsigned int left = 0, right = 0; // Rumbling
tail = lgff->xmit_tail;
if (lgff->xmit_head == tail) {
clear_bit(XMIT_RUNNING, lgff->xmit_flags);
spin_unlock_irqrestore(&lgff->xmit_lock, flags);
return;
}
lgff->buf[3] = lgff->xmit_data[tail].left;
lgff->buf[4] = lgff->xmit_data[tail].right;
tail++; tail &= LGFF_BUFFER_SIZE -1;
lgff->xmit_tail = tail;
spin_unlock_irqrestore(&lgff->xmit_lock, flags);
lgff->urbffout->pipe = usb_sndctrlpipe(hid->dev, 0);
lgff->ffcr.bRequestType = USB_TYPE_CLASS | USB_DIR_OUT | USB_RECIP_INTERFACE;
lgff->urbffout->transfer_buffer_length = lgff->ffcr.wLength = 8;
lgff->ffcr.bRequest = 9;
lgff->ffcr.wValue = 0x0203; /*NOTE: Potential problem with
little/big endian */
lgff->ffcr.wIndex = 0;
lgff->urbffout->dev = hid->dev;
if ((err=usb_submit_urb(lgff->urbffout, GFP_ATOMIC)))
warn("usb_submit_urb returned %d", err);
}
static void hid_lgff_make_rumble(struct hid_device* hid)
{
struct hid_ff_logitech *lgff = hid->ff_private;
int left = 0, right = 0;
int i; int i;
int head, tail;
unsigned long flags;
for (i=0; i<LGFF_EFFECTS; ++i) { spin_lock_irqsave(&lgff->lock, flags);
if (test_bit(EFFECT_USED, lgff->effects[i].flags)
&& test_bit(EFFECT_PLAYING, lgff->effects[i].flags)) {
left += lgff->effects[i].left;
right += lgff->effects[i].right;
}
}
spin_lock_irqsave(&lgff->xmit_lock, flags); for (i=0; i<LGFF_EFFECTS; ++i) {
struct lgff_effect* effect = lgff->effects +i;
head = lgff->xmit_head; if (test_bit(EFFECT_PLAYING, effect->flags)) {
tail = lgff->xmit_tail;
if (CIRC_SPACE(head, tail, LGFF_BUFFER_SIZE) < 1) { switch (effect->effect.type) {
warn("not enough space in xmit buffer to send new packet"); case FF_CONSTANT: {
spin_unlock_irqrestore(&lgff->xmit_lock, flags); //TODO: handle envelopes
return; int degrees = effect->effect.direction * 360 >> 16;
x += fixp_mult(fixp_sin(degrees),
fixp_new16(effect->effect.u.constant.level));
y += fixp_mult(-fixp_cos(degrees),
fixp_new16(effect->effect.u.constant.level));
} break;
case FF_RUMBLE:
right += effect->effect.u.rumble.strong_magnitude;
left += effect->effect.u.rumble.weak_magnitude;
break;
};
/* One run of the effect is finished playing */
if (time_after(jiffies,
effect->started_at
+ effect->effect.replay.delay*HZ/1000
+ effect->effect.replay.length*HZ/1000)) {
dbg("Finished playing once %d", i);
if (--effect->count <= 0) {
dbg("Stopped %d", i);
clear_bit(EFFECT_PLAYING, effect->flags);
} }
lgff->xmit_data[head].left = left > 0x7f ? 0x7f : left;
lgff->xmit_data[head].right = right > 0x7f ? 0x7f : right;
head++; head &= LGFF_BUFFER_SIZE -1;
lgff->xmit_head = head;
if (test_and_set_bit(XMIT_RUNNING, lgff->xmit_flags))
spin_unlock_irqrestore(&lgff->xmit_lock, flags);
else { else {
spin_unlock_irqrestore(&lgff->xmit_lock, flags); dbg("Start again %d", i);
hid_lgff_xmit(hid); if (effect->effect.replay.length != 0) {
}
}
static void hid_lgff_ctrl_out(struct urb *urb)
{
struct hid_device *hid = urb->context;
if (urb->status)
warn("hid_irq_ffout status %d received", urb->status);
hid_lgff_xmit(hid);
}
/* Lock must be held by caller */
static void hid_lgff_ctrl_playback(struct hid_device *hid,
struct lgff_effect *effect, int play)
{
if (play) {
set_bit(EFFECT_PLAYING, effect->flags);
hid_lgff_make_rumble(hid);
} else {
clear_bit(EFFECT_PLAYING, effect->flags); clear_bit(EFFECT_PLAYING, effect->flags);
hid_lgff_make_rumble(hid); set_bit(EFFECT_STARTED, effect->flags);
}
effect->started_at = jiffies;
}
} }
}
static void hid_lgff_timer(unsigned long timer_data)
{
struct lgff_effect *effect = (struct lgff_effect*) timer_data;
struct hid_ff_logitech* lgff = effect->lgff;
int id = effect->id;
unsigned long flags;
dbg("in hid_lgff_timer");
if (id < 0 || id >= LGFF_EFFECTS) { } else if (test_bit(EFFECT_STARTED, lgff->effects[i].flags)) {
warn("Bad effect id %d", id); /* Check if we should start playing the effect */
return; if (time_after(jiffies,
lgff->effects[i].started_at
+ lgff->effects[i].effect.replay.delay*HZ/1000)) {
dbg("Now playing %d", i);
clear_bit(EFFECT_STARTED, lgff->effects[i].flags);
set_bit(EFFECT_PLAYING, lgff->effects[i].flags);
}
}
} }
effect = lgff->effects + id; #define CLAMP(x) if (x < 0) x = 0; if (x > 0xff) x = 0xff
spin_lock_irqsave(&lgff->lock, flags); // Clamp values
CLAMP(x);
CLAMP(y);
CLAMP(left);
CLAMP(right);
if (!test_bit(EFFECT_USED, effect->flags)) { #undef CLAMP
warn("Unused effect id %d", id);
} else if (test_bit(EFFECT_STARTED, effect->flags)) { if (x != lgff->constant->field[0]->value[2]
clear_bit(EFFECT_STARTED, effect->flags); || y != lgff->constant->field[0]->value[3]) {
set_bit(EFFECT_PLAYING, effect->flags); lgff->constant->field[0]->value[2] = x;
hid_lgff_ctrl_playback(lgff->hid, effect, 1); lgff->constant->field[0]->value[3] = y;
effect->timer.expires = RUN_AT(effect->replay.length * HZ / 1000); dbg("(x,y)=(%04x, %04x)", x, y);
add_timer(&effect->timer); hid_submit_report(hid, lgff->constant, USB_DIR_OUT);
}
dbg("Effect %d starts playing", id); if (left != lgff->rumble->field[0]->value[3]
} else if (test_bit(EFFECT_PLAYING, effect->flags)) { || right != lgff->rumble->field[0]->value[4]) {
clear_bit(EFFECT_PLAYING, effect->flags); lgff->rumble->field[0]->value[3] = left;
hid_lgff_ctrl_playback(lgff->hid, effect, 0); lgff->rumble->field[0]->value[4] = right;
if (--effect->count > 0) { dbg("(left,right)=(%04x, %04x)", left, right);
/*TODO: check that replay.delay is non-null */ hid_submit_report(hid, lgff->rumble, USB_DIR_OUT);
set_bit(EFFECT_STARTED, effect->flags);
effect->timer.expires = RUN_AT(effect->replay.delay * HZ / 1000);
add_timer(&effect->timer);
dbg("Effect %d restarted", id);
} else {
dbg("Effect %d stopped", id);
} }
} else {
warn("Effect %d is not started nor playing", id); if (!test_bit(DEVICE_CLOSING, lgff->flags)) {
lgff->timer.expires = RUN_AT(PERIOD);
add_timer(&lgff->timer);
} }
spin_unlock_irqrestore(&lgff->lock, flags); spin_unlock_irqrestore(&lgff->lock, flags);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment