Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/trunk/src/external/ois/linux/LinuxForceFeedback.cpp @ 12178

Last change on this file since 12178 was 9672, checked in by smerkli, 11 years ago

Added a few includes that were necessary with the switch to GCC > 4.7
as well as a small define that lets boost work with threads on this
new GCC version. Things compile now, but have not been tested to be running
yet.

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