diff options
Diffstat (limited to 'drivers/usb/input/hid-lgff.c')
| -rw-r--r-- | drivers/usb/input/hid-lgff.c | 525 |
1 files changed, 0 insertions, 525 deletions
diff --git a/drivers/usb/input/hid-lgff.c b/drivers/usb/input/hid-lgff.c deleted file mode 100644 index f82c9c9e5d5..00000000000 --- a/drivers/usb/input/hid-lgff.c +++ /dev/null @@ -1,525 +0,0 @@ -/* - * $$ - * - * Force feedback support for hid-compliant for some of the devices from - * Logitech, namely: - * - WingMan Cordless RumblePad - * - WingMan Force 3D - * - * Copyright (c) 2002-2004 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 <johann.deneux@it.uu.se> - */ - -#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" - - -/* Periodicity of the update */ -#define PERIOD (HZ/10) - -#define RUN_AT(t) (jiffies + (t)) - -/* Effect status */ -#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 - -// For lgff_device::flags -#define DEVICE_CLOSING 0 /* The driver is being unitialised */ - -/* Check that the current process can access an effect */ -#define CHECK_OWNERSHIP(effect) (current->pid == 0 \ - || effect.owner == current->pid) - -#define LGFF_CHECK_OWNERSHIP(i, l) \ - (i>=0 && i<LGFF_EFFECTS \ - && test_bit(EFFECT_USED, l->effects[i].flags) \ - && CHECK_OWNERSHIP(l->effects[i])) - -#define LGFF_EFFECTS 8 - -struct device_type { - u16 idVendor; - u16 idProduct; - signed short *ff; -}; - -struct lgff_effect { - pid_t owner; - - struct ff_effect effect; - - unsigned long flags[1]; - unsigned int count; /* Number of times left to play */ - unsigned long started_at; /* When the effect started to play */ -}; - -struct lgff_device { - struct hid_device* hid; - - struct hid_report* constant; - struct hid_report* rumble; - struct hid_report* condition; - - struct lgff_effect effects[LGFF_EFFECTS]; - spinlock_t lock; /* device-level lock. Having locks on - a per-effect basis could be nice, but - isn't really necessary */ - - unsigned long flags[1]; /* Contains various information about the - state of the driver for this device */ - - struct timer_list timer; -}; - -/* Callbacks */ -static void hid_lgff_exit(struct hid_device* hid); -static int hid_lgff_event(struct hid_device *hid, struct input_dev *input, - unsigned int type, unsigned int code, int value); -static int hid_lgff_flush(struct input_dev *input, struct file *file); -static int hid_lgff_upload_effect(struct input_dev *input, - struct ff_effect *effect); -static int hid_lgff_erase(struct input_dev *input, int id); - -/* Local functions */ -static void hid_lgff_input_init(struct hid_device* hid); -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, 0xc219, ff_rumble}, - {0x046d, 0xc283, ff_joystick}, - {0x0000, 0x0000, ff_joystick} -}; - -int hid_lgff_init(struct hid_device* hid) -{ - struct lgff_device *private; - struct hid_report* report; - struct hid_field* field; - - /* 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; - - /* Input init */ - hid_lgff_input_init(hid); - - - private->constant = hid_lgff_duplicate_report(report); - if (!private->constant) { - 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] = 0x42; - - - private->condition = hid_lgff_duplicate_report(report); - if (!private->condition) { - hid_lgff_delete_report(private->rumble); - 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 */ - hid->ff_exit = hid_lgff_exit; - hid->ff_event = hid_lgff_event; - - /* 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 */ - - printk(KERN_INFO "Force feedback for Logitech force feedback devices by Johann Deneux <johann.deneux@it.uu.se>\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]; - - ret->field[0]->value = kmalloc(sizeof(s32[8]), GFP_KERNEL); - if (!ret->field[0]->value) { - kfree(ret->field[0]); - 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 = le16_to_cpu(hid->dev->descriptor.idVendor); - u16 idProduct = le16_to_cpu(hid->dev->descriptor.idProduct); - struct hid_input *hidinput = list_entry(hid->inputs.next, struct hid_input, list); - struct input_dev *input_dev = hidinput->input; - - while (dev->idVendor && (idVendor != dev->idVendor || idProduct != dev->idProduct)) - dev++; - - for (ff = dev->ff; *ff >= 0; ff++) - set_bit(*ff, input_dev->ffbit); - - input_dev->upload_effect = hid_lgff_upload_effect; - input_dev->flush = hid_lgff_flush; - - set_bit(EV_FF, input_dev->evbit); - input_dev->ff_effects_max = LGFF_EFFECTS; -} - -static void hid_lgff_exit(struct hid_device* hid) -{ - struct lgff_device *lgff = hid->ff_private; - - set_bit(DEVICE_CLOSING, lgff->flags); - del_timer_sync(&lgff->timer); - - 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, - unsigned int type, unsigned int code, int value) -{ - struct lgff_device *lgff = hid->ff_private; - struct lgff_effect *effect = lgff->effects + code; - unsigned long flags; - - if (type != EV_FF) return -EINVAL; - if (!LGFF_CHECK_OWNERSHIP(code, lgff)) return -EACCES; - if (value < 0) return -EINVAL; - - spin_lock_irqsave(&lgff->lock, flags); - - if (value > 0) { - if (test_bit(EFFECT_STARTED, effect->flags)) { - spin_unlock_irqrestore(&lgff->lock, flags); - return -EBUSY; - } - if (test_bit(EFFECT_PLAYING, effect->flags)) { - spin_unlock_irqrestore(&lgff->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(&lgff->lock, flags); - - return 0; - -} - -/* Erase all effects this process owns */ -static int hid_lgff_flush(struct input_dev *dev, struct file *file) -{ - struct hid_device *hid = dev->private; - struct lgff_device *lgff = 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 == lgff->effects[i].owner - && test_bit(EFFECT_USED, lgff->effects[i].flags)) { - - if (hid_lgff_erase(dev, i)) - warn("erase effect %d failed", i); - } - - } - - return 0; -} - -static int hid_lgff_erase(struct input_dev *dev, int id) -{ - struct hid_device *hid = dev->private; - struct lgff_device *lgff = hid->ff_private; - unsigned long flags; - - if (!LGFF_CHECK_OWNERSHIP(id, lgff)) return -EACCES; - - spin_lock_irqsave(&lgff->lock, flags); - lgff->effects[id].flags[0] = 0; - spin_unlock_irqrestore(&lgff->lock, flags); - - return 0; -} - -static int hid_lgff_upload_effect(struct input_dev* input, - struct ff_effect* effect) -{ - struct hid_device *hid = input->private; - struct lgff_device *lgff = hid->ff_private; - struct lgff_effect new; - int id; - unsigned long flags; - - dbg("ioctl rumble"); - - if (!test_bit(effect->type, input->ffbit)) return -EINVAL; - - spin_lock_irqsave(&lgff->lock, flags); - - if (effect->id == -1) { - int i; - - for (i=0; i<LGFF_EFFECTS && test_bit(EFFECT_USED, lgff->effects[i].flags); ++i); - if (i >= LGFF_EFFECTS) { - spin_unlock_irqrestore(&lgff->lock, flags); - return -ENOSPC; - } - - effect->id = i; - lgff->effects[i].owner = current->pid; - lgff->effects[i].flags[0] = 0; - set_bit(EFFECT_USED, lgff->effects[i].flags); - } - else if (!LGFF_CHECK_OWNERSHIP(effect->id, lgff)) { - spin_unlock_irqrestore(&lgff->lock, flags); - return -EACCES; - } - - id = effect->id; - new = lgff->effects[id]; - - new.effect = *effect; - - if (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 - being) */ - if (new.effect.replay.delay != lgff->effects[id].effect.replay.delay - || new.effect.replay.length != lgff->effects[id].effect.replay.length) { - spin_unlock_irqrestore(&lgff->lock, flags); - return -ENOSYS; - } - - lgff->effects[id] = new; - - } else { - lgff->effects[id] = new; - } - - spin_unlock_irqrestore(&lgff->lock, flags); - return 0; -} - -static void hid_lgff_timer(unsigned long timer_data) -{ - struct lgff_device *lgff = (struct lgff_device*)timer_data; - struct hid_device *hid = lgff->hid; - unsigned long flags; - int x = 0x7f, y = 0x7f; // Coordinates of constant effects - unsigned int left = 0, right = 0; // Rumbling - int i; - - spin_lock_irqsave(&lgff->lock, flags); - - for (i=0; i<LGFF_EFFECTS; ++i) { - struct lgff_effect* effect = lgff->effects +i; - - if (test_bit(EFFECT_PLAYING, effect->flags)) { - - switch (effect->effect.type) { - case 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)); - } 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); - } - else { - dbg("Start again %d", i); - 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, lgff->effects[i].flags)) { - /* Check if we should start playing the effect */ - 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); - } - } - } - -#define CLAMP(x) if (x < 0) x = 0; if (x > 0xff) x = 0xff - - // Clamp values - CLAMP(x); - CLAMP(y); - CLAMP(left); - CLAMP(right); - -#undef CLAMP - - if (x != lgff->constant->field[0]->value[2] - || y != lgff->constant->field[0]->value[3]) { - lgff->constant->field[0]->value[2] = x; - lgff->constant->field[0]->value[3] = y; - dbg("(x,y)=(%04x, %04x)", x, y); - hid_submit_report(hid, lgff->constant, USB_DIR_OUT); - } - - if (left != lgff->rumble->field[0]->value[2] - || right != lgff->rumble->field[0]->value[3]) { - lgff->rumble->field[0]->value[2] = left; - lgff->rumble->field[0]->value[3] = right; - dbg("(left,right)=(%04x, %04x)", left, right); - hid_submit_report(hid, lgff->rumble, USB_DIR_OUT); - } - - if (!test_bit(DEVICE_CLOSING, lgff->flags)) { - lgff->timer.expires = RUN_AT(PERIOD); - add_timer(&lgff->timer); - } - - spin_unlock_irqrestore(&lgff->lock, flags); -} |
