Commit 805f47cf authored by Greg Kroah-Hartman's avatar Greg Kroah-Hartman

USB: deleted hid-lg3dff.c as it's no longer needed.

parent 97cf4c34
/*
* $$
*
* Force feedback support for hid-compliant devices of the Logitech *3D family
*
* Copyright (c) 2002 Johann Deneux
*/
/*
* 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 by
* e-mail - mail your message to <deneux@ifrance.com>
*/
#include <linux/input.h>
#include <linux/sched.h>
#define DEBUG
#include <linux/usb.h>
#include <linux/circ_buf.h>
#include "hid.h"
#include "fixp-arith.h"
#define RUN_AT(t) (jiffies + (t))
/* Periodicity of the update */
#define PERIOD (HZ/10)
/* Effect status: lg3d_effect::flags */
#define EFFECT_STARTED 0 /* Effect is going to play after some time
(ff_replay.delay) */
#define EFFECT_PLAYING 1 /* Effect is being played */
#define EFFECT_USED 2
/* Check that the current process can access an effect */
#define CHECK_OWNERSHIP(i, l) \
(i>=0 && i<N_EFFECTS \
&& test_bit(EFFECT_USED, l->effects[i].flags) \
&& (current->pid == 0 \
|| l->effects[i].owner == current->pid))
#define N_EFFECTS 8
struct lg3d_effect {
pid_t owner;
struct ff_effect effect; /* Description of the effect */
unsigned int count; /* Number of times left to play */
unsigned long flags[1];
unsigned long started_at; /* When the effect started to play */
};
// For lg3d_device::flags
#define DEVICE_USB_XMIT 0 /* An URB is being sent */
#define DEVICE_CLOSING 1 /* The driver is being unitialised */
struct lg3d_device {
struct hid_device* hid;
struct urb* urbffout; /* Output URB used to send ff commands */
struct usb_ctrlrequest ffcr; /* ff commands use control URBs */
char buf[8];
struct lg3d_effect effects[N_EFFECTS];
spinlock_t lock; /* device-level lock. Having locks on
a per-effect basis could be nice, but
isn't really necessary */
struct timer_list timer;
unsigned long last_time; /* Last time the timer handler was
executed */
unsigned long flags[1]; /* Contains various information about the
state of the driver for this device */
};
static void hid_lg3d_ctrl_out(struct urb *urb);
static void hid_lg3d_exit(struct hid_device* hid);
static int hid_lg3d_event(struct hid_device *hid, struct input_dev *input,
unsigned int type, unsigned int code, int value);
static int hid_lg3d_flush(struct input_dev *input, struct file *file);
static int hid_lg3d_upload_effect(struct input_dev *input,
struct ff_effect *effect);
static int hid_lg3d_erase(struct input_dev *input, int id);
static void hid_lg3d_timer(unsigned long timer_data);
int hid_lg3d_init(struct hid_device* hid)
{
struct lg3d_device *private;
/* Private data */
private = kmalloc(sizeof(struct lg3d_device), GFP_KERNEL);
if (!private) return -1;
memset(private, 0, sizeof(struct lg3d_device));
hid->ff_private = private;
private->hid = hid;
spin_lock_init(&private->lock);
/* Timer for the periodic update task */
init_timer(&private->timer);
private->timer.data = (unsigned long)private;
private->timer.function = hid_lg3d_timer;
/* Event and exit callbacks */
hid->ff_exit = hid_lg3d_exit;
hid->ff_event = hid_lg3d_event;
/* USB init */
if (!(private->urbffout = usb_alloc_urb(0, GFP_KERNEL))) {
kfree(hid->ff_private);
return -1;
}
usb_fill_control_urb(private->urbffout, hid->dev, 0,
(void*) &private->ffcr, private->buf, 8,
hid_lg3d_ctrl_out, hid);
dbg("Created ff output control urb");
/* Input init */
hid->input.upload_effect = hid_lg3d_upload_effect;
hid->input.flush = hid_lg3d_flush;
set_bit(FF_CONSTANT, hid->input.ffbit);
set_bit(EV_FF, hid->input.evbit);
hid->input.ff_effects_max = N_EFFECTS;
printk(KERN_INFO "Force feedback for Logitech *3D devices by Johann Deneux <deneux@ifrance.com>\n");
/* Start the update task */
private->timer.expires = RUN_AT(PERIOD);
add_timer(&private->timer); /*TODO: only run the timer when at least
one effect is playing */
return 0;
}
static void hid_lg3d_exit(struct hid_device* hid)
{
struct lg3d_device *lg3d = hid->ff_private;
unsigned long flags;
spin_lock_irqsave(&lg3d->lock, flags);
set_bit(DEVICE_CLOSING, lg3d->flags);
spin_unlock_irqrestore(&lg3d->lock, flags);
del_timer_sync(&lg3d->timer);
if (lg3d->urbffout) {
usb_unlink_urb(lg3d->urbffout);
usb_free_urb(lg3d->urbffout);
}
kfree(lg3d);
}
static int hid_lg3d_event(struct hid_device *hid, struct input_dev* input,
unsigned int type, unsigned int code, int value)
{
struct lg3d_device *lg3d = hid->ff_private;
struct lg3d_effect *effect = lg3d->effects + code;
unsigned long flags;
if (type != EV_FF) return -EINVAL;
if (!CHECK_OWNERSHIP(code, lg3d)) return -EACCES;
if (value < 0) return -EINVAL;
spin_lock_irqsave(&lg3d->lock, flags);
if (value > 0) {
if (test_bit(EFFECT_STARTED, effect->flags)) {
spin_unlock_irqrestore(&lg3d->lock, flags);
return -EBUSY;
}
if (test_bit(EFFECT_PLAYING, effect->flags)) {
spin_unlock_irqrestore(&lg3d->lock, flags);
return -EBUSY;
}
effect->count = value;
if (effect->effect.replay.delay) {
set_bit(EFFECT_STARTED, effect->flags);
} else {
set_bit(EFFECT_PLAYING, effect->flags);
}
effect->started_at = jiffies;
}
else { /* value == 0 */
clear_bit(EFFECT_STARTED, effect->flags);
clear_bit(EFFECT_PLAYING, effect->flags);
}
spin_unlock_irqrestore(&lg3d->lock, flags);
return 0;
}
/* Erase all effects this process owns */
static int hid_lg3d_flush(struct input_dev *dev, struct file *file)
{
struct hid_device *hid = dev->private;
struct lg3d_device *lg3d = hid->ff_private;
int i;
for (i=0; i<dev->ff_effects_max; ++i) {
/*NOTE: no need to lock here. The only times EFFECT_USED is
modified is when effects are uploaded or when an effect is
erased. But a process cannot close its dev/input/eventX fd
and perform ioctls on the same fd all at the same time */
if ( current->pid == lg3d->effects[i].owner
&& test_bit(EFFECT_USED, lg3d->effects[i].flags)) {
if (hid_lg3d_erase(dev, i))
warn("erase effect %d failed", i);
}
}
return 0;
}
static int hid_lg3d_erase(struct input_dev *dev, int id)
{
struct hid_device *hid = dev->private;
struct lg3d_device *lg3d = hid->ff_private;
unsigned long flags;
if (!CHECK_OWNERSHIP(id, lg3d)) return -EACCES;
spin_lock_irqsave(&lg3d->lock, flags);
lg3d->effects[id].flags[0] = 0;
spin_unlock_irqrestore(&lg3d->lock, flags);
return 0;
}
static int hid_lg3d_upload_effect(struct input_dev* input,
struct ff_effect* effect)
{
struct hid_device *hid = input->private;
struct lg3d_device *lg3d = hid->ff_private;
struct lg3d_effect new;
int id;
unsigned long flags;
dbg("ioctl upload");
if (!test_bit(effect->type, input->ffbit)) return -EINVAL;
if (effect->type != FF_CONSTANT) return -EINVAL;
spin_lock_irqsave(&lg3d->lock, flags);
if (effect->id == -1) {
int i;
for (i=0; i<N_EFFECTS && test_bit(EFFECT_USED, lg3d->effects[i].flags); ++i);
if (i >= N_EFFECTS) {
spin_unlock_irqrestore(&lg3d->lock, flags);
return -ENOSPC;
}
effect->id = i;
lg3d->effects[i].owner = current->pid;
lg3d->effects[i].flags[0] = 0;
set_bit(EFFECT_USED, lg3d->effects[i].flags);
}
else if (!CHECK_OWNERSHIP(effect->id, lg3d)) {
spin_unlock_irqrestore(&lg3d->lock, flags);
return -EACCES;
}
id = effect->id;
new = lg3d->effects[id];
new.effect = *effect;
new.effect.replay = effect->replay;
if (test_bit(EFFECT_STARTED, lg3d->effects[id].flags)
|| test_bit(EFFECT_STARTED, lg3d->effects[id].flags)) {
/* Changing replay parameters is not allowed (for the time
being) */
if (new.effect.replay.delay != lg3d->effects[id].effect.replay.delay
|| new.effect.replay.length != lg3d->effects[id].effect.replay.length) {
spin_unlock_irqrestore(&lg3d->lock, flags);
return -ENOSYS;
}
lg3d->effects[id] = new;
} else {
lg3d->effects[id] = new;
}
spin_unlock_irqrestore(&lg3d->lock, flags);
return 0;
}
static void hid_lg3d_ctrl_out(struct urb *urb)
{
struct hid_device *hid = urb->context;
struct lg3d_device *lg3d = hid->ff_private;
unsigned long flags;
spin_lock_irqsave(&lg3d->lock, flags);
if (urb->status)
warn("hid_irq_ffout status %d received", urb->status);
clear_bit(DEVICE_USB_XMIT, lg3d->flags);
dbg("xmit = 0");
spin_unlock_irqrestore(&lg3d->lock, flags);
}
static void hid_lg3d_timer(unsigned long timer_data)
{
struct lg3d_device *lg3d = (struct lg3d_device*)timer_data;
struct hid_device *hid = lg3d->hid;
unsigned long flags;
int x, y;
int i;
int err;
spin_lock_irqsave(&lg3d->lock, flags);
if (test_bit(DEVICE_USB_XMIT, lg3d->flags)) {
if (lg3d->urbffout->status != -EINPROGRESS) {
warn("xmit *not* in progress");
}
else {
dbg("xmit in progress");
}
spin_unlock_irqrestore(&lg3d->lock, flags);
lg3d->timer.expires = RUN_AT(PERIOD);
add_timer(&lg3d->timer);
return;
}
x = 0x7f;
y = 0x7f;
for (i=0; i<N_EFFECTS; ++i) {
struct lg3d_effect* effect = lg3d->effects +i;
if (test_bit(EFFECT_PLAYING, effect->flags)) {
if (effect->effect.type == FF_CONSTANT) {
//TODO: handle envelopes
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));
}
/* 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");
if (--effect->count <= 0) {
dbg("Stopped");
clear_bit(EFFECT_PLAYING, effect->flags);
}
else {
dbg("Start again");
if (effect->effect.replay.length != 0) {
clear_bit(EFFECT_PLAYING, effect->flags);
set_bit(EFFECT_STARTED, effect->flags);
}
effect->started_at = jiffies;
}
}
} else if (test_bit(EFFECT_STARTED, lg3d->effects[i].flags)) {
dbg("Started");
/* Check if we should start playing the effect */
if (time_after(jiffies,
lg3d->effects[i].started_at
+ lg3d->effects[i].effect.replay.delay*HZ/1000)) {
dbg("Now playing");
clear_bit(EFFECT_STARTED, lg3d->effects[i].flags);
set_bit(EFFECT_PLAYING, lg3d->effects[i].flags);
}
}
}
if (x < 0) x = 0;
if (x > 0xff) x = 0xff;
if (y < 0) y = 0;
if (y > 0xff) y = 0xff;
lg3d->urbffout->pipe = usb_sndctrlpipe(hid->dev, 0);
lg3d->ffcr.bRequestType = USB_TYPE_CLASS | USB_DIR_OUT | USB_RECIP_INTERFACE;
lg3d->urbffout->transfer_buffer_length = lg3d->ffcr.wLength = 8;
lg3d->ffcr.bRequest = 9;
lg3d->ffcr.wValue = 0x0200; /*NOTE: Potential problem with
little/big endian */
lg3d->ffcr.wIndex = 0;
lg3d->urbffout->dev = hid->dev;
lg3d->buf[0] = 0x51;
lg3d->buf[1] = 0x08;
lg3d->buf[2] = x;
lg3d->buf[3] = y;
if ((err=usb_submit_urb(lg3d->urbffout, GFP_ATOMIC)))
warn("usb_submit_urb returned %d", err);
else
set_bit(DEVICE_USB_XMIT, lg3d->flags);
if (!test_bit(DEVICE_CLOSING, lg3d->flags)) {
lg3d->timer.expires = RUN_AT(PERIOD);
add_timer(&lg3d->timer);
}
spin_unlock_irqrestore(&lg3d->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