A couple of fixes in fg_joystick_x11.c identified in bug report #249
[freeglut] / src / x11 / fg_joystick_x11.c
1 /*
2  * fg_joystick_x11.c
3  *
4  * Joystick handling code
5  *
6  * Copyright (c) 1999-2000 Pawel W. Olszta. All Rights Reserved.
7  * Written by Steve Baker, <sjbaker1@airmail.net>
8  * Copied for Platform code by Evan Felix <karcaw at gmail.com>
9  * Creation date: Thur Feb 2 2012
10  *
11  * Permission is hereby granted, free of charge, to any person obtaining a
12  * copy of this software and associated documentation files (the "Software"),
13  * to deal in the Software without restriction, including without limitation
14  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
15  * and/or sell copies of the Software, and to permit persons to whom the
16  * Software is furnished to do so, subject to the following conditions:
17  *
18  * The above copyright notice and this permission notice shall be included
19  * in all copies or substantial portions of the Software.
20  *
21  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
22  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
24  * PAWEL W. OLSZTA BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
25  * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
26  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27  */
28
29 /*
30  * FreeBSD port by Stephen Montgomery-Smith <stephen@math.missouri.edu>
31  *
32  * Redone by John Fay 2/4/04 with another look from the PLIB "js" library.
33  *  Many thanks for Steve Baker for permission to pull from that library.
34  */
35
36 #include <GL/freeglut.h>
37 #include "../fg_internal.h"
38 #ifdef HAVE_SYS_PARAM_H
39 #    include <sys/param.h>
40 #endif
41
42 #include <fcntl.h>
43
44
45 /* BSD defines from "jsBSD.cxx" around lines 42-270 */
46
47 #if defined(__NetBSD__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
48
49 #    ifdef HAVE_USB_JS
50 #        if defined(__NetBSD__)
51 #            ifdef HAVE_USBHID_H
52 #                include <usbhid.h>
53 #            else
54 #                include <usb.h>
55 #            endif
56 #            include <dev/usb/usb.h>
57 #        elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
58 #            ifdef HAVE_USBHID_H
59 #                include <usbhid.h>
60 #            else
61 #                include <libusbhid.h>
62 #            endif
63 #            include <legacy/dev/usb/usb.h>
64 #        endif
65 #        include <dev/usb/usbhid.h>
66
67 /* Compatibility with older usb.h revisions */
68 #        if !defined(USB_MAX_DEVNAMES) && defined(MAXDEVNAMES)
69 #            define USB_MAX_DEVNAMES MAXDEVNAMES
70 #        endif
71 #    endif
72
73 struct os_specific_s {
74   char             fname [128 ];
75   int              fd;
76   int              is_analog;
77   /* The following structure members are specific to analog joysticks */
78   struct joystick  ajs;
79 #    ifdef HAVE_USB_JS
80   /* The following structure members are specific to USB joysticks */
81   struct hid_item *hids;
82   int              hid_dlen;
83   int              hid_offset;
84   char            *hid_data_buf;
85   int              axes_usage [ _JS_MAX_AXES ];
86 #    endif
87   /* We keep button and axes state ourselves, as they might not be updated
88    * on every read of a USB device
89    */
90   int              cache_buttons;
91   float            cache_axes [ _JS_MAX_AXES ];
92 };
93
94 /* Idents lower than USB_IDENT_OFFSET are for analog joysticks. */
95 #    define USB_IDENT_OFFSET    2
96
97 #    define USBDEV "/dev/usb"
98 #    define UHIDDEV "/dev/uhid"
99 #    define AJSDEV "/dev/joy"
100
101
102 #endif
103
104 #if defined(__NetBSD__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
105
106 #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
107 static int hatmap_x[9] = {0, 0, 1, 1, 1, 0, -1, -1, -1};
108 static int hatmap_y[9] = {0, 1, 1, 0, -1, -1, -1, 0, 1};
109
110 #    ifdef HAVE_USB_JS
111 /*
112 * fghJoystickFindUSBdev (and its helper, fghJoystickWalkUSBdev) try to locate
113 * the full name of a USB device. If /dev/usbN isn't readable, we punt and
114 * return the uhidN device name. We warn the user of this situation once.
115 */
116 static char *fghJoystickWalkUSBdev(int f, char *dev, char *out, int outlen)
117 {
118     struct usb_device_info di;
119     int i, a;
120     char *cp;
121
122     for (a = 1; a < USB_MAX_DEVICES; a++) {
123         di.udi_addr = a;
124         if (ioctl(f, USB_DEVICEINFO, &di) != 0)
125             return NULL;
126         for (i = 0; i < USB_MAX_DEVNAMES; i++)
127             if (di.udi_devnames[i][0] &&
128                 strcmp(di.udi_devnames[i], dev) == 0) {
129                 cp = calloc(1, strlen(di.udi_vendor) + strlen(di.udi_product) + 2);
130                 strcpy(cp, di.udi_vendor);
131                 strcat(cp, " ");
132                 strcat(cp, di.udi_product);
133                 strncpy(out, cp, outlen - 1);
134                 out[outlen - 1] = 0;
135                 free(cp);
136                 return out;
137             }
138     }
139     return NULL;
140 }
141
142 static int fghJoystickFindUSBdev(char *name, char *out, int outlen)
143 {
144     int i, f;
145     char buf[50];
146     char *cp;
147     static int protection_warned = 0;
148
149     for (i = 0; i < 16; i++) {
150         snprintf(buf, sizeof(buf), "%s%d", USBDEV, i);
151         f = open(buf, O_RDONLY);
152         if (f >= 0) {
153             cp = fghJoystickWalkUSBdev(f, name, out, outlen);
154             close(f);
155             if (cp)
156                 return 1;
157         }
158         else if (errno == EACCES) {
159             if (!protection_warned) {
160                 fgWarning("Can't open %s for read!", buf);
161                 protection_warned = 1;
162             }
163         }
164     }
165     return 0;
166 }
167 #endif
168
169 static int fghJoystickInitializeHID(struct os_specific_s *os,
170                                     int *num_axes, int *num_buttons)
171 {
172     int size, is_joystick;
173 #   ifdef HAVE_USBHID_H
174     int report_id = 0;
175 #   endif
176     struct hid_data *d;
177     struct hid_item h;
178     report_desc_t rd;
179
180     if ((rd = hid_get_report_desc(os->fd)) == 0)
181     {
182         fgWarning("error: %s: %s", os->fname, strerror(errno));
183         return FALSE;
184     }
185
186     os->hids = NULL;
187
188 #   ifdef HAVE_USBHID_H
189     if (ioctl(os->fd, USB_GET_REPORT_ID, &report_id) < 0)
190     {
191         /*** XXX {report_id} may not be the right variable? ***/
192         fgWarning("error: %s%d: %s", UHIDDEV, report_id, strerror(errno));
193         return FALSE;
194     }
195
196     size = hid_report_size(rd, hid_input, report_id);
197 #   else
198     size = hid_report_size(rd, 0, hid_input);
199 #   endif
200     os->hid_data_buf = calloc(1, size);
201     os->hid_dlen = size;
202
203     is_joystick = 0;
204 #   ifdef HAVE_USBHID_H
205     d = hid_start_parse(rd, 1 << hid_input, report_id);
206 #   else
207     d = hid_start_parse(rd, 1 << hid_input);
208 #   endif
209     while (hid_get_item(d, &h))
210     {
211         int usage, page, interesting_hid;
212
213         page = HID_PAGE(h.usage);
214         usage = HID_USAGE(h.usage);
215
216         /* This test is somewhat too simplistic, but this is how MicroSoft
217         * does, so I guess it works for all joysticks/game pads. */
218         is_joystick = is_joystick ||
219             (h.kind == hid_collection &&
220              page == HUP_GENERIC_DESKTOP &&
221              (usage == HUG_JOYSTICK || usage == HUG_GAME_PAD));
222
223         if (h.kind != hid_input)
224             continue;
225
226         if (!is_joystick)
227             continue;
228
229         interesting_hid = TRUE;
230         if (page == HUP_GENERIC_DESKTOP)
231         {
232             switch (usage)
233             {
234                 case HUG_X:
235                 case HUG_RX:
236                 case HUG_Y:
237                 case HUG_RY:
238                 case HUG_Z:
239                 case HUG_RZ:
240                 case HUG_SLIDER:
241                     if (*num_axes < _JS_MAX_AXES)
242                     {
243                         os->axes_usage[*num_axes] = usage;
244                         (*num_axes)++;
245                     }
246                     break;
247                 case HUG_HAT_SWITCH:
248                     /* Allocate two axes for a hat */
249                     if (*num_axes + 1 < _JS_MAX_AXES)
250                     {
251                         os->axes_usage[*num_axes] = usage;
252                         (*num_axes)++;
253                         os->axes_usage[*num_axes] = usage;
254                         (*num_axes)++;
255                     }
256                     break;
257                 default:
258                     interesting_hid = FALSE;
259                     break;
260             }
261         }
262         else if (page == HUP_BUTTON)
263         {
264             interesting_hid = (usage > 0) &&
265                 (usage <= _JS_MAX_BUTTONS);
266
267             if (interesting_hid && usage - 1 > *num_buttons)
268                 *num_buttons = usage - 1;
269         }
270
271         if (interesting_hid)
272         {
273             h.next = os->hids;
274             os->hids = calloc(1, sizeof(struct hid_item));
275             *os->hids = h;
276         }
277     }
278     hid_end_parse(d);
279
280     return os->hids != NULL;
281 }
282 #    endif
283 #endif
284
285
286 /*this should be defined in a header file */
287 #define MAX_NUM_JOYSTICKS  2
288 extern SFG_Joystick *fgJoystick [ MAX_NUM_JOYSTICKS ];
289
290 void fgPlatformJoystickRawRead( SFG_Joystick* joy, int* buttons, float* axes )
291 {
292     int status;
293
294 #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
295     int len;
296
297     if ( joy->pJoystick.os->is_analog )
298     {
299         int status = read ( joy->pJoystick.os->fd, &joy->pJoystick.os->ajs, sizeof(joy->pJoystick.os->ajs) );
300         if ( status != sizeof(joy->pJoystick.os->ajs) ) {
301             perror ( joy->pJoystick.os->fname );
302             joy->error = GL_TRUE;
303             return;
304         }
305         if ( buttons != NULL )
306             *buttons = ( joy->pJoystick.os->ajs.b1 ? 1 : 0 ) | ( joy->pJoystick.os->ajs.b2 ? 2 : 0 );
307
308         if ( axes != NULL )
309         {
310             axes[0] = (float) joy->pJoystick.os->ajs.x;
311             axes[1] = (float) joy->pJoystick.os->ajs.y;
312         }
313
314         return;
315     }
316
317 #  ifdef HAVE_USB_JS
318     while ( ( len = read ( joy->pJoystick.os->fd, joy->pJoystick.os->hid_data_buf, joy->pJoystick.os->hid_dlen ) ) == joy->pJoystick.os->hid_dlen )
319     {
320         struct hid_item *h;
321
322         for  ( h = joy->pJoystick.os->hids; h; h = h->next )
323         {
324             int d = hid_get_data ( joy->pJoystick.os->hid_data_buf, h );
325
326             int page = HID_PAGE ( h->usage );
327             int usage = HID_USAGE ( h->usage );
328
329             if ( page == HUP_GENERIC_DESKTOP )
330             {
331                 int i;
332                 for ( i = 0; i < joy->num_axes; i++ )
333                     if (joy->pJoystick.os->axes_usage[i] == usage)
334                     {
335                         if (usage == HUG_HAT_SWITCH)
336                         {
337                             if (d < 0 || d > 8)
338                                 d = 0;  /* safety */
339                             joy->pJoystick.os->cache_axes[i] = (float)hatmap_x[d];
340                             joy->pJoystick.os->cache_axes[i + 1] = (float)hatmap_y[d];
341                         }
342                         else
343                         {
344                             joy->pJoystick.os->cache_axes[i] = (float)d;
345                         }
346                         break;
347                     }
348             }
349             else if (page == HUP_BUTTON)
350             {
351                if (usage > 0 && usage < _JS_MAX_BUTTONS + 1)
352                {
353                    if (d)
354                        joy->pJoystick.os->cache_buttons |=  (1 << ( usage - 1 ));
355                    else
356                        joy->pJoystick.os->cache_buttons &= ~(1 << ( usage - 1 ));
357                }
358             }
359         }
360     }
361     if ( len < 0 && errno != EAGAIN )
362     {
363         perror( joy->pJoystick.os->fname );
364         joy->error = 1;
365     }
366     if ( buttons != NULL ) *buttons = joy->pJoystick.os->cache_buttons;
367     if ( axes    != NULL )
368         memcpy ( axes, joy->pJoystick.os->cache_axes, sizeof(float) * joy->num_axes );
369 #  endif
370 #endif
371
372 #ifdef JS_NEW
373
374     while ( 1 )
375     {
376         status = read ( joy->pJoystick.fd, &joy->pJoystick.js, sizeof(struct js_event) );
377
378         if ( status != sizeof( struct js_event ) )
379         {
380             if ( errno == EAGAIN )
381             {
382                 /* Use the old values */
383                 if ( buttons )
384                     *buttons = joy->pJoystick.tmp_buttons;
385                 if ( axes )
386                     memcpy( axes, joy->pJoystick.tmp_axes,
387                             sizeof( float ) * joy->num_axes );
388                 return;
389             }
390
391             fgWarning ( "%s", joy->pJoystick.fname );
392             joy->error = GL_TRUE;
393             return;
394         }
395
396         switch ( joy->pJoystick.js.type & ~JS_EVENT_INIT )
397         {
398         case JS_EVENT_BUTTON:
399             if( joy->pJoystick.js.value == 0 ) /* clear the flag */
400                 joy->pJoystick.tmp_buttons &= ~( 1 << joy->pJoystick.js.number );
401             else
402                 joy->pJoystick.tmp_buttons |= ( 1 << joy->pJoystick.js.number );
403             break;
404
405         case JS_EVENT_AXIS:
406             if ( joy->pJoystick.js.number < joy->num_axes )
407             {
408                 joy->pJoystick.tmp_axes[ joy->pJoystick.js.number ] = ( float )joy->pJoystick.js.value;
409
410                 if( axes )
411                     memcpy( axes, joy->pJoystick.tmp_axes, sizeof(float) * joy->num_axes );
412             }
413             break;
414
415         default:
416             fgWarning ( "PLIB_JS: Unrecognised /dev/js return!?!" );
417
418             /* use the old values */
419
420             if ( buttons != NULL ) *buttons = joy->pJoystick.tmp_buttons;
421             if ( axes    != NULL )
422                 memcpy ( axes, joy->pJoystick.tmp_axes, sizeof(float) * joy->num_axes );
423
424             return;
425         }
426
427         if( buttons )
428             *buttons = joy->pJoystick.tmp_buttons;
429     }
430 #else
431
432     status = read( joy->pJoystick.fd, &joy->pJoystick.js, JS_RETURN );
433
434     if ( status != JS_RETURN )
435     {
436         fgWarning( "%s", joy->pJoystick.fname );
437         joy->error = GL_TRUE;
438         return;
439     }
440
441     if ( buttons )
442 #    if defined( __FreeBSD__ ) || defined(__FreeBSD_kernel__) || defined( __NetBSD__ )
443         *buttons = ( joy->pJoystick.js.b1 ? 1 : 0 ) | ( joy->pJoystick.js.b2 ? 2 : 0 );  /* XXX Should not be here -- BSD is handled earlier */
444 #    else
445         *buttons = joy->pJoystick.js.buttons;
446 #    endif
447
448     if ( axes )
449     {
450         axes[ 0 ] = (float) joy->pJoystick.js.x;
451         axes[ 1 ] = (float) joy->pJoystick.js.y;
452     }
453 #endif
454 }
455
456
457 void fgPlatformJoystickOpen( SFG_Joystick* joy )
458 {
459 #if defined( __FreeBSD__ ) || defined(__FreeBSD_kernel__)
460     int i = 0;
461        char *cp;
462 #endif
463 #ifdef JS_NEW
464        unsigned char u;
465     int i=0;
466 #else
467 #  if defined( __linux__ ) || TARGET_HOST_SOLARIS
468     int i = 0;
469     int counter = 0;
470 #  endif
471 #endif
472
473 #if defined( __FreeBSD__ ) || defined(__FreeBSD_kernel__)
474     for( i = 0; i < _JS_MAX_AXES; i++ )
475         joy->pJoystick.os->cache_axes[ i ] = 0.0f;
476
477     joy->pJoystick.os->cache_buttons = 0;
478
479     joy->pJoystick.os->fd = open( joy->pJoystick.os->fname, O_RDONLY | O_NONBLOCK);
480
481     if( joy->pJoystick.os->fd < 0 && errno == EACCES )
482         fgWarning ( "%s exists but is not readable by you", joy->pJoystick.os->fname );
483
484     joy->error =( joy->pJoystick.os->fd < 0 );
485
486     if( joy->error )
487         return;
488
489     joy->num_axes = 0;
490     joy->num_buttons = 0;
491     if( joy->pJoystick.os->is_analog )
492     {
493         FILE *joyfile;
494         char joyfname[ 1024 ];
495         int noargs, in_no_axes;
496
497         float axes [ _JS_MAX_AXES ];
498         int buttons[ _JS_MAX_AXES ];
499
500         joy->num_axes    =  2;
501         joy->num_buttons = 32;
502
503         fghJoystickRawRead( joy, buttons, axes );
504         joy->error = axes[ 0 ] < -1000000000.0f;
505         if( joy->error )
506             return;
507
508         snprintf( joyfname, sizeof(joyfname), "%s/.joy%drc", getenv( "HOME" ), joy->id );
509
510         joyfile = fopen( joyfname, "r" );
511         joy->error =( joyfile == NULL );
512         if( joy->error )
513             return;
514
515         noargs = fscanf( joyfile, "%d%f%f%f%f%f%f", &in_no_axes,
516                          &joy->min[ 0 ], &joy->center[ 0 ], &joy->max[ 0 ],
517                          &joy->min[ 1 ], &joy->center[ 1 ], &joy->max[ 1 ] );
518         joy->error = noargs != 7 || in_no_axes != _JS_MAX_AXES;
519         fclose( joyfile );
520         if( joy->error )
521             return;
522
523         for( i = 0; i < _JS_MAX_AXES; i++ )
524         {
525             joy->dead_band[ i ] = 0.0f;
526             joy->saturate [ i ] = 1.0f;
527         }
528
529         return;    /* End of analog code */
530     }
531
532 #    ifdef HAVE_USB_JS
533     if( ! fghJoystickInitializeHID( joy->pJoystick.os, &joy->num_axes,
534                                     &joy->num_buttons ) )
535     {
536         close( joy->pJoystick.os->fd );
537         joy->error = GL_TRUE;
538         return;
539     }
540
541     cp = strrchr( joy->pJoystick.os->fname, '/' );
542     if( cp )
543     {
544         if( fghJoystickFindUSBdev( &cp[1], joy->name, sizeof( joy->name ) ) ==
545             0 )
546             strcpy( joy->name, &cp[1] );
547     }
548
549     if( joy->num_axes > _JS_MAX_AXES )
550         joy->num_axes = _JS_MAX_AXES;
551
552     for( i = 0; i < _JS_MAX_AXES; i++ )
553     {
554         /* We really should get this from the HID, but that data seems
555          * to be quite unreliable for analog-to-USB converters. Punt for
556          * now.
557          */
558         if( joy->pJoystick.os->axes_usage[ i ] == HUG_HAT_SWITCH )
559         {
560             joy->max   [ i ] = 1.0f;
561             joy->center[ i ] = 0.0f;
562             joy->min   [ i ] = -1.0f;
563         }
564         else
565         {
566             joy->max   [ i ] = 255.0f;
567             joy->center[ i ] = 127.0f;
568             joy->min   [ i ] = 0.0f;
569         }
570
571         joy->dead_band[ i ] = 0.0f;
572         joy->saturate[ i ] = 1.0f;
573     }
574 #    endif
575 #endif
576
577 #if defined( __linux__ ) || TARGET_HOST_SOLARIS
578     /* Default for older Linux systems. */
579     joy->num_axes    =  2;
580     joy->num_buttons = 32;
581
582 #    ifdef JS_NEW
583     for( i = 0; i < _JS_MAX_AXES; i++ )
584         joy->pJoystick.tmp_axes[ i ] = 0.0f;
585
586     joy->pJoystick.tmp_buttons = 0;
587 #    endif
588
589     joy->pJoystick.fd = open( joy->pJoystick.fname, O_RDONLY );
590
591     joy->error =( joy->pJoystick.fd < 0 );
592
593     if( joy->error )
594         return;
595
596     /* Set the correct number of axes for the linux driver */
597 #    ifdef JS_NEW
598     /* Melchior Franz's fixes for big-endian Linuxes since writing
599      *  to the upper byte of an uninitialized word doesn't work.
600      *  9 April 2003
601      */
602     if(ioctl(joy->pJoystick.fd, JSIOCGAXES, &u) != -1)
603         joy->num_axes = u;
604     if(ioctl(joy->pJoystick.fd, JSIOCGBUTTONS, &u) != -1)
605         joy->num_buttons = u;
606     ioctl( joy->pJoystick.fd, JSIOCGNAME( sizeof( joy->name ) ), joy->name );
607     fcntl(joy->pJoystick.fd, F_SETFL, fcntl(joy->pJoystick.fd, F_GETFL) | O_NONBLOCK);
608 #    endif
609
610     /*
611      * The Linux driver seems to return 512 for all axes
612      * when no stick is present - but there is a chance
613      * that could happen by accident - so it's gotta happen
614      * on both axes for at least 100 attempts.
615      *
616      * PWO: shouldn't be that done somehow wiser on the kernel level?
617      */
618 #    ifndef JS_NEW
619     counter = 0;
620
621     do
622     {
623         fghJoystickRawRead( joy, NULL, joy->center );
624         counter++;
625     } while( !joy->error &&
626              counter < 100 &&
627              joy->center[ 0 ] == 512.0f &&
628              joy->center[ 1 ] == 512.0f );
629
630     if ( counter >= 100 )
631         joy->error = GL_TRUE;
632 #    endif
633
634     for( i = 0; i < _JS_MAX_AXES; i++ )
635     {
636 #    ifdef JS_NEW
637         joy->max   [ i ] =  32767.0f;
638         joy->center[ i ] =      0.0f;
639         joy->min   [ i ] = -32767.0f;
640 #    else
641         joy->max[ i ] = joy->center[ i ] * 2.0f;
642         joy->min[ i ] = 0.0f;
643 #    endif
644         joy->dead_band[ i ] = 0.0f;
645         joy->saturate [ i ] = 1.0f;
646     }
647 #endif
648 }
649
650
651 void fgPlatformJoystickInit( SFG_Joystick *fgJoystick[], int ident )
652 {
653 #if defined( __FreeBSD__ ) || defined(__FreeBSD_kernel__)
654     fgJoystick[ ident ]->id = ident;
655     fgJoystick[ ident ]->error = GL_FALSE;
656
657     fgJoystick[ ident ]->pJoystick.os = calloc( 1, sizeof( struct os_specific_s ) );
658     memset( fgJoystick[ ident ]->pJoystick.os, 0, sizeof( struct os_specific_s ) );
659     if( ident < USB_IDENT_OFFSET )
660         fgJoystick[ ident ]->pJoystick.os->is_analog = 1;
661     if( fgJoystick[ ident ]->pJoystick.os->is_analog )
662         snprintf( fgJoystick[ ident ]->pJoystick.os->fname, sizeof(fgJoystick[ ident ]->pJoystick.os->fname), "%s%d", AJSDEV, ident );
663     else
664         snprintf( fgJoystick[ ident ]->pJoystick.os->fname, sizeof(fgJoystick[ ident ]->pJoystick.os->fname), "%s%d", UHIDDEV,
665                  ident - USB_IDENT_OFFSET );
666 #elif defined( __linux__ )
667     fgJoystick[ ident ]->id = ident;
668     fgJoystick[ ident ]->error = GL_FALSE;
669
670     snprintf( fgJoystick[ident]->pJoystick.fname, sizeof(fgJoystick[ident]->pJoystick.fname), "/dev/input/js%d", ident );
671
672     if( access( fgJoystick[ ident ]->pJoystick.fname, F_OK ) != 0 )
673         snprintf( fgJoystick[ ident ]->pJoystick.fname, sizeof(fgJoystick[ ident ]->pJoystick.fname), "/dev/js%d", ident );
674 #endif
675 }
676
677
678 void fgPlatformJoystickClose ( int ident )
679 {
680 #if defined( __FreeBSD__ ) || defined(__FreeBSD_kernel__)
681     if( fgJoystick[ident]->pJoystick.os )
682     {
683         if( ! fgJoystick[ ident ]->error )
684             close( fgJoystick[ ident ]->pJoystick.os->fd );
685 #ifdef HAVE_USB_JS
686         if( fgJoystick[ ident ]->pJoystick.os->hids )
687             free (fgJoystick[ ident ]->pJoystick.os->hids);
688         if( fgJoystick[ ident ]->pJoystick.os->hid_data_buf )
689             free( fgJoystick[ ident ]->pJoystick.os->hid_data_buf );
690 #endif
691         free( fgJoystick[ident]->pJoystick.os );
692     }
693 #endif
694
695     if( ! fgJoystick[ident]->error )
696          close( fgJoystick[ ident ]->pJoystick.fd );
697 }
698