• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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