diff options
author | Dave Jones <davej@redhat.com> | 2006-12-12 18:13:32 -0500 |
---|---|---|
committer | Dave Jones <davej@redhat.com> | 2006-12-12 18:13:32 -0500 |
commit | f0eef25339f92f7cd4aeea23d9ae97987a5a1e82 (patch) | |
tree | 2472e94d39f43a9580a6d2d5d92de0b749023263 /drivers/usb/input/hid-tmff.c | |
parent | 0cfea5dd98205f2fa318836da664a7d7df1afbc1 (diff) | |
parent | e1036502e5263851259d147771226161e5ccc85a (diff) |
Merge ../linus
Diffstat (limited to 'drivers/usb/input/hid-tmff.c')
-rw-r--r-- | drivers/usb/input/hid-tmff.c | 402 |
1 files changed, 43 insertions, 359 deletions
diff --git a/drivers/usb/input/hid-tmff.c b/drivers/usb/input/hid-tmff.c index 534425c69c0..ab67331620d 100644 --- a/drivers/usb/input/hid-tmff.c +++ b/drivers/usb/input/hid-tmff.c @@ -28,97 +28,66 @@ */ #include <linux/input.h> -#include <linux/sched.h> #undef DEBUG #include <linux/usb.h> -#include <linux/circ_buf.h> - -#include "hid.h" -#include "fixp-arith.h" +#include <linux/hid.h> +#include "usbhid.h" /* Usages for thrustmaster devices I know about */ #define THRUSTMASTER_USAGE_RUMBLE_LR (HID_UP_GENDESK | 0xbb) -#define DELAY_CALC(t,delay) ((t) + (delay)*HZ/1000) - -/* Effect status */ -#define EFFECT_STARTED 0 /* Effect is going to play after some time */ -#define EFFECT_PLAYING 1 /* Effect is playing */ -#define EFFECT_USED 2 - -/* For tmff_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 TMFF_CHECK_ID(id) ((id) >= 0 && (id) < TMFF_EFFECTS) -#define TMFF_CHECK_OWNERSHIP(i, l) \ - (test_bit(EFFECT_USED, l->effects[i].flags) \ - && CHECK_OWNERSHIP(l->effects[i])) - -#define TMFF_EFFECTS 8 - -struct tmff_effect { - pid_t owner; - - struct ff_effect effect; - - unsigned long flags[1]; - unsigned int count; /* Number of times left to play */ - - unsigned long play_at; /* When the effect starts to play */ - unsigned long stop_at; /* When the effect ends */ -}; struct tmff_device { - struct hid_device *hid; - struct hid_report *report; - struct hid_field *rumble; +}; - unsigned int effects_playing; - struct tmff_effect effects[TMFF_EFFECTS]; - spinlock_t lock; /* device-level lock. Having locks on - a per-effect basis could be nice, but - isn't really necessary */ +/* Changes values from 0 to 0xffff into values from minimum to maximum */ +static inline int hid_tmff_scale(unsigned int in, int minimum, int maximum) +{ + int ret; - unsigned long flags[1]; /* Contains various information about the - state of the driver for this device */ + ret = (in * (maximum - minimum) / 0xffff) + minimum; + if (ret < minimum) + return minimum; + if (ret > maximum) + return maximum; + return ret; +} - struct timer_list timer; -}; +static int hid_tmff_play(struct input_dev *dev, void *data, struct ff_effect *effect) +{ + struct hid_device *hid = dev->private; + struct tmff_device *tmff = data; + int left, right; /* Rumbling */ -/* Callbacks */ -static void hid_tmff_exit(struct hid_device *hid); -static int hid_tmff_event(struct hid_device *hid, struct input_dev *input, - unsigned int type, unsigned int code, int value); -static int hid_tmff_flush(struct input_dev *input, struct file *file); -static int hid_tmff_upload_effect(struct input_dev *input, - struct ff_effect *effect); -static int hid_tmff_erase(struct input_dev *input, int id); + left = hid_tmff_scale(effect->u.rumble.weak_magnitude, + tmff->rumble->logical_minimum, tmff->rumble->logical_maximum); + right = hid_tmff_scale(effect->u.rumble.strong_magnitude, + tmff->rumble->logical_minimum, tmff->rumble->logical_maximum); -/* Local functions */ -static void hid_tmff_recalculate_timer(struct tmff_device *tmff); -static void hid_tmff_timer(unsigned long timer_data); + tmff->rumble->value[0] = left; + tmff->rumble->value[1] = right; + dbg("(left,right)=(%08x, %08x)", left, right); + usbhid_submit_report(hid, tmff->report, USB_DIR_OUT); + + return 0; +} int hid_tmff_init(struct hid_device *hid) { - struct tmff_device *private; + struct tmff_device *tmff; struct list_head *pos; struct hid_input *hidinput = list_entry(hid->inputs.next, struct hid_input, list); struct input_dev *input_dev = hidinput->input; + int error; - private = kzalloc(sizeof(struct tmff_device), GFP_KERNEL); - if (!private) + tmff = kzalloc(sizeof(struct tmff_device), GFP_KERNEL); + if (!tmff) return -ENOMEM; - hid->ff_private = private; - /* Find the report to use */ __list_for_each(pos, &hid->report_enum[HID_OUTPUT_REPORT].report_list) { struct hid_report *report = (struct hid_report *)pos; @@ -142,18 +111,18 @@ int hid_tmff_init(struct hid_device *hid) continue; } - if (private->report && private->report != report) { + if (tmff->report && tmff->report != report) { warn("ignoring THRUSTMASTER_USAGE_RUMBLE_LR in other report"); continue; } - if (private->rumble && private->rumble != field) { + if (tmff->rumble && tmff->rumble != field) { warn("ignoring duplicate THRUSTMASTER_USAGE_RUMBLE_LR"); continue; } - private->report = report; - private->rumble = field; + tmff->report = report; + tmff->rumble = field; set_bit(FF_RUMBLE, input_dev->ffbit); break; @@ -162,302 +131,17 @@ int hid_tmff_init(struct hid_device *hid) warn("ignoring unknown output usage %08x", field->usage[0].hid); continue; } - - /* Fallthrough to here only when a valid usage is found */ - input_dev->upload_effect = hid_tmff_upload_effect; - input_dev->flush = hid_tmff_flush; - - set_bit(EV_FF, input_dev->evbit); - input_dev->ff_effects_max = TMFF_EFFECTS; } } - private->hid = hid; - - spin_lock_init(&private->lock); - init_timer(&private->timer); - private->timer.data = (unsigned long)private; - private->timer.function = hid_tmff_timer; - - /* Event and exit callbacks */ - hid->ff_exit = hid_tmff_exit; - hid->ff_event = hid_tmff_event; - - info("Force feedback for ThrustMaster rumble pad devices by Zinx Verituse <zinx@epicsol.org>"); - - return 0; -} - -static void hid_tmff_exit(struct hid_device *hid) -{ - struct tmff_device *tmff = hid->ff_private; - unsigned long flags; - - spin_lock_irqsave(&tmff->lock, flags); - - set_bit(DEVICE_CLOSING, tmff->flags); - del_timer_sync(&tmff->timer); - - spin_unlock_irqrestore(&tmff->lock, flags); - - kfree(tmff); -} - -static int hid_tmff_event(struct hid_device *hid, struct input_dev *input, - unsigned int type, unsigned int code, int value) -{ - struct tmff_device *tmff = hid->ff_private; - struct tmff_effect *effect = &tmff->effects[code]; - unsigned long flags; - - if (type != EV_FF) - return -EINVAL; - if (!TMFF_CHECK_ID(code)) - return -EINVAL; - if (!TMFF_CHECK_OWNERSHIP(code, tmff)) - return -EACCES; - if (value < 0) - return -EINVAL; - - spin_lock_irqsave(&tmff->lock, flags); - - if (value > 0) { - set_bit(EFFECT_STARTED, effect->flags); - clear_bit(EFFECT_PLAYING, effect->flags); - effect->count = value; - effect->play_at = DELAY_CALC(jiffies, effect->effect.replay.delay); - } else { - clear_bit(EFFECT_STARTED, effect->flags); - clear_bit(EFFECT_PLAYING, effect->flags); - } - - hid_tmff_recalculate_timer(tmff); - - spin_unlock_irqrestore(&tmff->lock, flags); - - return 0; - -} - -/* Erase all effects this process owns */ - -static int hid_tmff_flush(struct input_dev *dev, struct file *file) -{ - struct hid_device *hid = dev->private; - struct tmff_device *tmff = 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 == tmff->effects[i].owner - && test_bit(EFFECT_USED, tmff->effects[i].flags)) - if (hid_tmff_erase(dev, i)) - warn("erase effect %d failed", i); - - - return 0; -} - -static int hid_tmff_erase(struct input_dev *dev, int id) -{ - struct hid_device *hid = dev->private; - struct tmff_device *tmff = hid->ff_private; - unsigned long flags; - - if (!TMFF_CHECK_ID(id)) - return -EINVAL; - if (!TMFF_CHECK_OWNERSHIP(id, tmff)) - return -EACCES; - - spin_lock_irqsave(&tmff->lock, flags); - - tmff->effects[id].flags[0] = 0; - hid_tmff_recalculate_timer(tmff); - - spin_unlock_irqrestore(&tmff->lock, flags); - - return 0; -} - -static int hid_tmff_upload_effect(struct input_dev *input, - struct ff_effect *effect) -{ - struct hid_device *hid = input->private; - struct tmff_device *tmff = hid->ff_private; - int id; - unsigned long flags; - - if (!test_bit(effect->type, input->ffbit)) - return -EINVAL; - if (effect->id != -1 && !TMFF_CHECK_ID(effect->id)) - return -EINVAL; - - spin_lock_irqsave(&tmff->lock, flags); - - if (effect->id == -1) { - /* Find a free effect */ - for (id = 0; id < TMFF_EFFECTS && test_bit(EFFECT_USED, tmff->effects[id].flags); ++id); - - if (id >= TMFF_EFFECTS) { - spin_unlock_irqrestore(&tmff->lock, flags); - return -ENOSPC; - } - - effect->id = id; - tmff->effects[id].owner = current->pid; - tmff->effects[id].flags[0] = 0; - set_bit(EFFECT_USED, tmff->effects[id].flags); - - } else { - /* Re-uploading an owned effect, to change parameters */ - id = effect->id; - clear_bit(EFFECT_PLAYING, tmff->effects[id].flags); + error = input_ff_create_memless(input_dev, tmff, hid_tmff_play); + if (error) { + kfree(tmff); + return error; } - tmff->effects[id].effect = *effect; - - hid_tmff_recalculate_timer(tmff); + info("Force feedback for ThrustMaster rumble pad devices by Zinx Verituse <zinx@epicsol.org>"); - spin_unlock_irqrestore(&tmff->lock, flags); return 0; } -/* Start the timer for the next start/stop/delay */ -/* Always call this while tmff->lock is locked */ - -static void hid_tmff_recalculate_timer(struct tmff_device *tmff) -{ - int i; - int events = 0; - unsigned long next_time; - - next_time = 0; /* Shut up compiler's incorrect warning */ - - /* Find the next change in an effect's status */ - for (i = 0; i < TMFF_EFFECTS; ++i) { - struct tmff_effect *effect = &tmff->effects[i]; - unsigned long play_time; - - if (!test_bit(EFFECT_STARTED, effect->flags)) - continue; - - effect->stop_at = DELAY_CALC(effect->play_at, effect->effect.replay.length); - - if (!test_bit(EFFECT_PLAYING, effect->flags)) - play_time = effect->play_at; - else - play_time = effect->stop_at; - - events++; - - if (time_after(jiffies, play_time)) - play_time = jiffies; - - if (events == 1) - next_time = play_time; - else { - if (time_after(next_time, play_time)) - next_time = play_time; - } - } - - if (!events && tmff->effects_playing) { - /* Treat all effects turning off as an event */ - events = 1; - next_time = jiffies; - } - - if (!events) { - /* No events, no time, no need for a timer. */ - del_timer_sync(&tmff->timer); - return; - } - - mod_timer(&tmff->timer, next_time); -} - -/* Changes values from 0 to 0xffff into values from minimum to maximum */ -static inline int hid_tmff_scale(unsigned int in, int minimum, int maximum) -{ - int ret; - - ret = (in * (maximum - minimum) / 0xffff) + minimum; - if (ret < minimum) - return minimum; - if (ret > maximum) - return maximum; - return ret; -} - -static void hid_tmff_timer(unsigned long timer_data) -{ - struct tmff_device *tmff = (struct tmff_device *) timer_data; - struct hid_device *hid = tmff->hid; - unsigned long flags; - int left = 0, right = 0; /* Rumbling */ - int i; - - spin_lock_irqsave(&tmff->lock, flags); - - tmff->effects_playing = 0; - - for (i = 0; i < TMFF_EFFECTS; ++i) { - struct tmff_effect *effect = &tmff->effects[i]; - - if (!test_bit(EFFECT_STARTED, effect->flags)) - continue; - - if (!time_after(jiffies, effect->play_at)) - continue; - - if (time_after(jiffies, effect->stop_at)) { - - dbg("Finished playing once %d", i); - clear_bit(EFFECT_PLAYING, effect->flags); - - if (--effect->count <= 0) { - dbg("Stopped %d", i); - clear_bit(EFFECT_STARTED, effect->flags); - continue; - } else { - dbg("Start again %d", i); - effect->play_at = DELAY_CALC(jiffies, effect->effect.replay.delay); - continue; - } - } - - ++tmff->effects_playing; - - set_bit(EFFECT_PLAYING, effect->flags); - - switch (effect->effect.type) { - case FF_RUMBLE: - right += effect->effect.u.rumble.strong_magnitude; - left += effect->effect.u.rumble.weak_magnitude; - break; - default: - BUG(); - break; - } - } - - left = hid_tmff_scale(left, tmff->rumble->logical_minimum, tmff->rumble->logical_maximum); - right = hid_tmff_scale(right, tmff->rumble->logical_minimum, tmff->rumble->logical_maximum); - - if (left != tmff->rumble->value[0] || right != tmff->rumble->value[1]) { - tmff->rumble->value[0] = left; - tmff->rumble->value[1] = right; - dbg("(left,right)=(%08x, %08x)", left, right); - hid_submit_report(hid, tmff->report, USB_DIR_OUT); - } - - if (!test_bit(DEVICE_CLOSING, tmff->flags)) - hid_tmff_recalculate_timer(tmff); - - spin_unlock_irqrestore(&tmff->lock, flags); -} |