1 /*
2   Simple DirectMedia Layer
3   Copyright (C) 1997-2020 Sam Lantinga <slouken@libsdl.org>
4 
5   This software is provided 'as-is', without any express or implied
6   warranty.  In no event will the authors be held liable for any damages
7   arising from the use of this software.
8 
9   Permission is granted to anyone to use this software for any purpose,
10   including commercial applications, and to alter it and redistribute it
11   freely, subject to the following restrictions:
12 
13   1. The origin of this software must not be misrepresented; you must not
14      claim that you wrote the original software. If you use this software
15      in a product, an acknowledgment in the product documentation would be
16      appreciated but is not required.
17   2. Altered source versions must be plainly marked as such, and must not be
18      misrepresented as being the original software.
19   3. This notice may not be removed or altered from any source distribution.
20 */
21 #include "../../SDL_internal.h"
22 
23 #ifdef SDL_HAPTIC_LINUX
24 
25 #include "SDL_assert.h"
26 #include "SDL_haptic.h"
27 #include "../SDL_syshaptic.h"
28 #include "SDL_joystick.h"
29 #include "../../joystick/SDL_sysjoystick.h"     /* For the real SDL_Joystick */
30 #include "../../joystick/linux/SDL_sysjoystick_c.h"     /* For joystick hwdata */
31 #include "../../core/linux/SDL_udev.h"
32 
33 #include <unistd.h>             /* close */
34 #include <linux/input.h>        /* Force feedback linux stuff. */
35 #include <fcntl.h>              /* O_RDWR */
36 #include <limits.h>             /* INT_MAX */
37 #include <errno.h>              /* errno, strerror */
38 #include <math.h>               /* atan2 */
39 #include <sys/stat.h>           /* stat */
40 
41 /* Just in case. */
42 #ifndef M_PI
43 #  define M_PI     3.14159265358979323846
44 #endif
45 
46 
47 #define MAX_HAPTICS  32         /* It's doubtful someone has more then 32 evdev */
48 
49 static int MaybeAddDevice(const char *path);
50 #if SDL_USE_LIBUDEV
51 static int MaybeRemoveDevice(const char *path);
52 static void haptic_udev_callback(SDL_UDEV_deviceevent udev_type, int udev_class, const char *devpath);
53 #endif /* SDL_USE_LIBUDEV */
54 
55 /*
56  * List of available haptic devices.
57  */
58 typedef struct SDL_hapticlist_item
59 {
60     char *fname;                /* Dev path name (like /dev/input/event1) */
61     SDL_Haptic *haptic;         /* Associated haptic. */
62     dev_t dev_num;
63     struct SDL_hapticlist_item *next;
64 } SDL_hapticlist_item;
65 
66 
67 /*
68  * Haptic system hardware data.
69  */
70 struct haptic_hwdata
71 {
72     int fd;                     /* File descriptor of the device. */
73     char *fname;                /* Points to the name in SDL_hapticlist. */
74 };
75 
76 
77 /*
78  * Haptic system effect data.
79  */
80 struct haptic_hweffect
81 {
82     struct ff_effect effect;    /* The linux kernel effect structure. */
83 };
84 
85 static SDL_hapticlist_item *SDL_hapticlist = NULL;
86 static SDL_hapticlist_item *SDL_hapticlist_tail = NULL;
87 static int numhaptics = 0;
88 
89 #define test_bit(nr, addr) \
90    (((1UL << ((nr) & 31)) & (((const unsigned int *) addr)[(nr) >> 5])) != 0)
91 #define EV_TEST(ev,f) \
92    if (test_bit((ev), features)) ret |= (f);
93 /*
94  * Test whether a device has haptic properties.
95  * Returns available properties or 0 if there are none.
96  */
97 static int
EV_IsHaptic(int fd)98 EV_IsHaptic(int fd)
99 {
100     unsigned int ret;
101     unsigned long features[1 + FF_MAX / sizeof(unsigned long)];
102 
103     /* Ask device for what it has. */
104     ret = 0;
105     if (ioctl(fd, EVIOCGBIT(EV_FF, sizeof(features)), features) < 0) {
106         return SDL_SetError("Haptic: Unable to get device's features: %s",
107                             strerror(errno));
108     }
109 
110     /* Convert supported features to SDL_HAPTIC platform-neutral features. */
111     EV_TEST(FF_CONSTANT, SDL_HAPTIC_CONSTANT);
112     EV_TEST(FF_SINE, SDL_HAPTIC_SINE);
113     /* !!! FIXME: put this back when we have more bits in 2.1 */
114     /* EV_TEST(FF_SQUARE, SDL_HAPTIC_SQUARE); */
115     EV_TEST(FF_TRIANGLE, SDL_HAPTIC_TRIANGLE);
116     EV_TEST(FF_SAW_UP, SDL_HAPTIC_SAWTOOTHUP);
117     EV_TEST(FF_SAW_DOWN, SDL_HAPTIC_SAWTOOTHDOWN);
118     EV_TEST(FF_RAMP, SDL_HAPTIC_RAMP);
119     EV_TEST(FF_SPRING, SDL_HAPTIC_SPRING);
120     EV_TEST(FF_FRICTION, SDL_HAPTIC_FRICTION);
121     EV_TEST(FF_DAMPER, SDL_HAPTIC_DAMPER);
122     EV_TEST(FF_INERTIA, SDL_HAPTIC_INERTIA);
123     EV_TEST(FF_CUSTOM, SDL_HAPTIC_CUSTOM);
124     EV_TEST(FF_GAIN, SDL_HAPTIC_GAIN);
125     EV_TEST(FF_AUTOCENTER, SDL_HAPTIC_AUTOCENTER);
126     EV_TEST(FF_RUMBLE, SDL_HAPTIC_LEFTRIGHT);
127 
128     /* Return what it supports. */
129     return ret;
130 }
131 
132 
133 /*
134  * Tests whether a device is a mouse or not.
135  */
136 static int
EV_IsMouse(int fd)137 EV_IsMouse(int fd)
138 {
139     unsigned long argp[40];
140 
141     /* Ask for supported features. */
142     if (ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(argp)), argp) < 0) {
143         return -1;
144     }
145 
146     /* Currently we only test for BTN_MOUSE which can give fake positives. */
147     if (test_bit(BTN_MOUSE, argp) != 0) {
148         return 1;
149     }
150 
151     return 0;
152 }
153 
154 /*
155  * Initializes the haptic subsystem by finding available devices.
156  */
157 int
SDL_SYS_HapticInit(void)158 SDL_SYS_HapticInit(void)
159 {
160     const char joydev_pattern[] = "/dev/input/event%d";
161     char path[PATH_MAX];
162     int i, j;
163 
164     /*
165      * Limit amount of checks to MAX_HAPTICS since we may or may not have
166      * permission to some or all devices.
167      */
168     i = 0;
169     for (j = 0; j < MAX_HAPTICS; ++j) {
170 
171         snprintf(path, PATH_MAX, joydev_pattern, i++);
172         MaybeAddDevice(path);
173     }
174 
175 #if SDL_USE_LIBUDEV
176     if (SDL_UDEV_Init() < 0) {
177         return SDL_SetError("Could not initialize UDEV");
178     }
179 
180     if ( SDL_UDEV_AddCallback(haptic_udev_callback) < 0) {
181         SDL_UDEV_Quit();
182         return SDL_SetError("Could not setup haptic <-> udev callback");
183     }
184 
185     /* Force a scan to build the initial device list */
186     SDL_UDEV_Scan();
187 #endif /* SDL_USE_LIBUDEV */
188 
189     return numhaptics;
190 }
191 
192 int
SDL_SYS_NumHaptics(void)193 SDL_SYS_NumHaptics(void)
194 {
195     return numhaptics;
196 }
197 
198 static SDL_hapticlist_item *
HapticByDevIndex(int device_index)199 HapticByDevIndex(int device_index)
200 {
201     SDL_hapticlist_item *item = SDL_hapticlist;
202 
203     if ((device_index < 0) || (device_index >= numhaptics)) {
204         return NULL;
205     }
206 
207     while (device_index > 0) {
208         SDL_assert(item != NULL);
209         --device_index;
210         item = item->next;
211     }
212 
213     return item;
214 }
215 
216 #if SDL_USE_LIBUDEV
haptic_udev_callback(SDL_UDEV_deviceevent udev_type,int udev_class,const char * devpath)217 static void haptic_udev_callback(SDL_UDEV_deviceevent udev_type, int udev_class, const char *devpath)
218 {
219     if (devpath == NULL || !(udev_class & SDL_UDEV_DEVICE_JOYSTICK)) {
220         return;
221     }
222 
223     switch( udev_type )
224     {
225         case SDL_UDEV_DEVICEADDED:
226             MaybeAddDevice(devpath);
227             break;
228 
229         case SDL_UDEV_DEVICEREMOVED:
230             MaybeRemoveDevice(devpath);
231             break;
232 
233         default:
234             break;
235     }
236 
237 }
238 #endif /* SDL_USE_LIBUDEV */
239 
240 static int
MaybeAddDevice(const char * path)241 MaybeAddDevice(const char *path)
242 {
243     struct stat sb;
244     int fd;
245     int success;
246     SDL_hapticlist_item *item;
247 
248     if (path == NULL) {
249         return -1;
250     }
251 
252     /* check to see if file exists */
253     if (stat(path, &sb) != 0) {
254         return -1;
255     }
256 
257     /* check for duplicates */
258     for (item = SDL_hapticlist; item != NULL; item = item->next) {
259         if (item->dev_num == sb.st_rdev) {
260             return -1;  /* duplicate. */
261         }
262     }
263 
264     /* try to open */
265     fd = open(path, O_RDWR, 0);
266     if (fd < 0) {
267         return -1;
268     }
269 
270 #ifdef DEBUG_INPUT_EVENTS
271     printf("Checking %s\n", path);
272 #endif
273 
274     /* see if it works */
275     success = EV_IsHaptic(fd);
276     close(fd);
277     if (success <= 0) {
278         return -1;
279     }
280 
281     item = (SDL_hapticlist_item *) SDL_calloc(1, sizeof (SDL_hapticlist_item));
282     if (item == NULL) {
283         return -1;
284     }
285 
286     item->fname = SDL_strdup(path);
287     if (item->fname == NULL) {
288         SDL_free(item);
289         return -1;
290     }
291 
292     item->dev_num = sb.st_rdev;
293 
294     /* TODO: should we add instance IDs? */
295     if (SDL_hapticlist_tail == NULL) {
296         SDL_hapticlist = SDL_hapticlist_tail = item;
297     } else {
298         SDL_hapticlist_tail->next = item;
299         SDL_hapticlist_tail = item;
300     }
301 
302     ++numhaptics;
303 
304     /* !!! TODO: Send a haptic add event? */
305 
306     return numhaptics;
307 }
308 
309 #if SDL_USE_LIBUDEV
310 static int
MaybeRemoveDevice(const char * path)311 MaybeRemoveDevice(const char* path)
312 {
313     SDL_hapticlist_item *item;
314     SDL_hapticlist_item *prev = NULL;
315 
316     if (path == NULL) {
317         return -1;
318     }
319 
320     for (item = SDL_hapticlist; item != NULL; item = item->next) {
321         /* found it, remove it. */
322         if (SDL_strcmp(path, item->fname) == 0) {
323             const int retval = item->haptic ? item->haptic->index : -1;
324 
325             if (prev != NULL) {
326                 prev->next = item->next;
327             } else {
328                 SDL_assert(SDL_hapticlist == item);
329                 SDL_hapticlist = item->next;
330             }
331             if (item == SDL_hapticlist_tail) {
332                 SDL_hapticlist_tail = prev;
333             }
334 
335             /* Need to decrement the haptic count */
336             --numhaptics;
337             /* !!! TODO: Send a haptic remove event? */
338 
339             SDL_free(item->fname);
340             SDL_free(item);
341             return retval;
342         }
343         prev = item;
344     }
345 
346     return -1;
347 }
348 #endif /* SDL_USE_LIBUDEV */
349 
350 /*
351  * Gets the name from a file descriptor.
352  */
353 static const char *
SDL_SYS_HapticNameFromFD(int fd)354 SDL_SYS_HapticNameFromFD(int fd)
355 {
356     static char namebuf[128];
357 
358     /* We use the evdev name ioctl. */
359     if (ioctl(fd, EVIOCGNAME(sizeof(namebuf)), namebuf) <= 0) {
360         return NULL;
361     }
362 
363     return namebuf;
364 }
365 
366 
367 /*
368  * Return the name of a haptic device, does not need to be opened.
369  */
370 const char *
SDL_SYS_HapticName(int index)371 SDL_SYS_HapticName(int index)
372 {
373     SDL_hapticlist_item *item;
374     int fd;
375     const char *name;
376 
377     item = HapticByDevIndex(index);
378     /* Open the haptic device. */
379     name = NULL;
380     fd = open(item->fname, O_RDONLY, 0);
381 
382     if (fd >= 0) {
383 
384         name = SDL_SYS_HapticNameFromFD(fd);
385         if (name == NULL) {
386             /* No name found, return device character device */
387             name = item->fname;
388         }
389         close(fd);
390     }
391 
392     return name;
393 }
394 
395 
396 /*
397  * Opens the haptic device from the file descriptor.
398  */
399 static int
SDL_SYS_HapticOpenFromFD(SDL_Haptic * haptic,int fd)400 SDL_SYS_HapticOpenFromFD(SDL_Haptic * haptic, int fd)
401 {
402     /* Allocate the hwdata */
403     haptic->hwdata = (struct haptic_hwdata *)
404         SDL_malloc(sizeof(*haptic->hwdata));
405     if (haptic->hwdata == NULL) {
406         SDL_OutOfMemory();
407         goto open_err;
408     }
409     SDL_memset(haptic->hwdata, 0, sizeof(*haptic->hwdata));
410 
411     /* Set the data. */
412     haptic->hwdata->fd = fd;
413     haptic->supported = EV_IsHaptic(fd);
414     haptic->naxes = 2;          /* Hardcoded for now, not sure if it's possible to find out. */
415 
416     /* Set the effects */
417     if (ioctl(fd, EVIOCGEFFECTS, &haptic->neffects) < 0) {
418         SDL_SetError("Haptic: Unable to query device memory: %s",
419                      strerror(errno));
420         goto open_err;
421     }
422     haptic->nplaying = haptic->neffects;        /* Linux makes no distinction. */
423     haptic->effects = (struct haptic_effect *)
424         SDL_malloc(sizeof(struct haptic_effect) * haptic->neffects);
425     if (haptic->effects == NULL) {
426         SDL_OutOfMemory();
427         goto open_err;
428     }
429     /* Clear the memory */
430     SDL_memset(haptic->effects, 0,
431                sizeof(struct haptic_effect) * haptic->neffects);
432 
433     return 0;
434 
435     /* Error handling */
436   open_err:
437     close(fd);
438     if (haptic->hwdata != NULL) {
439         SDL_free(haptic->hwdata);
440         haptic->hwdata = NULL;
441     }
442     return -1;
443 }
444 
445 
446 /*
447  * Opens a haptic device for usage.
448  */
449 int
SDL_SYS_HapticOpen(SDL_Haptic * haptic)450 SDL_SYS_HapticOpen(SDL_Haptic * haptic)
451 {
452     int fd;
453     int ret;
454     SDL_hapticlist_item *item;
455 
456     item = HapticByDevIndex(haptic->index);
457     /* Open the character device */
458     fd = open(item->fname, O_RDWR, 0);
459     if (fd < 0) {
460         return SDL_SetError("Haptic: Unable to open %s: %s",
461                             item->fname, strerror(errno));
462     }
463 
464     /* Try to create the haptic. */
465     ret = SDL_SYS_HapticOpenFromFD(haptic, fd); /* Already closes on error. */
466     if (ret < 0) {
467         return -1;
468     }
469 
470     /* Set the fname. */
471     haptic->hwdata->fname = SDL_strdup( item->fname );
472     return 0;
473 }
474 
475 
476 /*
477  * Opens a haptic device from first mouse it finds for usage.
478  */
479 int
SDL_SYS_HapticMouse(void)480 SDL_SYS_HapticMouse(void)
481 {
482     int fd;
483     int device_index = 0;
484     SDL_hapticlist_item *item;
485 
486     for (item = SDL_hapticlist; item; item = item->next) {
487         /* Open the device. */
488         fd = open(item->fname, O_RDWR, 0);
489         if (fd < 0) {
490             return SDL_SetError("Haptic: Unable to open %s: %s",
491                                 item->fname, strerror(errno));
492         }
493 
494         /* Is it a mouse? */
495         if (EV_IsMouse(fd)) {
496             close(fd);
497             return device_index;
498         }
499 
500         close(fd);
501 
502         ++device_index;
503     }
504 
505     return -1;
506 }
507 
508 
509 /*
510  * Checks to see if a joystick has haptic features.
511  */
512 int
SDL_SYS_JoystickIsHaptic(SDL_Joystick * joystick)513 SDL_SYS_JoystickIsHaptic(SDL_Joystick * joystick)
514 {
515 #ifdef SDL_JOYSTICK_LINUX
516     if (joystick->driver != &SDL_LINUX_JoystickDriver) {
517         return SDL_FALSE;
518     }
519     if (EV_IsHaptic(joystick->hwdata->fd)) {
520         return SDL_TRUE;
521     }
522 #endif
523     return SDL_FALSE;
524 }
525 
526 
527 /*
528  * Checks to see if the haptic device and joystick are in reality the same.
529  */
530 int
SDL_SYS_JoystickSameHaptic(SDL_Haptic * haptic,SDL_Joystick * joystick)531 SDL_SYS_JoystickSameHaptic(SDL_Haptic * haptic, SDL_Joystick * joystick)
532 {
533 #ifdef SDL_JOYSTICK_LINUX
534     if (joystick->driver != &SDL_LINUX_JoystickDriver) {
535         return 0;
536     }
537     /* We are assuming Linux is using evdev which should trump the old
538      * joystick methods. */
539     if (SDL_strcmp(joystick->hwdata->fname, haptic->hwdata->fname) == 0) {
540         return 1;
541     }
542 #endif
543     return 0;
544 }
545 
546 
547 /*
548  * Opens a SDL_Haptic from a SDL_Joystick.
549  */
550 int
SDL_SYS_HapticOpenFromJoystick(SDL_Haptic * haptic,SDL_Joystick * joystick)551 SDL_SYS_HapticOpenFromJoystick(SDL_Haptic * haptic, SDL_Joystick * joystick)
552 {
553 #ifdef SDL_JOYSTICK_LINUX
554     int device_index = 0;
555     int fd;
556     int ret;
557     SDL_hapticlist_item *item;
558 
559     if (joystick->driver != &SDL_LINUX_JoystickDriver) {
560         return -1;
561     }
562     /* Find the joystick in the haptic list. */
563     for (item = SDL_hapticlist; item; item = item->next) {
564         if (SDL_strcmp(item->fname, joystick->hwdata->fname) == 0) {
565             break;
566         }
567         ++device_index;
568     }
569     haptic->index = device_index;
570 
571     if (device_index >= MAX_HAPTICS) {
572         return SDL_SetError("Haptic: Joystick doesn't have Haptic capabilities");
573     }
574 
575     fd = open(joystick->hwdata->fname, O_RDWR, 0);
576     if (fd < 0) {
577         return SDL_SetError("Haptic: Unable to open %s: %s",
578                             joystick->hwdata->fname, strerror(errno));
579     }
580     ret = SDL_SYS_HapticOpenFromFD(haptic, fd); /* Already closes on error. */
581     if (ret < 0) {
582         return -1;
583     }
584 
585     haptic->hwdata->fname = SDL_strdup( joystick->hwdata->fname );
586 
587     return 0;
588 #else
589     return -1;
590 #endif
591 }
592 
593 
594 /*
595  * Closes the haptic device.
596  */
597 void
SDL_SYS_HapticClose(SDL_Haptic * haptic)598 SDL_SYS_HapticClose(SDL_Haptic * haptic)
599 {
600     if (haptic->hwdata) {
601 
602         /* Free effects. */
603         SDL_free(haptic->effects);
604         haptic->effects = NULL;
605         haptic->neffects = 0;
606 
607         /* Clean up */
608         close(haptic->hwdata->fd);
609 
610         /* Free */
611         SDL_free(haptic->hwdata->fname);
612         SDL_free(haptic->hwdata);
613         haptic->hwdata = NULL;
614     }
615 
616     /* Clear the rest. */
617     SDL_memset(haptic, 0, sizeof(SDL_Haptic));
618 }
619 
620 
621 /*
622  * Clean up after system specific haptic stuff
623  */
624 void
SDL_SYS_HapticQuit(void)625 SDL_SYS_HapticQuit(void)
626 {
627     SDL_hapticlist_item *item = NULL;
628     SDL_hapticlist_item *next = NULL;
629 
630     for (item = SDL_hapticlist; item; item = next) {
631         next = item->next;
632         /* Opened and not closed haptics are leaked, this is on purpose.
633          * Close your haptic devices after usage. */
634         SDL_free(item->fname);
635         SDL_free(item);
636     }
637 
638 #if SDL_USE_LIBUDEV
639     SDL_UDEV_DelCallback(haptic_udev_callback);
640     SDL_UDEV_Quit();
641 #endif /* SDL_USE_LIBUDEV */
642 
643     numhaptics = 0;
644     SDL_hapticlist = NULL;
645     SDL_hapticlist_tail = NULL;
646 }
647 
648 
649 /*
650  * Converts an SDL button to a ff_trigger button.
651  */
652 static Uint16
SDL_SYS_ToButton(Uint16 button)653 SDL_SYS_ToButton(Uint16 button)
654 {
655     Uint16 ff_button;
656 
657     ff_button = 0;
658 
659     /*
660      * Not sure what the proper syntax is because this actually isn't implemented
661      * in the current kernel from what I've seen (2.6.26).
662      */
663     if (button != 0) {
664         ff_button = BTN_GAMEPAD + button - 1;
665     }
666 
667     return ff_button;
668 }
669 
670 
671 /*
672  * Initializes the ff_effect usable direction from a SDL_HapticDirection.
673  */
674 static int
SDL_SYS_ToDirection(Uint16 * dest,SDL_HapticDirection * src)675 SDL_SYS_ToDirection(Uint16 *dest, SDL_HapticDirection * src)
676 {
677     Uint32 tmp;
678 
679     switch (src->type) {
680     case SDL_HAPTIC_POLAR:
681         /* Linux directions start from south.
682                 (and range from 0 to 0xFFFF)
683                    Quoting include/linux/input.h, line 926:
684                    Direction of the effect is encoded as follows:
685                         0 deg -> 0x0000 (down)
686                         90 deg -> 0x4000 (left)
687                         180 deg -> 0x8000 (up)
688                         270 deg -> 0xC000 (right)
689                    The force pulls into the direction specified by Linux directions,
690                    i.e. the opposite convention of SDL directions.
691                     */
692         tmp = ((src->dir[0] % 36000) * 0x8000) / 18000; /* convert to range [0,0xFFFF] */
693         *dest = (Uint16) tmp;
694         break;
695 
696     case SDL_HAPTIC_SPHERICAL:
697             /*
698                 We convert to polar, because that's the only supported direction on Linux.
699                 The first value of a spherical direction is practically the same as a
700                 Polar direction, except that we have to add 90 degrees. It is the angle
701                 from EAST {1,0} towards SOUTH {0,1}.
702                 --> add 9000
703                 --> finally convert to [0,0xFFFF] as in case SDL_HAPTIC_POLAR.
704             */
705             tmp = ((src->dir[0]) + 9000) % 36000;    /* Convert to polars */
706         tmp = (tmp * 0x8000) / 18000; /* convert to range [0,0xFFFF] */
707         *dest = (Uint16) tmp;
708         break;
709 
710     case SDL_HAPTIC_CARTESIAN:
711         if (!src->dir[1])
712             *dest = (src->dir[0] >= 0 ? 0x4000 : 0xC000);
713         else if (!src->dir[0])
714             *dest = (src->dir[1] >= 0 ? 0x8000 : 0);
715         else {
716             float f = SDL_atan2(src->dir[1], src->dir[0]);    /* Ideally we'd use fixed point math instead of floats... */
717                     /*
718                       atan2 takes the parameters: Y-axis-value and X-axis-value (in that order)
719                        - Y-axis-value is the second coordinate (from center to SOUTH)
720                        - X-axis-value is the first coordinate (from center to EAST)
721                         We add 36000, because atan2 also returns negative values. Then we practically
722                         have the first spherical value. Therefore we proceed as in case
723                         SDL_HAPTIC_SPHERICAL and add another 9000 to get the polar value.
724                       --> add 45000 in total
725                       --> finally convert to [0,0xFFFF] as in case SDL_HAPTIC_POLAR.
726                     */
727                 tmp = (((Sint32) (f * 18000. / M_PI)) + 45000) % 36000;
728             tmp = (tmp * 0x8000) / 18000; /* convert to range [0,0xFFFF] */
729             *dest = (Uint16) tmp;
730         }
731         break;
732     case SDL_HAPTIC_STEERING_AXIS:
733         *dest = 0x4000;
734         break;
735     default:
736         return SDL_SetError("Haptic: Unsupported direction type.");
737     }
738 
739     return 0;
740 }
741 
742 
743 #define  CLAMP(x)    (((x) > 32767) ? 32767 : x)
744 /*
745  * Initializes the Linux effect struct from a haptic_effect.
746  * Values above 32767 (for unsigned) are unspecified so we must clamp.
747  */
748 static int
SDL_SYS_ToFFEffect(struct ff_effect * dest,SDL_HapticEffect * src)749 SDL_SYS_ToFFEffect(struct ff_effect *dest, SDL_HapticEffect * src)
750 {
751     SDL_HapticConstant *constant;
752     SDL_HapticPeriodic *periodic;
753     SDL_HapticCondition *condition;
754     SDL_HapticRamp *ramp;
755     SDL_HapticLeftRight *leftright;
756 
757     /* Clear up */
758     SDL_memset(dest, 0, sizeof(struct ff_effect));
759 
760     switch (src->type) {
761     case SDL_HAPTIC_CONSTANT:
762         constant = &src->constant;
763 
764         /* Header */
765         dest->type = FF_CONSTANT;
766         if (SDL_SYS_ToDirection(&dest->direction, &constant->direction) == -1)
767             return -1;
768 
769         /* Replay */
770         dest->replay.length = (constant->length == SDL_HAPTIC_INFINITY) ?
771             0 : CLAMP(constant->length);
772         dest->replay.delay = CLAMP(constant->delay);
773 
774         /* Trigger */
775         dest->trigger.button = SDL_SYS_ToButton(constant->button);
776         dest->trigger.interval = CLAMP(constant->interval);
777 
778         /* Constant */
779         dest->u.constant.level = constant->level;
780 
781         /* Envelope */
782         dest->u.constant.envelope.attack_length =
783             CLAMP(constant->attack_length);
784         dest->u.constant.envelope.attack_level =
785             CLAMP(constant->attack_level);
786         dest->u.constant.envelope.fade_length = CLAMP(constant->fade_length);
787         dest->u.constant.envelope.fade_level = CLAMP(constant->fade_level);
788 
789         break;
790 
791     case SDL_HAPTIC_SINE:
792     /* !!! FIXME: put this back when we have more bits in 2.1 */
793     /* case SDL_HAPTIC_SQUARE: */
794     case SDL_HAPTIC_TRIANGLE:
795     case SDL_HAPTIC_SAWTOOTHUP:
796     case SDL_HAPTIC_SAWTOOTHDOWN:
797         periodic = &src->periodic;
798 
799         /* Header */
800         dest->type = FF_PERIODIC;
801         if (SDL_SYS_ToDirection(&dest->direction, &periodic->direction) == -1)
802             return -1;
803 
804         /* Replay */
805         dest->replay.length = (periodic->length == SDL_HAPTIC_INFINITY) ?
806             0 : CLAMP(periodic->length);
807         dest->replay.delay = CLAMP(periodic->delay);
808 
809         /* Trigger */
810         dest->trigger.button = SDL_SYS_ToButton(periodic->button);
811         dest->trigger.interval = CLAMP(periodic->interval);
812 
813         /* Periodic */
814         if (periodic->type == SDL_HAPTIC_SINE)
815             dest->u.periodic.waveform = FF_SINE;
816         /* !!! FIXME: put this back when we have more bits in 2.1 */
817         /* else if (periodic->type == SDL_HAPTIC_SQUARE)
818             dest->u.periodic.waveform = FF_SQUARE; */
819         else if (periodic->type == SDL_HAPTIC_TRIANGLE)
820             dest->u.periodic.waveform = FF_TRIANGLE;
821         else if (periodic->type == SDL_HAPTIC_SAWTOOTHUP)
822             dest->u.periodic.waveform = FF_SAW_UP;
823         else if (periodic->type == SDL_HAPTIC_SAWTOOTHDOWN)
824             dest->u.periodic.waveform = FF_SAW_DOWN;
825         dest->u.periodic.period = CLAMP(periodic->period);
826         dest->u.periodic.magnitude = periodic->magnitude;
827         dest->u.periodic.offset = periodic->offset;
828         /* Linux phase is defined in interval "[0x0000, 0x10000[", corresponds with "[0deg, 360deg[" phase shift. */
829         dest->u.periodic.phase = ((Uint32)periodic->phase * 0x10000U) / 36000;
830 
831         /* Envelope */
832         dest->u.periodic.envelope.attack_length =
833             CLAMP(periodic->attack_length);
834         dest->u.periodic.envelope.attack_level =
835             CLAMP(periodic->attack_level);
836         dest->u.periodic.envelope.fade_length = CLAMP(periodic->fade_length);
837         dest->u.periodic.envelope.fade_level = CLAMP(periodic->fade_level);
838 
839         break;
840 
841     case SDL_HAPTIC_SPRING:
842     case SDL_HAPTIC_DAMPER:
843     case SDL_HAPTIC_INERTIA:
844     case SDL_HAPTIC_FRICTION:
845         condition = &src->condition;
846 
847         /* Header */
848         if (condition->type == SDL_HAPTIC_SPRING)
849             dest->type = FF_SPRING;
850         else if (condition->type == SDL_HAPTIC_DAMPER)
851             dest->type = FF_DAMPER;
852         else if (condition->type == SDL_HAPTIC_INERTIA)
853             dest->type = FF_INERTIA;
854         else if (condition->type == SDL_HAPTIC_FRICTION)
855             dest->type = FF_FRICTION;
856         dest->direction = 0;    /* Handled by the condition-specifics. */
857 
858         /* Replay */
859         dest->replay.length = (condition->length == SDL_HAPTIC_INFINITY) ?
860             0 : CLAMP(condition->length);
861         dest->replay.delay = CLAMP(condition->delay);
862 
863         /* Trigger */
864         dest->trigger.button = SDL_SYS_ToButton(condition->button);
865         dest->trigger.interval = CLAMP(condition->interval);
866 
867         /* Condition */
868         /* X axis */
869         dest->u.condition[0].right_saturation = condition->right_sat[0];
870         dest->u.condition[0].left_saturation = condition->left_sat[0];
871         dest->u.condition[0].right_coeff = condition->right_coeff[0];
872         dest->u.condition[0].left_coeff = condition->left_coeff[0];
873         dest->u.condition[0].deadband = condition->deadband[0];
874         dest->u.condition[0].center = condition->center[0];
875         /* Y axis */
876         dest->u.condition[1].right_saturation = condition->right_sat[1];
877         dest->u.condition[1].left_saturation = condition->left_sat[1];
878         dest->u.condition[1].right_coeff = condition->right_coeff[1];
879         dest->u.condition[1].left_coeff = condition->left_coeff[1];
880         dest->u.condition[1].deadband = condition->deadband[1];
881         dest->u.condition[1].center = condition->center[1];
882 
883         /*
884          * There is no envelope in the linux force feedback api for conditions.
885          */
886 
887         break;
888 
889     case SDL_HAPTIC_RAMP:
890         ramp = &src->ramp;
891 
892         /* Header */
893         dest->type = FF_RAMP;
894         if (SDL_SYS_ToDirection(&dest->direction, &ramp->direction) == -1)
895             return -1;
896 
897         /* Replay */
898         dest->replay.length = (ramp->length == SDL_HAPTIC_INFINITY) ?
899             0 : CLAMP(ramp->length);
900         dest->replay.delay = CLAMP(ramp->delay);
901 
902         /* Trigger */
903         dest->trigger.button = SDL_SYS_ToButton(ramp->button);
904         dest->trigger.interval = CLAMP(ramp->interval);
905 
906         /* Ramp */
907         dest->u.ramp.start_level = ramp->start;
908         dest->u.ramp.end_level = ramp->end;
909 
910         /* Envelope */
911         dest->u.ramp.envelope.attack_length = CLAMP(ramp->attack_length);
912         dest->u.ramp.envelope.attack_level = CLAMP(ramp->attack_level);
913         dest->u.ramp.envelope.fade_length = CLAMP(ramp->fade_length);
914         dest->u.ramp.envelope.fade_level = CLAMP(ramp->fade_level);
915 
916         break;
917 
918     case SDL_HAPTIC_LEFTRIGHT:
919         leftright = &src->leftright;
920 
921         /* Header */
922         dest->type = FF_RUMBLE;
923         dest->direction = 0;
924 
925         /* Replay */
926         dest->replay.length = (leftright->length == SDL_HAPTIC_INFINITY) ?
927             0 : CLAMP(leftright->length);
928 
929         /* Trigger */
930         dest->trigger.button = 0;
931         dest->trigger.interval = 0;
932 
933         /* Rumble (Linux expects 0-65535, so multiply by 2) */
934         dest->u.rumble.strong_magnitude = CLAMP(leftright->large_magnitude) * 2;
935         dest->u.rumble.weak_magnitude = CLAMP(leftright->small_magnitude) * 2;
936 
937         break;
938 
939 
940     default:
941         return SDL_SetError("Haptic: Unknown effect type.");
942     }
943 
944     return 0;
945 }
946 
947 
948 /*
949  * Creates a new haptic effect.
950  */
951 int
SDL_SYS_HapticNewEffect(SDL_Haptic * haptic,struct haptic_effect * effect,SDL_HapticEffect * base)952 SDL_SYS_HapticNewEffect(SDL_Haptic * haptic, struct haptic_effect *effect,
953                         SDL_HapticEffect * base)
954 {
955     struct ff_effect *linux_effect;
956 
957     /* Allocate the hardware effect */
958     effect->hweffect = (struct haptic_hweffect *)
959         SDL_malloc(sizeof(struct haptic_hweffect));
960     if (effect->hweffect == NULL) {
961         return SDL_OutOfMemory();
962     }
963 
964     /* Prepare the ff_effect */
965     linux_effect = &effect->hweffect->effect;
966     if (SDL_SYS_ToFFEffect(linux_effect, base) != 0) {
967         goto new_effect_err;
968     }
969     linux_effect->id = -1;      /* Have the kernel give it an id */
970 
971     /* Upload the effect */
972     if (ioctl(haptic->hwdata->fd, EVIOCSFF, linux_effect) < 0) {
973         SDL_SetError("Haptic: Error uploading effect to the device: %s",
974                      strerror(errno));
975         goto new_effect_err;
976     }
977 
978     return 0;
979 
980   new_effect_err:
981     SDL_free(effect->hweffect);
982     effect->hweffect = NULL;
983     return -1;
984 }
985 
986 
987 /*
988  * Updates an effect.
989  *
990  * Note: Dynamically updating the direction can in some cases force
991  * the effect to restart and run once.
992  */
993 int
SDL_SYS_HapticUpdateEffect(SDL_Haptic * haptic,struct haptic_effect * effect,SDL_HapticEffect * data)994 SDL_SYS_HapticUpdateEffect(SDL_Haptic * haptic,
995                            struct haptic_effect *effect,
996                            SDL_HapticEffect * data)
997 {
998     struct ff_effect linux_effect;
999 
1000     /* Create the new effect */
1001     if (SDL_SYS_ToFFEffect(&linux_effect, data) != 0) {
1002         return -1;
1003     }
1004     linux_effect.id = effect->hweffect->effect.id;
1005 
1006     /* See if it can be uploaded. */
1007     if (ioctl(haptic->hwdata->fd, EVIOCSFF, &linux_effect) < 0) {
1008         return SDL_SetError("Haptic: Error updating the effect: %s",
1009                             strerror(errno));
1010     }
1011 
1012     /* Copy the new effect into memory. */
1013     SDL_memcpy(&effect->hweffect->effect, &linux_effect,
1014                sizeof(struct ff_effect));
1015 
1016     return effect->hweffect->effect.id;
1017 }
1018 
1019 
1020 /*
1021  * Runs an effect.
1022  */
1023 int
SDL_SYS_HapticRunEffect(SDL_Haptic * haptic,struct haptic_effect * effect,Uint32 iterations)1024 SDL_SYS_HapticRunEffect(SDL_Haptic * haptic, struct haptic_effect *effect,
1025                         Uint32 iterations)
1026 {
1027     struct input_event run;
1028 
1029     /* Prepare to run the effect */
1030     run.type = EV_FF;
1031     run.code = effect->hweffect->effect.id;
1032     /* We don't actually have infinity here, so we just do INT_MAX which is pretty damn close. */
1033     run.value = (iterations > INT_MAX) ? INT_MAX : iterations;
1034 
1035     if (write(haptic->hwdata->fd, (const void *) &run, sizeof(run)) < 0) {
1036         return SDL_SetError("Haptic: Unable to run the effect: %s", strerror(errno));
1037     }
1038 
1039     return 0;
1040 }
1041 
1042 
1043 /*
1044  * Stops an effect.
1045  */
1046 int
SDL_SYS_HapticStopEffect(SDL_Haptic * haptic,struct haptic_effect * effect)1047 SDL_SYS_HapticStopEffect(SDL_Haptic * haptic, struct haptic_effect *effect)
1048 {
1049     struct input_event stop;
1050 
1051     stop.type = EV_FF;
1052     stop.code = effect->hweffect->effect.id;
1053     stop.value = 0;
1054 
1055     if (write(haptic->hwdata->fd, (const void *) &stop, sizeof(stop)) < 0) {
1056         return SDL_SetError("Haptic: Unable to stop the effect: %s",
1057                             strerror(errno));
1058     }
1059 
1060     return 0;
1061 }
1062 
1063 
1064 /*
1065  * Frees the effect.
1066  */
1067 void
SDL_SYS_HapticDestroyEffect(SDL_Haptic * haptic,struct haptic_effect * effect)1068 SDL_SYS_HapticDestroyEffect(SDL_Haptic * haptic, struct haptic_effect *effect)
1069 {
1070     if (ioctl(haptic->hwdata->fd, EVIOCRMFF, effect->hweffect->effect.id) < 0) {
1071         SDL_SetError("Haptic: Error removing the effect from the device: %s",
1072                      strerror(errno));
1073     }
1074     SDL_free(effect->hweffect);
1075     effect->hweffect = NULL;
1076 }
1077 
1078 
1079 /*
1080  * Gets the status of a haptic effect.
1081  */
1082 int
SDL_SYS_HapticGetEffectStatus(SDL_Haptic * haptic,struct haptic_effect * effect)1083 SDL_SYS_HapticGetEffectStatus(SDL_Haptic * haptic,
1084                               struct haptic_effect *effect)
1085 {
1086 #if 0                           /* Not supported atm. */
1087     struct input_event ie;
1088 
1089     ie.type = EV_FF;
1090     ie.type = EV_FF_STATUS;
1091     ie.code = effect->hweffect->effect.id;
1092 
1093     if (write(haptic->hwdata->fd, &ie, sizeof(ie)) < 0) {
1094         return SDL_SetError("Haptic: Error getting device status.");
1095     }
1096 
1097     return 0;
1098 #endif
1099 
1100     return -1;
1101 }
1102 
1103 
1104 /*
1105  * Sets the gain.
1106  */
1107 int
SDL_SYS_HapticSetGain(SDL_Haptic * haptic,int gain)1108 SDL_SYS_HapticSetGain(SDL_Haptic * haptic, int gain)
1109 {
1110     struct input_event ie;
1111 
1112     ie.type = EV_FF;
1113     ie.code = FF_GAIN;
1114     ie.value = (0xFFFFUL * gain) / 100;
1115 
1116     if (write(haptic->hwdata->fd, &ie, sizeof(ie)) < 0) {
1117         return SDL_SetError("Haptic: Error setting gain: %s", strerror(errno));
1118     }
1119 
1120     return 0;
1121 }
1122 
1123 
1124 /*
1125  * Sets the autocentering.
1126  */
1127 int
SDL_SYS_HapticSetAutocenter(SDL_Haptic * haptic,int autocenter)1128 SDL_SYS_HapticSetAutocenter(SDL_Haptic * haptic, int autocenter)
1129 {
1130     struct input_event ie;
1131 
1132     ie.type = EV_FF;
1133     ie.code = FF_AUTOCENTER;
1134     ie.value = (0xFFFFUL * autocenter) / 100;
1135 
1136     if (write(haptic->hwdata->fd, &ie, sizeof(ie)) < 0) {
1137         return SDL_SetError("Haptic: Error setting autocenter: %s", strerror(errno));
1138     }
1139 
1140     return 0;
1141 }
1142 
1143 
1144 /*
1145  * Pausing is not supported atm by linux.
1146  */
1147 int
SDL_SYS_HapticPause(SDL_Haptic * haptic)1148 SDL_SYS_HapticPause(SDL_Haptic * haptic)
1149 {
1150     return -1;
1151 }
1152 
1153 
1154 /*
1155  * Unpausing is not supported atm by linux.
1156  */
1157 int
SDL_SYS_HapticUnpause(SDL_Haptic * haptic)1158 SDL_SYS_HapticUnpause(SDL_Haptic * haptic)
1159 {
1160     return -1;
1161 }
1162 
1163 
1164 /*
1165  * Stops all the currently playing effects.
1166  */
1167 int
SDL_SYS_HapticStopAll(SDL_Haptic * haptic)1168 SDL_SYS_HapticStopAll(SDL_Haptic * haptic)
1169 {
1170     int i, ret;
1171 
1172     /* Linux does not support this natively so we have to loop. */
1173     for (i = 0; i < haptic->neffects; i++) {
1174         if (haptic->effects[i].hweffect != NULL) {
1175             ret = SDL_SYS_HapticStopEffect(haptic, &haptic->effects[i]);
1176             if (ret < 0) {
1177                 return SDL_SetError
1178                     ("Haptic: Error while trying to stop all playing effects.");
1179             }
1180         }
1181     }
1182     return 0;
1183 }
1184 
1185 #endif /* SDL_HAPTIC_LINUX */
1186 
1187 /* vi: set ts=4 sw=4 expandtab: */
1188