/src/ois/src/linux/LinuxForceFeedback.cpp
C++ | 559 lines | 394 code | 96 blank | 69 comment | 37 complexity | 077dec65f3adf49c4034a80dd9ad4197 MD5 | raw file
1/* 2The zlib/libpng License 3 4Copyright (c) 2005-2007 Phillip Castaneda (pjcast -- www.wreckedgames.com) 5 6This software is provided 'as-is', without any express or implied warranty. In no event will 7the authors be held liable for any damages arising from the use of this software. 8 9Permission is granted to anyone to use this software for any purpose, including commercial 10applications, and to alter it and redistribute it freely, subject to the following 11restrictions: 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 30using 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//--------------------------------------------------------------// 41LinuxForceFeedback::LinuxForceFeedback(int deviceID) : 42 ForceFeedback(), mJoyStick(deviceID) 43{ 44} 45 46//--------------------------------------------------------------// 47LinuxForceFeedback::~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//--------------------------------------------------------------// 61unsigned 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//--------------------------------------------------------------// 75void 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//--------------------------------------------------------------// 108void 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//--------------------------------------------------------------// 137void 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//--------------------------------------------------------------// 163void LinuxForceFeedback::modify( const Effect* effect ) 164{ 165 upload(effect); 166} 167 168//--------------------------------------------------------------// 169void 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//--------------------------------------------------------------// 223void 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//--------------------------------------------------------------// 284void 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//--------------------------------------------------------------// 306void 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//--------------------------------------------------------------// 331void 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//--------------------------------------------------------------// 392void 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//--------------------------------------------------------------// 451void 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//--------------------------------------------------------------// 513void 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//--------------------------------------------------------------// 531void 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//--------------------------------------------------------------// 549void 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}