Some work on force feedback

This commit is contained in:
Ingo Ruhnke 2009-01-23 20:00:02 +01:00
parent 6e2672e49a
commit 9256649810
6 changed files with 398 additions and 126 deletions

View file

@ -1,36 +1,37 @@
# -*- python -*-
env = Environment(CPPFLAGS=["-g", "-O2", "-Wall", "-ansi", "-pedantic"], LIBS=["usb", "X11"])
# env = Environment(CPPFLAGS=["-g", "-O2"], LIBS=["usb", "X11"])
env.Program("xboxdrv", ["src/xboxdrv.cpp",
"src/xboxmsg.cpp",
"src/uinput.cpp",
"src/helper.cpp",
"src/modifier.cpp",
"src/command_line_options.cpp",
"src/xbox_controller.cpp",
"src/xbox360_controller.cpp",
"src/xbox360_wireless_controller.cpp",
env = Environment(CPPFLAGS=['-g', '-O2', '-Wall', '-ansi', '-pedantic'], LIBS=['usb', 'X11'])
# env = Environment(CPPFLAGS=['-g', '-O2'], LIBS=['usb', 'X11'])
env.Program('xboxdrv', ['src/xboxdrv.cpp',
'src/xboxmsg.cpp',
'src/uinput.cpp',
'src/helper.cpp',
'src/modifier.cpp',
'src/command_line_options.cpp',
'src/xbox_controller.cpp',
'src/xbox360_controller.cpp',
'src/xbox360_wireless_controller.cpp',
'src/firestorm_dual_controller.cpp',
"src/evdev_helper.cpp",
"src/linux_uinput.cpp"
'src/evdev_helper.cpp',
'src/linux_uinput.cpp',
'src/force_feedback_handler.cpp'
])
if False:
env.Program("inputdrv",
["src/inputdrv/inputdrv.cpp",
"src/inputdrv/xbox360_driver.cpp",
"src/inputdrv/evdev_driver.cpp",
"src/inputdrv/xbox360_usb_thread.cpp",
"src/inputdrv/control.cpp",
"src/inputdrv/abs_to_rel.cpp",
"src/inputdrv/abs_to_btn.cpp",
"src/inputdrv/btn_to_abs.cpp",
"src/inputdrv/autofire_button.cpp",
"src/inputdrv/uinput_driver.cpp",
"src/inputdrv/join_axis.cpp",
"src/inputdrv/throttle.cpp",
"src/inputdrv/toggle_button.cpp"],
env.Program('inputdrv',
['src/inputdrv/inputdrv.cpp',
'src/inputdrv/xbox360_driver.cpp',
'src/inputdrv/evdev_driver.cpp',
'src/inputdrv/xbox360_usb_thread.cpp',
'src/inputdrv/control.cpp',
'src/inputdrv/abs_to_rel.cpp',
'src/inputdrv/abs_to_btn.cpp',
'src/inputdrv/btn_to_abs.cpp',
'src/inputdrv/autofire_button.cpp',
'src/inputdrv/uinput_driver.cpp',
'src/inputdrv/join_axis.cpp',
'src/inputdrv/throttle.cpp',
'src/inputdrv/toggle_button.cpp'],
LIBS=['boost_signals', 'usb', 'pthread'])
# EOF #

2
TODO
View file

@ -36,7 +36,7 @@ Stuff to do before 0.5 release:
fixme:dinput:joy_polldev joystick cannot handle type 21 event (code 0) <- 21 == EV_FF (status report?)
http://www.immersion.com/developer/downloads/ImmFundamentals/HTML/
http://msdn.microsoft.com/en-us/library/bb219655(VS.85).aspx
Later versions:
===============

View file

@ -0,0 +1,250 @@
/*
** Xbox360 USB Gamepad Userspace Driver
** Copyright (C) 2008 Ingo Ruhnke <grumbel@gmx.de>
**
** 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 3 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, see <http://www.gnu.org/licenses/>.
*/
#include <iostream>
#include <assert.h>
#include "force_feedback_handler.hpp"
std::ostream& operator<<(std::ostream& out, const struct ff_envelope& envelope)
{
out << "Envelope(attack_length:" << envelope.attack_length
<< ", attack_level:" << envelope.attack_level
<< ", fade_length:" << envelope.fade_length
<< ", fade_level:" << envelope.fade_level << ")";
return out;
}
std::ostream& operator<<(std::ostream& out, const struct ff_replay& replay)
{
out << "Replay(length:" << replay.length << ", delay:" << replay.delay << ")";
return out;
}
std::ostream& operator<<(std::ostream& out, const struct ff_trigger& trigger)
{
out << "Trigger(button:" << trigger.button << ", interval:" << trigger.interval << ")";
return out;
}
std::ostream& operator<<(std::ostream& out, const struct ff_effect& effect)
{
std::cout << "Effect(";
switch (effect.type)
{
case FF_CONSTANT:
out << "FF_CONSTANT("
<< "level:" << effect.u.constant.level
<< ", envelope:" << effect.u.constant.envelope << ")";
break;
case FF_PERIODIC:
out << "FF_PERIODIC("
<< ", waveform:" << effect.u.periodic.waveform
<< ", period:" << effect.u.periodic.period
<< ", magnitude:" << effect.u.periodic.magnitude
<< ", offset:" << effect.u.periodic.offset
<< ", phase:" << effect.u.periodic.phase
<< ", envelope:" << effect.u.periodic.envelope << ")";
break;
case FF_RAMP:
out << "FF_RAMP("
<< "start_level:" << effect.u.ramp.start_level
<< ", end_level:" << effect.u.ramp.end_level
<< ", envelope:" << effect.u.ramp.envelope << ")";
break;
case FF_SPRING:
out << "FF_SPRING()";
break;
case FF_FRICTION:
out << "FF_FRICTION()";
break;
case FF_DAMPER:
out << "FF_DAMPER()";
break;
case FF_RUMBLE:
out << "FF_RUMBLE("
<< "strong_magnitude:" << effect.u.rumble.strong_magnitude
<< ", weak_magnitude:" << effect.u.rumble.weak_magnitude << ")";
break;
case FF_INERTIA:
out << "FF_INERTIA()";
break;
case FF_CUSTOM:
out << "FF_CUSTOM()";
break;
default:
out << "FF_<unknown>()";
break;
}
out << ", direction:" << effect.direction
<< ", replay:" << effect.replay
<< ", trigger:" << effect.trigger << ")";
return out;
}
ForceFeedbackEffect::ForceFeedbackEffect(const struct ff_effect& effect)
{
delay = effect.replay.delay;
length = effect.replay.length;
switch(effect.type)
{
case FF_CONSTANT:
start_weak_magnitude = effect.u.constant.level;
start_strong_magnitude = effect.u.constant.level;
end_weak_magnitude = effect.u.constant.level;
end_strong_magnitude = effect.u.constant.level;
envelope = effect.u.constant.envelope;
break;
case FF_PERIODIC:
start_weak_magnitude = effect.u.periodic.magnitude;
start_strong_magnitude = effect.u.periodic.magnitude;
end_weak_magnitude = effect.u.periodic.magnitude;
end_strong_magnitude = effect.u.periodic.magnitude;
envelope = effect.u.periodic.envelope;
break;
case FF_RAMP:
start_weak_magnitude = effect.u.ramp.start_level;
start_strong_magnitude = effect.u.ramp.start_level;
end_weak_magnitude = effect.u.ramp.end_level;
end_strong_magnitude = effect.u.ramp.end_level;
envelope = effect.u.ramp.envelope;
break;
case FF_RUMBLE:
start_weak_magnitude = effect.u.rumble.weak_magnitude;
start_strong_magnitude = effect.u.rumble.strong_magnitude;
end_weak_magnitude = effect.u.rumble.weak_magnitude;
end_strong_magnitude = effect.u.rumble.strong_magnitude;
break;
default:
// Unsupported effects
// case FF_SPRING:
// case FF_FRICTION:
// case FF_DAMPER
// case FF_INERTIA:
// case FF_CUSTOM:
assert(!"Unsupported effect");
break;
}
}
ForceFeedbackHandler::ForceFeedbackHandler()
: max_effects(16),
weak_magnitude(0),
strong_magnitude(0)
{
}
ForceFeedbackHandler::~ForceFeedbackHandler()
{
}
int
ForceFeedbackHandler::get_max_effects()
{
return max_effects;
}
void
ForceFeedbackHandler::upload(const struct ff_effect& effect)
{
std::cout << "FF_UPLOAD("
<< "effect_id:" << effect.id
<< ", effect_type:" << effect.type
<< ",\n " << effect
<< ")" << std::endl;
effects[effect.id] = ForceFeedbackEffect(effect);
}
void
ForceFeedbackHandler::erase(int id)
{
std::cout << "FF_ERASE(effect_id:" << id << ")" << std::endl;
std::map<int, ForceFeedbackEffect>::iterator i = effects.find(id);
if (i != effects.end())
effects.erase(i);
else
std::cout << "ForceFeedbackHandler::erase: Unknown id " << id << std::endl;
}
void
ForceFeedbackHandler::play(int id)
{
std::cout << "FFPlay(effect_id:" << id << ")" << std::endl;
std::map<int, ForceFeedbackEffect>::iterator i = effects.find(id);
if (i != effects.end())
; // play
else
std::cout << "ForceFeedbackHandler::play: Unknown id " << id << std::endl;
}
void
ForceFeedbackHandler::stop(int id)
{
std::cout << "FFStop(effect_id:" << id << ")" << std::endl;
std::map<int, ForceFeedbackEffect>::iterator i = effects.find(id);
if (i != effects.end())
; // stop
else
std::cout << "ForceFeedbackHandler::play: Unknown id " << id << std::endl;
}
void
ForceFeedbackHandler::update(int msec_delta)
{
for(Effects::iterator i = effects.begin(); i != effects.end(); ++i)
{
}
}
int
ForceFeedbackHandler::get_weak_magnitude() const
{
return weak_magnitude;
}
int
ForceFeedbackHandler::get_strong_magnitude() const
{
return strong_magnitude;
}
/* EOF */

View file

@ -0,0 +1,102 @@
/*
** Xbox360 USB Gamepad Userspace Driver
** Copyright (C) 2008 Ingo Ruhnke <grumbel@gmx.de>
**
** 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 3 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, see <http://www.gnu.org/licenses/>.
*/
#ifndef HEADER_FF_HANDLER_HPP
#define HEADER_FF_HANDLER_HPP
#include <linux/input.h>
#include <map>
class ForceFeedbackEffect
{
public:
ForceFeedbackEffect() {}
ForceFeedbackEffect(const struct ff_effect& e);
// Delay before the effect start
int delay;
// Length of the effect
int length;
// Rumble motor strength
int start_strong_magnitude;
int start_weak_magnitude;
// Rumble motor strength
int end_strong_magnitude;
int end_weak_magnitude;
// Envelope
struct Envelope
{
Envelope()
: attack_length(0),
attack_level(0),
fade_length(0),
fade_level(0)
{}
Envelope(const struct ff_envelope& e)
: attack_length(e.attack_length),
attack_level(e.attack_level),
fade_length(e.fade_length),
fade_level(e.fade_level)
{
}
int attack_length;
int attack_level;
int fade_length;
int fade_level;
} envelope;
};
/** */
class ForceFeedbackHandler
{
private:
int max_effects;
typedef std::map<int, ForceFeedbackEffect> Effects;
Effects effects;
int weak_magnitude;
int strong_magnitude;
public:
ForceFeedbackHandler();
~ForceFeedbackHandler();
int get_max_effects();
void upload(const struct ff_effect& effect);
void erase(int id);
void play(int id);
void stop(int id);
void update(int msec_delta);
int get_weak_magnitude() const;
int get_strong_magnitude() const;
};
#endif
/* EOF */

View file

@ -16,6 +16,7 @@
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <assert.h>
#include <iostream>
#include <sstream>
#include <stdexcept>
@ -27,95 +28,9 @@
#include <sys/stat.h>
#include <fcntl.h>
#include "evdev_helper.hpp"
#include "force_feedback_handler.hpp"
#include "linux_uinput.hpp"
std::ostream& operator<<(std::ostream& out, const struct ff_envelope& envelope)
{
out << "Envelope(attack_length:" << envelope.attack_length
<< ", attack_level:" << envelope.attack_level
<< ", fade_length:" << envelope.fade_length
<< ", fade_level:" << envelope.fade_level << ")";
return out;
}
std::ostream& operator<<(std::ostream& out, const struct ff_replay& replay)
{
out << "Replay(length:" << replay.length << ", delay:" << replay.delay << ")";
return out;
}
std::ostream& operator<<(std::ostream& out, const struct ff_trigger& trigger)
{
out << "Trigger(button:" << trigger.button << ", interval:" << trigger.interval << ")";
return out;
}
std::ostream& operator<<(std::ostream& out, const struct ff_effect& effect)
{
std::cout << "Effect(";
switch (effect.type)
{
case FF_CONSTANT:
out << "FF_CONSTANT("
<< "level:" << effect.u.constant.level
<< ", envelope:" << effect.u.constant.envelope << ")";
break;
case FF_PERIODIC:
out << "FF_PERIODIC("
<< ", waveform:" << effect.u.periodic.waveform
<< ", period:" << effect.u.periodic.period
<< ", magnitude:" << effect.u.periodic.magnitude
<< ", offset:" << effect.u.periodic.offset
<< ", phase:" << effect.u.periodic.phase
<< ", envelope:" << effect.u.periodic.envelope << ")";
break;
case FF_RAMP:
out << "FF_RAMP("
<< "start_level:" << effect.u.ramp.start_level
<< ", end_level:" << effect.u.ramp.end_level
<< ", envelope:" << effect.u.ramp.envelope << ")";
break;
case FF_SPRING:
out << "FF_SPRING()";
break;
case FF_FRICTION:
out << "FF_FRICTION()";
break;
case FF_DAMPER:
out << "FF_DAMPER()";
break;
case FF_RUMBLE:
out << "FF_RUMBLE("
<< "strong_magnitude:" << effect.u.rumble.strong_magnitude
<< ", weak_magnitude:" << effect.u.rumble.weak_magnitude << ")";
break;
case FF_INERTIA:
out << "FF_INERTIA()";
break;
case FF_CUSTOM:
out << "FF_CUSTOM()";
break;
default:
out << "FF_<unknown>()";
break;
}
out << ", direction:" << effect.direction
<< ", replay:" << effect.replay
<< ", trigger:" << effect.trigger << ")";
return out;
}
LinuxUinput::LinuxUinput(const std::string& name, uint16_t vendor, uint16_t product)
: name(name),
vendor(vendor),
@ -125,7 +40,8 @@ LinuxUinput::LinuxUinput(const std::string& name, uint16_t vendor, uint16_t prod
rel_bit(false),
abs_bit(false),
led_bit(false),
ff_bit(false)
ff_bit(false),
ff_handler(0)
{
std::fill_n(abs_lst, ABS_CNT, false);
std::fill_n(rel_lst, REL_CNT, false);
@ -241,6 +157,8 @@ LinuxUinput::add_ff(uint16_t code)
{
ioctl(fd, UI_SET_EVBIT, EV_FF);
ff_bit = true;
assert(ff_handler == 0);
ff_handler = new ForceFeedbackHandler();
}
ioctl(fd, UI_SET_FFBIT, code);
@ -295,6 +213,7 @@ LinuxUinput::update(int msec_delta)
{
if (ff_bit)
{
assert(ff_handler);
struct input_event ev;
int ret = read(fd, &ev, sizeof(ev));
@ -318,7 +237,10 @@ LinuxUinput::update(int msec_delta)
break;
case EV_FF:
std::cout << "FFPlay(effect_id:" << ev.code << ", value:" << ev.value << ")" << std::endl;
if (ev.value)
ff_handler->play(ev.code);
else
ff_handler->stop(ev.code);
break;
case EV_UINPUT:
@ -335,12 +257,7 @@ LinuxUinput::update(int msec_delta)
upload.request_id = ev.value;
ioctl(fd, UI_BEGIN_FF_UPLOAD, &upload);
std::cout << "FF_UPLOAD("
<< "effect_id:" << upload.effect.id
<< ", effect_type:" << upload.effect.type
<< ",\n " << upload.effect
<< ")" << std::endl;
ff_handler->upload(upload.effect);
upload.retval = 0;
ioctl(fd, UI_END_FF_UPLOAD, &upload);
@ -358,8 +275,7 @@ LinuxUinput::update(int msec_delta)
erase.request_id = ev.value;
ioctl(fd, UI_BEGIN_FF_ERASE, &erase);
std::cout << "FF_ERASE(effect_id:" << erase.effect_id << ")" << std::endl;
ff_handler->erase(erase.effect_id);
erase.retval = 0;
ioctl(fd, UI_END_FF_ERASE, &erase);

View file

@ -23,7 +23,8 @@
#include <string>
#include <stdint.h>
/** */
class ForceFeedbackHandler;
class LinuxUinput
{
private:
@ -44,6 +45,8 @@ private:
bool key_lst[KEY_CNT];
bool ff_lst[FF_CNT];
ForceFeedbackHandler* ff_handler;
public:
LinuxUinput(const std::string& name, uint16_t vendor, uint16_t product);
~LinuxUinput();