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 /*this should be defined in a header file */
45 #define MAX_NUM_JOYSTICKS  2
46 
47 #if defined(__NetBSD__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
48 #  ifdef HAVE_USB_JS
49 /*
50  * fghJoystickFindUSBdev (and its helper, fghJoystickWalkUSBdev) try to locate
51  * the full name of a USB device. If /dev/usbN isn't readable, we punt and
52  * return the uhidN device name. We warn the user of this situation once.
53  */
fghJoystickWalkUSBdev(int f,char * dev,char * out,int outlen)54 static char *fghJoystickWalkUSBdev(int f, char *dev, char *out, int outlen)
55 {
56 #if __FreeBSD_version < 800061
57   struct usb_device_info di;
58   int i, a;
59   char *cp;
60 
61   for (a = 1; a < USB_MAX_DEVICES; a++) {
62     di.udi_addr = a;
63     if (ioctl(f, USB_DEVICEINFO, &di) != 0)
64       return NULL;
65     for (i = 0; i < USB_MAX_DEVNAMES; i++)
66       if (di.udi_devnames[i][0] &&
67           strcmp(di.udi_devnames[i], dev) == 0) {
68         cp =  calloc( 1, strlen(di.udi_vendor) + strlen(di.udi_product) + 2);
69         strcpy(cp, di.udi_vendor);
70         strcat(cp, " ");
71         strcat(cp, di.udi_product);
72         strncpy(out, cp, outlen - 1);
73         out[outlen - 1] = 0;
74         free( cp );
75         return out;
76       }
77   }
78 #endif
79   return NULL;
80 }
81 
fghJoystickFindUSBdev(char * name,char * out,int outlen)82 static int fghJoystickFindUSBdev(char *name, char *out, int outlen)
83 {
84   int i, f;
85   char buf[50];
86   char *cp;
87   static int protection_warned = 0;
88 
89   for (i = 0; i < 16; i++) {
90     snprintf(buf, sizeof(buf), "%s%d", USBDEV, i);
91     f = open(buf, O_RDONLY);
92     if (f >= 0) {
93       cp = fghJoystickWalkUSBdev(f, name, out, outlen);
94       close(f);
95       if (cp)
96         return 1;
97     }
98     else if (errno == EACCES) {
99       if (!protection_warned) {
100         fgWarning ( "Can't open %s for read!", buf );
101         protection_warned = 1;
102       }
103     }
104   }
105   return 0;
106 }
107 
fghJoystickInitializeHID(struct os_specific_s * os,int * num_axes,int * num_buttons)108 static int fghJoystickInitializeHID(struct os_specific_s *os,
109        int *num_axes, int *num_buttons)
110 {
111     int size, is_joystick;
112 #   ifdef HAVE_USBHID_H
113         int report_id = 0;
114 #   endif
115     struct hid_data *d;
116     struct hid_item h;
117     report_desc_t rd;
118 
119     if ( ( rd = hid_get_report_desc( os->fd ) ) == 0 )
120     {
121         fgWarning ( "error: %s: %s", os->fname, strerror( errno ) );
122         return FALSE;
123     }
124 
125     os->hids = NULL;
126 
127 #   ifdef HAVE_USBHID_H
128         if( ioctl( os->fd, USB_GET_REPORT_ID, &report_id ) < 0)
129         {
130             /*** XXX {report_id} may not be the right variable? ***/
131             fgWarning ( "error: %s%d: %s", UHIDDEV, report_id, strerror( errno ) );
132             return FALSE;
133         }
134 
135         size = hid_report_size( rd, hid_input, report_id );
136 #   else
137         size = hid_report_size( rd, 0, hid_input );
138 #   endif
139     os->hid_data_buf = calloc( 1, size );
140     os->hid_dlen = size;
141 
142     is_joystick = 0;
143 #   ifdef HAVE_USBHID_H
144         d = hid_start_parse( rd, 1 << hid_input, report_id );
145 #   else
146         d = hid_start_parse( rd, 1 << hid_input );
147 #   endif
148         while( hid_get_item( d, &h ) )
149         {
150             int usage, page, interesting_hid;
151 
152             page = HID_PAGE( h.usage );
153             usage = HID_USAGE( h.usage );
154 
155             /* This test is somewhat too simplistic, but this is how MicroSoft
156              * does, so I guess it works for all joysticks/game pads. */
157             is_joystick = is_joystick ||
158                 ( h.kind == hid_collection &&
159                   page == HUP_GENERIC_DESKTOP &&
160                   ( usage == HUG_JOYSTICK || usage == HUG_GAME_PAD ) );
161 
162             if( h.kind != hid_input )
163                 continue;
164 
165             if( !is_joystick )
166                 continue;
167 
168             interesting_hid = TRUE;
169             if( page == HUP_GENERIC_DESKTOP )
170             {
171                 switch( usage )
172                 {
173                 case HUG_X:
174                 case HUG_RX:
175                 case HUG_Y:
176                 case HUG_RY:
177                 case HUG_Z:
178                 case HUG_RZ:
179                 case HUG_SLIDER:
180                     if( *num_axes < _JS_MAX_AXES )
181                     {
182                         os->axes_usage[ *num_axes ] = usage;
183                         ( *num_axes )++;
184                     }
185                     break;
186                 case HUG_HAT_SWITCH:
187                     /* Allocate two axes for a hat */
188                     if( *num_axes + 1 < _JS_MAX_AXES )
189                     {
190                         os->axes_usage[ *num_axes ] = usage;
191                         (*num_axes)++;
192                         os->axes_usage[ *num_axes ] = usage;
193                         (*num_axes)++;
194                     }
195                     break;
196                 default:
197                     interesting_hid = FALSE;
198                     break;
199                 }
200             }
201             else if( page == HUP_BUTTON )
202             {
203                 interesting_hid = ( usage > 0 ) &&
204                     ( usage <= _JS_MAX_BUTTONS );
205 
206                 if( interesting_hid && usage - 1 > *num_buttons )
207                     *num_buttons = usage - 1;
208             }
209 
210             if( interesting_hid )
211             {
212                 h.next = os->hids;
213                 os->hids = calloc( 1, sizeof ( struct hid_item ) );
214                 *os->hids = h;
215             }
216         }
217         hid_end_parse( d );
218 
219         return os->hids != NULL;
220 }
221 #  endif /* HAVE_USB_JS */
222 #endif /* FreeBSD or NetBSD */
223 
224 
225 extern SFG_Joystick *fgJoystick [ MAX_NUM_JOYSTICKS ];
226 
fgPlatformJoystickRawRead(SFG_Joystick * joy,int * buttons,float * axes)227 void fgPlatformJoystickRawRead( SFG_Joystick* joy, int* buttons, float* axes )
228 {
229     int status;
230 
231 #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
232     int len;
233 
234     if ( joy->pJoystick.os->is_analog )
235     {
236         int status = read ( joy->pJoystick.os->fd, &joy->pJoystick.os->ajs, sizeof(joy->pJoystick.os->ajs) );
237         if ( status != sizeof(joy->pJoystick.os->ajs) ) {
238             perror ( joy->pJoystick.os->fname );
239             joy->error = GL_TRUE;
240             return;
241         }
242         if ( buttons != NULL )
243             *buttons = ( joy->pJoystick.os->ajs.b1 ? 1 : 0 ) | ( joy->pJoystick.os->ajs.b2 ? 2 : 0 );
244 
245         if ( axes != NULL )
246         {
247             axes[0] = (float) joy->pJoystick.os->ajs.x;
248             axes[1] = (float) joy->pJoystick.os->ajs.y;
249         }
250 
251         return;
252     }
253 
254 #  ifdef HAVE_USB_JS
255     while ( ( len = read ( joy->pJoystick.os->fd, joy->pJoystick.os->hid_data_buf, joy->pJoystick.os->hid_dlen ) ) == joy->pJoystick.os->hid_dlen )
256     {
257         struct hid_item *h;
258 
259         for  ( h = joy->pJoystick.os->hids; h; h = h->next )
260         {
261             int d = hid_get_data ( joy->pJoystick.os->hid_data_buf, h );
262 
263             int page = HID_PAGE ( h->usage );
264             int usage = HID_USAGE ( h->usage );
265 
266             if ( page == HUP_GENERIC_DESKTOP )
267             {
268                 int i;
269                 for ( i = 0; i < joy->num_axes; i++ )
270                     if (joy->pJoystick.os->axes_usage[i] == usage)
271                     {
272                         if (usage == HUG_HAT_SWITCH)
273                         {
274                             if (d < 0 || d > 8)
275                                 d = 0;  /* safety */
276                             joy->pJoystick.os->cache_axes[i] = (float)hatmap_x[d];
277                             joy->pJoystick.os->cache_axes[i + 1] = (float)hatmap_y[d];
278                         }
279                         else
280                         {
281                             joy->pJoystick.os->cache_axes[i] = (float)d;
282                         }
283                         break;
284                     }
285             }
286             else if (page == HUP_BUTTON)
287             {
288                if (usage > 0 && usage < _JS_MAX_BUTTONS + 1)
289                {
290                    if (d)
291                        joy->pJoystick.os->cache_buttons |=  (1 << ( usage - 1 ));
292                    else
293                        joy->pJoystick.os->cache_buttons &= ~(1 << ( usage - 1 ));
294                }
295             }
296         }
297     }
298     if ( len < 0 && errno != EAGAIN )
299     {
300         perror( joy->pJoystick.os->fname );
301         joy->error = 1;
302     }
303     if ( buttons != NULL ) *buttons = joy->pJoystick.os->cache_buttons;
304     if ( axes    != NULL )
305         memcpy ( axes, joy->pJoystick.os->cache_axes, sizeof(float) * joy->num_axes );
306 #  endif
307 #endif
308 
309 #ifdef JS_NEW
310 
311     while ( 1 )
312     {
313         status = read ( joy->pJoystick.fd, &joy->pJoystick.js, sizeof(struct js_event) );
314 
315         if ( status != sizeof( struct js_event ) )
316         {
317             if ( errno == EAGAIN )
318             {
319                 /* Use the old values */
320                 if ( buttons )
321                     *buttons = joy->pJoystick.tmp_buttons;
322                 if ( axes )
323                     memcpy( axes, joy->pJoystick.tmp_axes,
324                             sizeof( float ) * joy->num_axes );
325                 return;
326             }
327 
328             fgWarning ( "%s", joy->pJoystick.fname );
329             joy->error = GL_TRUE;
330             return;
331         }
332 
333         switch ( joy->pJoystick.js.type & ~JS_EVENT_INIT )
334         {
335         case JS_EVENT_BUTTON:
336             if( joy->pJoystick.js.value == 0 ) /* clear the flag */
337                 joy->pJoystick.tmp_buttons &= ~( 1 << joy->pJoystick.js.number );
338             else
339                 joy->pJoystick.tmp_buttons |= ( 1 << joy->pJoystick.js.number );
340             break;
341 
342         case JS_EVENT_AXIS:
343             if ( joy->pJoystick.js.number < joy->num_axes )
344             {
345                 joy->pJoystick.tmp_axes[ joy->pJoystick.js.number ] = ( float )joy->pJoystick.js.value;
346 
347                 if( axes )
348                     memcpy( axes, joy->pJoystick.tmp_axes, sizeof(float) * joy->num_axes );
349             }
350             break;
351 
352         default:
353             fgWarning ( "PLIB_JS: Unrecognised /dev/js return!?!" );
354 
355             /* use the old values */
356 
357             if ( buttons != NULL ) *buttons = joy->pJoystick.tmp_buttons;
358             if ( axes    != NULL )
359                 memcpy ( axes, joy->pJoystick.tmp_axes, sizeof(float) * joy->num_axes );
360 
361             return;
362         }
363 
364         if( buttons )
365             *buttons = joy->pJoystick.tmp_buttons;
366     }
367 #else
368 
369     status = read( joy->pJoystick.fd, &joy->pJoystick.js, JS_RETURN );
370 
371     if ( status != JS_RETURN )
372     {
373         fgWarning( "%s", joy->pJoystick.fname );
374         joy->error = GL_TRUE;
375         return;
376     }
377 
378     if ( buttons )
379 #    if defined( __FreeBSD__ ) || defined(__FreeBSD_kernel__) || defined( __NetBSD__ )
380         *buttons = ( joy->pJoystick.js.b1 ? 1 : 0 ) | ( joy->pJoystick.js.b2 ? 2 : 0 );  /* XXX Should not be here -- BSD is handled earlier */
381 #    else
382         *buttons = joy->pJoystick.js.buttons;
383 #    endif
384 
385     if ( axes )
386     {
387         axes[ 0 ] = (float) joy->pJoystick.js.x;
388         axes[ 1 ] = (float) joy->pJoystick.js.y;
389     }
390 #endif
391 }
392 
393 
fgPlatformJoystickOpen(SFG_Joystick * joy)394 void fgPlatformJoystickOpen( SFG_Joystick* joy )
395 {
396 #if defined( __FreeBSD__ ) || defined(__FreeBSD_kernel__)
397     int i = 0;
398        char *cp;
399 #endif
400 #ifdef JS_NEW
401        unsigned char u;
402     int i=0;
403 #else
404 #  if defined( __linux__ ) || TARGET_HOST_SOLARIS
405     int i = 0;
406     int counter = 0;
407 #  endif
408 #endif
409 
410 #if defined( __FreeBSD__ ) || defined(__FreeBSD_kernel__)
411     for( i = 0; i < _JS_MAX_AXES; i++ )
412         joy->pJoystick.os->cache_axes[ i ] = 0.0f;
413 
414     joy->pJoystick.os->cache_buttons = 0;
415 
416     joy->pJoystick.os->fd = open( joy->pJoystick.os->fname, O_RDONLY | O_NONBLOCK);
417 
418     if( joy->pJoystick.os->fd < 0 && errno == EACCES )
419         fgWarning ( "%s exists but is not readable by you", joy->pJoystick.os->fname );
420 
421     joy->error =( joy->pJoystick.os->fd < 0 );
422 
423     if( joy->error )
424         return;
425 
426     joy->num_axes = 0;
427     joy->num_buttons = 0;
428     if( joy->pJoystick.os->is_analog )
429     {
430         FILE *joyfile;
431         char joyfname[ 1024 ];
432         int noargs, in_no_axes;
433 
434         float axes [ _JS_MAX_AXES ];
435         int buttons[ _JS_MAX_AXES ];
436 
437         joy->num_axes    =  2;
438         joy->num_buttons = 32;
439 
440         fgJoystickRawRead( joy, buttons, axes );
441         joy->error = axes[ 0 ] < -1000000000.0f;
442         if( joy->error )
443             return;
444 
445         snprintf( joyfname, sizeof(joyfname), "%s/.joy%drc", getenv( "HOME" ), joy->id );
446 
447         joyfile = fopen( joyfname, "r" );
448         joy->error =( joyfile == NULL );
449         if( joy->error )
450             return;
451 
452         noargs = fscanf( joyfile, "%d%f%f%f%f%f%f", &in_no_axes,
453                          &joy->min[ 0 ], &joy->center[ 0 ], &joy->max[ 0 ],
454                          &joy->min[ 1 ], &joy->center[ 1 ], &joy->max[ 1 ] );
455         joy->error = noargs != 7 || in_no_axes != _JS_MAX_AXES;
456         fclose( joyfile );
457         if( joy->error )
458             return;
459 
460         for( i = 0; i < _JS_MAX_AXES; i++ )
461         {
462             joy->dead_band[ i ] = 0.0f;
463             joy->saturate [ i ] = 1.0f;
464         }
465 
466         return;    /* End of analog code */
467     }
468 
469 #    ifdef HAVE_USB_JS
470     if( ! fghJoystickInitializeHID( joy->pJoystick.os, &joy->num_axes,
471                                     &joy->num_buttons ) )
472     {
473         close( joy->pJoystick.os->fd );
474         joy->error = GL_TRUE;
475         return;
476     }
477 
478     cp = strrchr( joy->pJoystick.os->fname, '/' );
479     if( cp )
480     {
481         if( fghJoystickFindUSBdev( &cp[1], joy->name, sizeof( joy->name ) ) ==
482             0 )
483             strcpy( joy->name, &cp[1] );
484     }
485 
486     if( joy->num_axes > _JS_MAX_AXES )
487         joy->num_axes = _JS_MAX_AXES;
488 
489     for( i = 0; i < _JS_MAX_AXES; i++ )
490     {
491         /* We really should get this from the HID, but that data seems
492          * to be quite unreliable for analog-to-USB converters. Punt for
493          * now.
494          */
495         if( joy->pJoystick.os->axes_usage[ i ] == HUG_HAT_SWITCH )
496         {
497             joy->max   [ i ] = 1.0f;
498             joy->center[ i ] = 0.0f;
499             joy->min   [ i ] = -1.0f;
500         }
501         else
502         {
503             joy->max   [ i ] = 255.0f;
504             joy->center[ i ] = 127.0f;
505             joy->min   [ i ] = 0.0f;
506         }
507 
508         joy->dead_band[ i ] = 0.0f;
509         joy->saturate[ i ] = 1.0f;
510     }
511 #    endif
512 #endif
513 
514 #if defined( __linux__ ) || TARGET_HOST_SOLARIS
515     /* Default for older Linux systems. */
516     joy->num_axes    =  2;
517     joy->num_buttons = 32;
518 
519 #    ifdef JS_NEW
520     for( i = 0; i < _JS_MAX_AXES; i++ )
521         joy->pJoystick.tmp_axes[ i ] = 0.0f;
522 
523     joy->pJoystick.tmp_buttons = 0;
524 #    endif
525 
526     joy->pJoystick.fd = open( joy->pJoystick.fname, O_RDONLY );
527 
528     joy->error =( joy->pJoystick.fd < 0 );
529 
530     if( joy->error )
531         return;
532 
533     /* Set the correct number of axes for the linux driver */
534 #    ifdef JS_NEW
535     /* Melchior Franz's fixes for big-endian Linuxes since writing
536      *  to the upper byte of an uninitialized word doesn't work.
537      *  9 April 2003
538      */
539     if(ioctl(joy->pJoystick.fd, JSIOCGAXES, &u) != -1)
540         joy->num_axes = u;
541     if(ioctl(joy->pJoystick.fd, JSIOCGBUTTONS, &u) != -1)
542         joy->num_buttons = u;
543     ioctl( joy->pJoystick.fd, JSIOCGNAME( sizeof( joy->name ) ), joy->name );
544     fcntl(joy->pJoystick.fd, F_SETFL, fcntl(joy->pJoystick.fd, F_GETFL) | O_NONBLOCK);
545 #    endif
546 
547     /*
548      * The Linux driver seems to return 512 for all axes
549      * when no stick is present - but there is a chance
550      * that could happen by accident - so it's gotta happen
551      * on both axes for at least 100 attempts.
552      *
553      * PWO: shouldn't be that done somehow wiser on the kernel level?
554      */
555 #    ifndef JS_NEW
556     counter = 0;
557 
558     do
559     {
560         fgJoystickRawRead( joy, NULL, joy->center );
561         counter++;
562     } while( !joy->error &&
563              counter < 100 &&
564              joy->center[ 0 ] == 512.0f &&
565              joy->center[ 1 ] == 512.0f );
566 
567     if ( counter >= 100 )
568         joy->error = GL_TRUE;
569 #    endif
570 
571     for( i = 0; i < _JS_MAX_AXES; i++ )
572     {
573 #    ifdef JS_NEW
574         joy->max   [ i ] =  32767.0f;
575         joy->center[ i ] =      0.0f;
576         joy->min   [ i ] = -32767.0f;
577 #    else
578         joy->max[ i ] = joy->center[ i ] * 2.0f;
579         joy->min[ i ] = 0.0f;
580 #    endif
581         joy->dead_band[ i ] = 0.0f;
582         joy->saturate [ i ] = 1.0f;
583     }
584 #endif
585 }
586 
fgJoystickRawRead(SFG_Joystick * joy,int * buttons,float * axes)587 void fgJoystickRawRead( SFG_Joystick* joy, int* buttons, float* axes )
588 {
589     int i;
590 
591     /* Defaults */
592     if( buttons )
593         *buttons = 0;
594 
595     if( axes )
596         for( i = 0; i < joy->num_axes; i++ )
597             axes[ i ] = 1500.0f;
598 
599     if( joy->error )
600         return;
601 
602 	fgPlatformJoystickRawRead ( joy, buttons, axes );
603 }
604 
fgPlatformJoystickInit(SFG_Joystick * fgJoystick[],int ident)605 void fgPlatformJoystickInit( SFG_Joystick *fgJoystick[], int ident )
606 {
607 #if defined( __FreeBSD__ ) || defined(__FreeBSD_kernel__)
608     fgJoystick[ ident ]->id = ident;
609     fgJoystick[ ident ]->error = GL_FALSE;
610 
611     fgJoystick[ ident ]->pJoystick.os = calloc( 1, sizeof( struct os_specific_s ) );
612     memset( fgJoystick[ ident ]->pJoystick.os, 0, sizeof( struct os_specific_s ) );
613     if( ident < USB_IDENT_OFFSET )
614         fgJoystick[ ident ]->pJoystick.os->is_analog = 1;
615     if( fgJoystick[ ident ]->pJoystick.os->is_analog )
616         snprintf( fgJoystick[ ident ]->pJoystick.os->fname, sizeof(fgJoystick[ ident ]->pJoystick.os->fname), "%s%d", AJSDEV, ident );
617     else
618         snprintf( fgJoystick[ ident ]->pJoystick.os->fname, sizeof(fgJoystick[ ident ]->pJoystick.os->fname), "%s%d", UHIDDEV,
619                  ident - USB_IDENT_OFFSET );
620 #elif defined( __linux__ )
621     fgJoystick[ ident ]->id = ident;
622     fgJoystick[ ident ]->error = GL_FALSE;
623 
624     snprintf( fgJoystick[ident]->pJoystick.fname, sizeof(fgJoystick[ident]->pJoystick.fname), "/dev/input/js%d", ident );
625 
626     if( access( fgJoystick[ ident ]->pJoystick.fname, F_OK ) != 0 )
627         snprintf( fgJoystick[ ident ]->pJoystick.fname, sizeof(fgJoystick[ ident ]->pJoystick.fname), "/dev/js%d", ident );
628 #endif
629 }
630 
631 
fgPlatformJoystickClose(int ident)632 void fgPlatformJoystickClose ( int ident )
633 {
634 #if defined( __FreeBSD__ ) || defined(__FreeBSD_kernel__)
635     if( fgJoystick[ident]->pJoystick.os )
636     {
637         if( ! fgJoystick[ ident ]->error )
638             close( fgJoystick[ ident ]->pJoystick.os->fd );
639 #ifdef HAVE_USB_JS
640         if( fgJoystick[ ident ]->pJoystick.os->hids )
641             free (fgJoystick[ ident ]->pJoystick.os->hids);
642         if( fgJoystick[ ident ]->pJoystick.os->hid_data_buf )
643             free( fgJoystick[ ident ]->pJoystick.os->hid_data_buf );
644 #endif
645         free( fgJoystick[ident]->pJoystick.os );
646     }
647 #endif
648 
649     if( ! fgJoystick[ident]->error )
650          close( fgJoystick[ ident ]->pJoystick.fd );
651 }
652 
653