Home | History | Annotate | Download | only in linux
      1 /*
      2 The zlib/libpng License
      3 
      4 Copyright (c) 2005-2007 Phillip Castaneda (pjcast -- www.wreckedgames.com)
      5 
      6 This software is provided 'as-is', without any express or implied warranty. In no event will
      7 the authors be held liable for any damages arising from the use of this software.
      8 
      9 Permission is granted to anyone to use this software for any purpose, including commercial
     10 applications, and to alter it and redistribute it freely, subject to the following
     11 restrictions:
     12 
     13     1. The origin of this software must not be misrepresented; you must not claim that
     14 		you wrote the original software. If you use this software in a product,
     15 		an acknowledgment in the product documentation would be appreciated but is
     16 		not required.
     17 
     18     2. Altered source versions must be plainly marked as such, and must not be
     19 		misrepresented as being the original software.
     20 
     21     3. This notice may not be removed or altered from any source distribution.
     22 */
     23 #include "linux/LinuxForceFeedback.h"
     24 #include "OISException.h"
     25 
     26 #include <cstdlib>
     27 #include <errno.h>
     28 #include <memory.h>
     29 
     30 using namespace OIS;
     31 
     32 // 0 = No trace; 1 = Important traces; 2 = Debug traces
     33 #define OIS_LINUX_JOYFF_DEBUG 1
     34 
     35 #ifdef OIS_LINUX_JOYFF_DEBUG
     36 # include <iostream>
     37   using namespace std;
     38 #endif
     39 
     40 //--------------------------------------------------------------//
     41 LinuxForceFeedback::LinuxForceFeedback(int deviceID) :
     42 	ForceFeedback(), mJoyStick(deviceID)
     43 {
     44 }
     45 
     46 //--------------------------------------------------------------//
     47 LinuxForceFeedback::~LinuxForceFeedback()
     48 {
     49 	// Unload all effects.
     50 	for(EffectList::iterator i = mEffectList.begin(); i != mEffectList.end(); ++i )
     51 	{
     52 		struct ff_effect *linEffect = i->second;
     53 		if( linEffect )
     54 			_unload(linEffect->id);
     55 	}
     56 
     57 	mEffectList.clear();
     58 }
     59 
     60 //--------------------------------------------------------------//
     61 unsigned short LinuxForceFeedback::getFFMemoryLoad()
     62 {
     63 	int nEffects = -1;
     64 	if (ioctl(mJoyStick, EVIOCGEFFECTS, &nEffects) == -1)
     65 		OIS_EXCEPT(E_General, "Unknown error reading max number of uploaded effects.");
     66 #if (OIS_LINUX_JOYFF_DEBUG > 1)
     67 	cout << "LinuxForceFeedback("<< mJoyStick
     68 		 << ") : Read device max number of uploaded effects : " << nEffects << endl;
     69 #endif
     70 
     71 	return (unsigned short int)(nEffects > 0 ? 100.0*mEffectList.size()/nEffects : 100);
     72 }
     73 
     74 //--------------------------------------------------------------//
     75 void LinuxForceFeedback::setMasterGain(float value)
     76 {
     77 	if (!mSetGainSupport)
     78 	{
     79 #if (OIS_LINUX_JOYFF_DEBUG > 0)
     80 		cout << "LinuxForceFeedback("<< mJoyStick << ") : Setting master gain "
     81 			 << "is not supported by the device" << endl;
     82 #endif
     83 		return;
     84 	}
     85 
     86 	struct input_event event;
     87 
     88 	memset(&event, 0, sizeof(event));
     89 	event.type = EV_FF;
     90 	event.code = FF_GAIN;
     91 	if (value < 0.0)
     92 		value = 0.0;
     93 	else if (value > 1.0)
     94 		value = 1.0;
     95 	event.value = (__s32)(value * 0xFFFFUL);
     96 
     97 #if (OIS_LINUX_JOYFF_DEBUG > 0)
     98 	cout << "LinuxForceFeedback("<< mJoyStick << ") : Setting master gain to "
     99 		 << value << " => " << event.value << endl;
    100 #endif
    101 
    102 	if (write(mJoyStick, &event, sizeof(event)) != sizeof(event)) {
    103 		OIS_EXCEPT(E_General, "Unknown error changing master gain.");
    104 	}
    105 }
    106 
    107 //--------------------------------------------------------------//
    108 void LinuxForceFeedback::setAutoCenterMode(bool enabled)
    109 {
    110 	if (!mSetAutoCenterSupport)
    111 	{
    112 #if (OIS_LINUX_JOYFF_DEBUG > 0)
    113 		cout << "LinuxForceFeedback("<< mJoyStick << ") : Setting auto-center mode "
    114 			 << "is not supported by the device" << endl;
    115 #endif
    116 		return;
    117 	}
    118 
    119 	struct input_event event;
    120 
    121 	memset(&event, 0, sizeof(event));
    122 	event.type = EV_FF;
    123 	event.code = FF_AUTOCENTER;
    124 	event.value = (__s32)(enabled*0xFFFFFFFFUL);
    125 
    126 #if (OIS_LINUX_JOYFF_DEBUG > 0)
    127 	cout << "LinuxForceFeedback("<< mJoyStick << ") : Toggling auto-center to "
    128 		 << enabled << " => 0x" << hex << event.value << dec << endl;
    129 #endif
    130 
    131 	if (write(mJoyStick, &event, sizeof(event)) != sizeof(event)) {
    132 		OIS_EXCEPT(E_General, "Unknown error toggling auto-center.");
    133 	}
    134 }
    135 
    136 //--------------------------------------------------------------//
    137 void LinuxForceFeedback::upload( const Effect* effect )
    138 {
    139 	switch( effect->force )
    140 	{
    141 		case OIS::Effect::ConstantForce:
    142 			_updateConstantEffect(effect);
    143 			break;
    144 		case OIS::Effect::ConditionalForce:
    145 			_updateConditionalEffect(effect);
    146 			break;
    147 		case OIS::Effect::PeriodicForce:
    148 			_updatePeriodicEffect(effect);
    149 			break;
    150 		case OIS::Effect::RampForce:
    151 			_updateRampEffect(effect);
    152 			break;
    153 		case OIS::Effect::CustomForce:
    154 			//_updateCustomEffect(effect);
    155 			//break;
    156 		default:
    157 			OIS_EXCEPT(E_NotImplemented, "Requested force not implemented yet, sorry!");
    158 			break;
    159 	}
    160 }
    161 
    162 //--------------------------------------------------------------//
    163 void LinuxForceFeedback::modify( const Effect* effect )
    164 {
    165 	upload(effect);
    166 }
    167 
    168 //--------------------------------------------------------------//
    169 void LinuxForceFeedback::remove( const Effect* effect )
    170 {
    171 	//Get the effect - if it exists
    172 	EffectList::iterator i = mEffectList.find(effect->_handle);
    173 	if( i != mEffectList.end() )
    174 	{
    175 		struct ff_effect *linEffect = i->second;
    176 		if( linEffect )
    177 		{
    178 			_stop(effect->_handle);
    179 
    180 			_unload(effect->_handle);
    181 
    182 			free(linEffect);
    183 
    184 			mEffectList.erase(i);
    185 		}
    186 		else
    187 			mEffectList.erase(i);
    188 	}
    189 }
    190 
    191 //--------------------------------------------------------------//
    192 // To Signed16/Unsigned15 safe conversions
    193 #define MaxUnsigned15Value 0x7FFF
    194 #define toUnsigned15(value) \
    195 	(__u16)((value) < 0 ? 0 : ((value) > MaxUnsigned15Value ? MaxUnsigned15Value : (value)))
    196 
    197 #define MaxSigned16Value  0x7FFF
    198 #define MinSigned16Value -0x7FFF
    199 #define toSigned16(value) \
    200   (__s16)((value) < MinSigned16Value ? MinSigned16Value : ((value) > MaxSigned16Value ? MaxSigned16Value : (value)))
    201 
    202 // OIS to Linux duration
    203 #define LinuxInfiniteDuration 0xFFFF
    204 #define OISDurationUnitMS 1000 // OIS duration unit (microseconds), expressed in milliseconds (theLinux duration unit)
    205 
    206 // linux/input.h : All duration values are expressed in ms. Values above 32767 ms (0x7fff)
    207 //                 should not be used and have unspecified results.
    208 #define LinuxDuration(oisDuration) ((oisDuration) == Effect::OIS_INFINITE ? LinuxInfiniteDuration \
    209 									: toUnsigned15((oisDuration)/OISDurationUnitMS))
    210 
    211 
    212 // OIS to Linux levels
    213 #define OISMaxLevel 10000
    214 #define LinuxMaxLevel 0x7FFF
    215 
    216 // linux/input.h : Valid range for the attack and fade levels is 0x0000 - 0x7fff
    217 #define LinuxPositiveLevel(oisLevel) toUnsigned15(LinuxMaxLevel*(long)(oisLevel)/OISMaxLevel)
    218 
    219 #define LinuxSignedLevel(oisLevel) toSigned16(LinuxMaxLevel*(long)(oisLevel)/OISMaxLevel)
    220 
    221 
    222 //--------------------------------------------------------------//
    223 void LinuxForceFeedback::_setCommonProperties(struct ff_effect *event,
    224 											  struct ff_envelope *ffenvelope,
    225 											  const Effect* effect, const Envelope *envelope )
    226 {
    227 	memset(event, 0, sizeof(struct ff_effect));
    228 
    229 	if (envelope && ffenvelope && envelope->isUsed()) {
    230 		ffenvelope->attack_length = LinuxDuration(envelope->attackLength);
    231 		ffenvelope->attack_level = LinuxPositiveLevel(envelope->attackLevel);
    232 		ffenvelope->fade_length = LinuxDuration(envelope->fadeLength);
    233 		ffenvelope->fade_level = LinuxPositiveLevel(envelope->fadeLevel);
    234 	}
    235 
    236 #if (OIS_LINUX_JOYFF_DEBUG > 1)
    237 	cout << endl;
    238 	if (envelope && ffenvelope)
    239 	{
    240 		cout << "  Enveloppe :" << endl
    241 			 << "    AttackLen : " << envelope->attackLength
    242 			 << " => " << ffenvelope->attack_length << endl
    243 			 << "    AttackLvl : " << envelope->attackLevel
    244 			 << " => " << ffenvelope->attack_level << endl
    245 			 << "    FadeLen   : " << envelope->fadeLength
    246 			 << " => " << ffenvelope->fade_length << endl
    247 			 << "    FadeLvl   : " << envelope->fadeLevel
    248 			 << " => " << ffenvelope->fade_level << endl;
    249 	}
    250 #endif
    251 
    252 	event->direction = (__u16)(1 + (effect->direction*45.0+135.0)*0xFFFFUL/360.0);
    253 
    254 #if (OIS_LINUX_JOYFF_DEBUG > 1)
    255 	cout << "  Direction : " << Effect::getDirectionName(effect->direction)
    256 		 << " => 0x" << hex << event->direction << dec << endl;
    257 #endif
    258 
    259 	// TODO trigger_button 0 vs. -1
    260 	event->trigger.button = effect->trigger_button; // < 0 ? 0 : effect->trigger_button;
    261 	event->trigger.interval = LinuxDuration(effect->trigger_interval);
    262 
    263 #if (OIS_LINUX_JOYFF_DEBUG > 1)
    264 	cout << "  Trigger :" << endl
    265 		 << "    Button   : " << effect->trigger_button
    266 		 << " => " << event->trigger.button << endl
    267 		 << "    Interval : " << effect->trigger_interval
    268 		 << " => " << event->trigger.interval << endl;
    269 #endif
    270 
    271 	event->replay.length = LinuxDuration(effect->replay_length);
    272 	event->replay.delay = LinuxDuration(effect->replay_delay);
    273 
    274 #if (OIS_LINUX_JOYFF_DEBUG > 1)
    275 	cout << "  Replay :" << endl
    276 		 << "    Length : " << effect->replay_length
    277 		 << " => " << event->replay.length << endl
    278 		 << "    Delay  : " << effect->replay_delay
    279 		 << " => " << event->replay.delay << endl;
    280 #endif
    281 }
    282 
    283 //--------------------------------------------------------------//
    284 void LinuxForceFeedback::_updateConstantEffect( const Effect* eff )
    285 {
    286 	struct ff_effect event;
    287 
    288 	ConstantEffect *effect = static_cast<ConstantEffect*>(eff->getForceEffect());
    289 
    290 	_setCommonProperties(&event, &event.u.constant.envelope, eff, &effect->envelope);
    291 
    292 	event.type = FF_CONSTANT;
    293 	event.id = -1;
    294 
    295 	event.u.constant.level = LinuxSignedLevel(effect->level);
    296 
    297 #if (OIS_LINUX_JOYFF_DEBUG > 1)
    298 	cout << "  Level : " << effect->level
    299 		 << " => " << event.u.constant.level << endl;
    300 #endif
    301 
    302 	_upload(&event, eff);
    303 }
    304 
    305 //--------------------------------------------------------------//
    306 void LinuxForceFeedback::_updateRampEffect( const Effect* eff )
    307 {
    308 	struct ff_effect event;
    309 
    310 	RampEffect *effect = static_cast<RampEffect*>(eff->getForceEffect());
    311 
    312 	_setCommonProperties(&event, &event.u.constant.envelope, eff, &effect->envelope);
    313 
    314 	event.type = FF_RAMP;
    315 	event.id = -1;
    316 
    317 	event.u.ramp.start_level = LinuxSignedLevel(effect->startLevel);
    318 	event.u.ramp.end_level = LinuxSignedLevel(effect->endLevel);
    319 
    320 #if (OIS_LINUX_JOYFF_DEBUG > 1)
    321 	cout << "  StartLevel : " << effect->startLevel
    322 		 << " => " << event.u.ramp.start_level << endl
    323 		 << "  EndLevel   : " << effect->endLevel
    324 		 << " => " << event.u.ramp.end_level << endl;
    325 #endif
    326 
    327 	_upload(&event, eff);
    328 }
    329 
    330 //--------------------------------------------------------------//
    331 void LinuxForceFeedback::_updatePeriodicEffect( const Effect* eff )
    332 {
    333 	struct ff_effect event;
    334 
    335 	PeriodicEffect *effect = static_cast<PeriodicEffect*>(eff->getForceEffect());
    336 
    337 	_setCommonProperties(&event, &event.u.periodic.envelope, eff, &effect->envelope);
    338 
    339 	event.type = FF_PERIODIC;
    340 	event.id = -1;
    341 
    342 	switch( eff->type )
    343 	{
    344 		case OIS::Effect::Square:
    345 			event.u.periodic.waveform = FF_SQUARE;
    346 			break;
    347 		case OIS::Effect::Triangle:
    348 			event.u.periodic.waveform = FF_TRIANGLE;
    349 			break;
    350 		case OIS::Effect::Sine:
    351 			event.u.periodic.waveform = FF_SINE;
    352 			break;
    353 		case OIS::Effect::SawToothUp:
    354 			event.u.periodic.waveform = FF_SAW_UP;
    355 			break;
    356 		case OIS::Effect::SawToothDown:
    357 			event.u.periodic.waveform = FF_SAW_DOWN;
    358 			break;
    359 		// Note: No support for Custom periodic force effect for the moment
    360 		//case OIS::Effect::Custom:
    361 			//event.u.periodic.waveform = FF_CUSTOM;
    362 			//break;
    363 		default:
    364 			OIS_EXCEPT(E_General, "No such available effect for Periodic force!");
    365 			break;
    366 	}
    367 
    368 	event.u.periodic.period    = LinuxDuration(effect->period);
    369 	event.u.periodic.magnitude = LinuxPositiveLevel(effect->magnitude);
    370 	event.u.periodic.offset    = LinuxPositiveLevel(effect->offset);
    371 	event.u.periodic.phase     = (__u16)(effect->phase*event.u.periodic.period/36000.0); // ?????
    372 
    373 	// Note: No support for Custom periodic force effect for the moment
    374 	event.u.periodic.custom_len = 0;
    375 	event.u.periodic.custom_data = 0;
    376 
    377 #if (OIS_LINUX_JOYFF_DEBUG > 1)
    378 	cout << "  Magnitude : " << effect->magnitude
    379 		 << " => " << event.u.periodic.magnitude << endl
    380 		 << "  Period    : " << effect->period
    381 		 << " => " << event.u.periodic.period  << endl
    382 		 << "  Offset    : " << effect->offset
    383 		 << " => " << event.u.periodic.offset << endl
    384 		 << "  Phase     : " << effect->phase
    385 		 << " => " << event.u.periodic.phase << endl;
    386 #endif
    387 
    388 	_upload(&event, eff);
    389 }
    390 
    391 //--------------------------------------------------------------//
    392 void LinuxForceFeedback::_updateConditionalEffect( const Effect* eff )
    393 {
    394 	struct ff_effect event;
    395 
    396 	ConditionalEffect *effect = static_cast<ConditionalEffect*>(eff->getForceEffect());
    397 
    398 	_setCommonProperties(&event, NULL, eff, NULL);
    399 
    400 	switch( eff->type )
    401 	{
    402 		case OIS::Effect::Friction:
    403 			event.type = FF_FRICTION;
    404 			break;
    405 		case OIS::Effect::Damper:
    406 			event.type = FF_DAMPER;
    407 			break;
    408 		case OIS::Effect::Inertia:
    409 			event.type = FF_INERTIA;
    410 			break;
    411 		case OIS::Effect::Spring:
    412 			event.type = FF_SPRING;
    413 			break;
    414 		default:
    415 			OIS_EXCEPT(E_General, "No such available effect for Conditional force!");
    416 			break;
    417 	}
    418 
    419 	event.id = -1;
    420 
    421 	event.u.condition[0].right_saturation = LinuxSignedLevel(effect->rightSaturation);
    422 	event.u.condition[0].left_saturation  = LinuxSignedLevel(effect->leftSaturation);
    423 	event.u.condition[0].right_coeff      = LinuxSignedLevel(effect->rightCoeff);
    424 	event.u.condition[0].left_coeff       = LinuxSignedLevel(effect->leftCoeff);
    425 	event.u.condition[0].deadband         = LinuxPositiveLevel(effect->deadband);// Unit ??
    426 	event.u.condition[0].center           = LinuxSignedLevel(effect->center); // Unit ?? TODO ?
    427 
    428 	// TODO support for second condition
    429 	event.u.condition[1] = event.u.condition[0];
    430 
    431 #if (OIS_LINUX_JOYFF_DEBUG > 1)
    432 	cout << "  Condition[0] : " << endl
    433 		 << "    RightSaturation  : " << effect->rightSaturation
    434 		 << " => " << event.u.condition[0].right_saturation << endl
    435 		 << "    LeftSaturation   : " << effect->leftSaturation
    436 		 << " => " << event.u.condition[0]. left_saturation << endl
    437 		 << "    RightCoefficient : " << effect->rightCoeff
    438 		 << " => " << event.u.condition[0].right_coeff << endl
    439 		 << "    LeftCoefficient : " << effect->leftCoeff
    440 		 << " => " << event.u.condition[0].left_coeff << endl
    441 		 << "    DeadBand        : " << effect->deadband
    442 		 << " => " << event.u.condition[0].deadband  << endl
    443 		 << "    Center          : " << effect->center
    444 		 << " => " << event.u.condition[0].center << endl;
    445 	cout << "  Condition[1] : Not implemented" << endl;
    446 #endif
    447 	_upload(&event, eff);
    448 }
    449 
    450 //--------------------------------------------------------------//
    451 void LinuxForceFeedback::_upload( struct ff_effect* ffeffect, const Effect* effect)
    452 {
    453 	struct ff_effect *linEffect = 0;
    454 
    455 	//Get the effect - if it exists
    456 	EffectList::iterator i = mEffectList.find(effect->_handle);
    457 	//It has been created already
    458 	if( i != mEffectList.end() )
    459 		linEffect = i->second;
    460 
    461 	if( linEffect == 0 )
    462 	{
    463 #if (OIS_LINUX_JOYFF_DEBUG > 1)
    464 		cout << endl << "LinuxForceFeedback("<< mJoyStick << ") : Adding new effect : "
    465 			 << Effect::getEffectTypeName(effect->type) << endl;
    466 #endif
    467 
    468 		//This effect has not yet been created, so create it in the device
    469 		if (ioctl(mJoyStick, EVIOCSFF, ffeffect) == -1) {
    470 			// TODO device full check
    471 			// OIS_EXCEPT(E_DeviceFull, "Remove an effect before adding more!");
    472 			OIS_EXCEPT(E_General, "Unknown error creating effect (may be the device is full)->..");
    473 		}
    474 
    475 		// Save returned effect handle
    476 		effect->_handle = ffeffect->id;
    477 
    478 		// Save a copy of the uploaded effect for later simple modifications
    479 		linEffect = (struct ff_effect *)calloc(1, sizeof(struct ff_effect));
    480 		memcpy(linEffect, ffeffect, sizeof(struct ff_effect));
    481 
    482 		mEffectList[effect->_handle] = linEffect;
    483 
    484 		// Start playing the effect.
    485 		_start(effect->_handle);
    486 	}
    487 	else
    488 	{
    489 #if (OIS_LINUX_JOYFF_DEBUG > 1)
    490 		cout << endl << "LinuxForceFeedback("<< mJoyStick << ") : Replacing effect : "
    491 			 << Effect::getEffectTypeName(effect->type) << endl;
    492 #endif
    493 
    494 		// Keep same id/handle, as this is just an update in the device.
    495 		ffeffect->id = effect->_handle;
    496 
    497 		// Update effect in the device.
    498 		if (ioctl(mJoyStick, EVIOCSFF, ffeffect) == -1) {
    499 			OIS_EXCEPT(E_General, "Unknown error updating an effect->..");
    500 		}
    501 
    502 		// Update local linEffect for next time.
    503 		memcpy(linEffect, ffeffect, sizeof(struct ff_effect));
    504 	}
    505 
    506 #if (OIS_LINUX_JOYFF_DEBUG > 1)
    507 	cout << "LinuxForceFeedback("<< mJoyStick
    508 		 << ") : Effect handle : " << effect->_handle << endl;
    509 #endif
    510 }
    511 
    512 //--------------------------------------------------------------//
    513 void LinuxForceFeedback::_stop( int handle) {
    514 	struct input_event stop;
    515 
    516 	stop.type = EV_FF;
    517 	stop.code = handle;
    518 	stop.value = 0;
    519 
    520 #if (OIS_LINUX_JOYFF_DEBUG > 1)
    521 	cout << endl << "LinuxForceFeedback("<< mJoyStick
    522 		 << ") : Stopping effect with handle " << handle << endl;
    523 #endif
    524 
    525 	if (write(mJoyStick, &stop, sizeof(stop)) != sizeof(stop)) {
    526 		OIS_EXCEPT(E_General, "Unknown error stopping effect->..");
    527 	}
    528 }
    529 
    530 //--------------------------------------------------------------//
    531 void LinuxForceFeedback::_start( int handle) {
    532 	struct input_event play;
    533 
    534 	play.type = EV_FF;
    535 	play.code = handle;
    536 	play.value = 1; // Play once.
    537 
    538 #if (OIS_LINUX_JOYFF_DEBUG > 1)
    539 	cout << endl << "LinuxForceFeedback("<< mJoyStick
    540 		 << ") : Starting effect with handle " << handle << endl;
    541 #endif
    542 
    543 	if (write(mJoyStick, &play, sizeof(play)) != sizeof(play)) {
    544 		OIS_EXCEPT(E_General, "Unknown error playing effect->..");
    545 	}
    546 }
    547 
    548 //--------------------------------------------------------------//
    549 void LinuxForceFeedback::_unload( int handle)
    550 {
    551 #if (OIS_LINUX_JOYFF_DEBUG > 1)
    552 	cout << endl << "LinuxForceFeedback("<< mJoyStick
    553 		 << ") : Removing effect with handle " << handle << endl;
    554 #endif
    555 
    556 	if (ioctl(mJoyStick, EVIOCRMFF, handle) == -1) {
    557 		OIS_EXCEPT(E_General, "Unknown error removing effect->..");
    558 	}
    559 }
    560