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 //--------------------------------------------------------------//
LinuxForceFeedback(int deviceID)41 LinuxForceFeedback::LinuxForceFeedback(int deviceID) :
42 ForceFeedback(), mJoyStick(deviceID)
43 {
44 }
45
46 //--------------------------------------------------------------//
~LinuxForceFeedback()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 //--------------------------------------------------------------//
getFFMemoryLoad()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 //--------------------------------------------------------------//
setMasterGain(float value)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 //--------------------------------------------------------------//
setAutoCenterMode(bool enabled)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 //--------------------------------------------------------------//
upload(const Effect * effect)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 //--------------------------------------------------------------//
modify(const Effect * effect)163 void LinuxForceFeedback::modify( const Effect* effect )
164 {
165 upload(effect);
166 }
167
168 //--------------------------------------------------------------//
remove(const Effect * effect)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 //--------------------------------------------------------------//
_setCommonProperties(struct ff_effect * event,struct ff_envelope * ffenvelope,const Effect * effect,const Envelope * envelope)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 //--------------------------------------------------------------//
_updateConstantEffect(const Effect * eff)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 //--------------------------------------------------------------//
_updateRampEffect(const Effect * eff)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 //--------------------------------------------------------------//
_updatePeriodicEffect(const Effect * eff)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 //--------------------------------------------------------------//
_updateConditionalEffect(const Effect * eff)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 //--------------------------------------------------------------//
_upload(struct ff_effect * ffeffect,const Effect * effect)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 //--------------------------------------------------------------//
_stop(int handle)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 //--------------------------------------------------------------//
_start(int handle)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 //--------------------------------------------------------------//
_unload(int handle)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