1 /*
2      PLIB - A Suite of Portable Game Libraries
3      Copyright (C) 1998,2002  Steve Baker
4 
5      This library is free software; you can redistribute it and/or
6      modify it under the terms of the GNU Library General Public
7      License as published by the Free Software Foundation; either
8      version 2 of the License, or (at your option) any later version.
9 
10      This library is distributed in the hope that it will be useful,
11      but WITHOUT ANY WARRANTY; without even the implied warranty of
12      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13      Library General Public License for more details.
14 
15      You should have received a copy of the GNU Library General Public
16      License along with this library; if not, write to the Free Software
17      Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
18 
19      For further information visit http://plib.sourceforge.net
20 
21      $Id: jsBSD.cxx 2133 2008-07-18 14:32:22Z fayjf $
22 */
23 
24 /*
25  * Inspired by the X-Mame USB HID joystick driver for NetBSD and
26  * FreeBSD by Krister Walfridsson <cato@df.lth.se>.
27  * Incorporates the original analog joystick driver for BSD by
28  * Stephen Montgomery-Smith <stephen@math.missouri.edu>, with
29  * NetBSD mods courtesy of Rene Hexel.
30  *
31  * Bert Driehuis <driehuis@playbeing.org>
32  *
33  * Notes:
34  * Hats are mapped to two axes for now. A cleaner implementation requires
35  * an API extension, and to be useful for my devious purposes, FlightGear
36  * would need to understand that.
37  */
38 
39 #include "FlightGear_js.h"
40 
41 #if defined(__NetBSD__) || defined(__FreeBSD__)
42 #define HAVE_USB_JS	1
43 #endif
44 
45 #ifndef TRUE
46 #define TRUE  1
47 #define FALSE 0
48 #endif
49 
50 #include <simgear/props/props.hxx> /* for jsSetError and SG_WARN */
51 
52 #include <sys/param.h>
53 #include <fcntl.h>
54 
55 #include <unistd.h>
56 #include <string.h>
57 #include <errno.h>
58 #include <sys/ioctl.h>
59 #if defined(__FreeBSD__)
60 # include <sys/joystick.h>
61 #else
62 # include <machine/joystick.h>		// For analog joysticks
63 #endif
64 #ifdef HAVE_USB_JS
65 #if defined(__NetBSD__)
66 #ifdef HAVE_USBHID_H
67 #include <usbhid.h>
68 #else
69 #include <usb.h>
70 #endif
71 #elif defined(__FreeBSD__)
72 extern "C" {
73 #  if __FreeBSD_version < 500000
74 #    include <libusbhid.h>
75 #  else
76 #    define HAVE_USBHID_H 1
77 #    include <usbhid.h>
78 #    include <dev/usb/usb_ioctl.h>
79 #  endif
80 }
81 #endif
82 #include <dev/usb/usb.h>
83 #include <dev/usb/usbhid.h>
84 
85 /* Compatibility with older usb.h revisions */
86 #if !defined(USB_MAX_DEVNAMES) && defined(MAXDEVNAMES)
87 #define USB_MAX_DEVNAMES MAXDEVNAMES
88 #endif
89 #endif
90 
91 static int hatmap_x[9] = { 0, 0, 1, 1, 1, 0, -1, -1, -1 };
92 static int hatmap_y[9] = { 0, 1, 1, 0, -1, -1, -1, 0, 1 };
93 struct os_specific_s {
94   char             fname [128 ];
95   int              fd;
96   int              is_analog;
97   // The following structure members are specific to analog joysticks
98   struct joystick  ajs;
99 #ifdef HAVE_USB_JS
100   // The following structure members are specific to USB joysticks
101   struct hid_item *hids;
102   int              hid_dlen;
103   int              hid_offset;
104   char            *hid_data_buf;
105   int              axes_usage [ _JS_MAX_AXES ] ;
106 #endif
107   // We keep button and axes state ourselves, as they might not be updated
108   // on every read of a USB device
109   int              cache_buttons ;
110   float            cache_axes [ _JS_MAX_AXES ] ;
111   float            axes_minimum [ _JS_MAX_AXES ] ;
112   float            axes_maximum [ _JS_MAX_AXES ] ;
113 };
114 
115 // Idents lower than USB_IDENT_OFFSET are for analog joysticks.
116 #define USB_IDENT_OFFSET	2
117 
118 #define USBDEV "/dev/usb"
119 #define UHIDDEV "/dev/uhid"
120 #define AJSDEV "/dev/joy"
121 
122 #ifdef HAVE_USB_JS
123 /*
124  * findusbdev (and its helper, walkusbdev) try to locate the full name
125  * of a USB device. If /dev/usbN isn't readable, we punt and return the
126  * uhidN device name. We warn the user of this situation once.
127  */
128 static char *
walkusbdev(int f,char * dev,char * out,int outlen)129 walkusbdev(int f, char *dev, char *out, int outlen)
130 {
131   return NULL;
132 }
133 
134 static int
findusbdev(char * name,char * out,int outlen)135 findusbdev(char *name, char *out, int outlen)
136 {
137   return 0;
138 }
139 
joy_initialize_hid(struct os_specific_s * os,int * num_axes,int * num_buttons)140 static int joy_initialize_hid(struct os_specific_s *os,
141 	int *num_axes, int *num_buttons)
142 {
143   int size, is_joystick;
144 #ifdef HAVE_USBHID_H
145   int report_id = 0;
146 #endif
147   struct hid_data *d;
148   struct hid_item h;
149   report_desc_t rd;
150 
151   if ((rd = hid_get_report_desc(os->fd)) == 0)
152     {
153       fprintf(stderr, "error: %s: %s", os->fname, strerror(errno));
154       return FALSE;
155     }
156 
157   os->hids = NULL;
158 
159 #ifdef HAVE_USBHID_H
160   if (ioctl(os->fd, USB_GET_REPORT_ID, &report_id) < 0)
161     {
162       fprintf(stderr, "error: %s: %s", os->fname, strerror(errno));
163       return FALSE;
164     }
165 
166   size = hid_report_size(rd, hid_input, report_id);
167 #else
168   size = hid_report_size(rd, 0, hid_input);
169 #endif
170   os->hid_data_buf = new char[size];
171   os->hid_dlen = size;
172 
173   is_joystick = 0;
174 #ifdef HAVE_USBHID_H
175   d = hid_start_parse(rd, 1 << hid_input, report_id);
176 #else
177   d = hid_start_parse(rd, 1 << hid_input);
178 #endif
179   while (hid_get_item(d, &h))
180   {
181     int usage, page, interesting_hid;
182 
183     page = HID_PAGE(h.usage);
184     usage = HID_USAGE(h.usage);
185 
186     /* This test is somewhat too simplistic, but this is how MicroSoft
187      * does, so I guess it works for all joysticks/game pads. */
188     is_joystick = is_joystick ||
189       (h.kind == hid_collection &&
190        page == HUP_GENERIC_DESKTOP &&
191        (usage == HUG_JOYSTICK || usage == HUG_GAME_PAD));
192 
193     if (h.kind != hid_input)
194       continue;
195 
196     if (!is_joystick)
197       continue;
198 
199     interesting_hid = TRUE;
200     if (page == HUP_GENERIC_DESKTOP)
201     {
202        switch(usage) {
203          case HUG_X:
204          case HUG_RX:
205          case HUG_Y:
206          case HUG_RY:
207          case HUG_Z:
208          case HUG_RZ:
209          case HUG_SLIDER:
210            if (*num_axes < _JS_MAX_AXES)
211            {
212              os->axes_usage[*num_axes] = usage;
213              os->axes_minimum[*num_axes] = h.logical_minimum;
214              os->axes_maximum[*num_axes] = h.logical_maximum;
215              (*num_axes)++;
216            }
217            break;
218          case HUG_HAT_SWITCH:
219            if (*num_axes + 1 < _JS_MAX_AXES)	// Allocate two axes for a hat
220            {
221              os->axes_usage[*num_axes] = usage;
222              (*num_axes)++;
223              os->axes_usage[*num_axes] = usage;
224              (*num_axes)++;
225            }
226            break;
227          default:
228            interesting_hid = FALSE;
229       }
230     }
231     else if (page == HUP_BUTTON)
232     {
233       interesting_hid = (usage > 0) && (usage <= _JS_MAX_BUTTONS);
234 
235       if (interesting_hid && usage - 1 > *num_buttons)
236       {
237         *num_buttons = usage - 1;
238       }
239     }
240 
241     if (interesting_hid)
242       {
243         h.next = os->hids;
244         os->hids = new struct hid_item;
245         *os->hids = h;
246       }
247   }
248   hid_end_parse(d);
249 
250   return (os->hids != NULL);
251 }
252 #endif
253 
open()254 void jsJoystick::open ()
255 {
256   char *cp;
257 
258   name [0] = '\0' ;
259 
260   for ( int i = 0 ; i < _JS_MAX_AXES ; i++ )
261     os->cache_axes [ i ] = 0.0f ;
262 
263   os->cache_buttons = 0 ;
264 
265   os->fd = ::open ( os->fname, O_RDONLY | O_NONBLOCK) ;
266 
267   if (os->fd < 0 && errno == EACCES)
268     fprintf(stderr, "%s exists but is not readable by you\n", os->fname);
269 
270   error = ( os->fd < 0 ) ;
271 
272   if ( error )
273     return ;
274 
275   num_axes = 0;
276   num_buttons = 0;
277   if ( os->is_analog )
278   {
279     num_axes    =  2 ;
280     num_buttons = 32 ;
281     FILE *joyfile ;
282     char joyfname [ 1024 ] ;
283     int noargs, in_no_axes ;
284 
285     float axes  [ _JS_MAX_AXES ] ;
286     int buttons [ _JS_MAX_AXES ] ;
287 
288     rawRead ( buttons, axes ) ;
289     error = axes[0] < -1000000000.0f && axes[1] < -1000000000.0f ;
290     if ( error )
291       return ;
292 
293     sprintf( joyfname, "%s/.joy%drc", ::getenv ( "HOME" ), id ) ;
294 
295     joyfile = fopen ( joyfname, "r" ) ;
296     error = ( joyfile == NULL ) ;
297     if ( error )
298     {
299       jsSetError ( SG_WARN, "unable to open calibration file, you can generate "
300         "the calibration file with the plib-jscal utility" );
301       return ;
302     }
303 
304     noargs = fscanf ( joyfile, "%d%f%f%f%f%f%f", &in_no_axes,
305                       &min [ 0 ], &center [ 0 ], &max [ 0 ],
306                       &min [ 1 ], &center [ 1 ], &max [ 1 ] ) ;
307     error = noargs != 7 || in_no_axes != _JS_MAX_AXES ;
308     fclose ( joyfile ) ;
309     if ( error )
310       return ;
311 
312     for ( int i = 0 ; i < _JS_MAX_AXES ; i++ )
313     {
314       dead_band [ i ] = 0.0f ;
315       saturate  [ i ] = 1.0f ;
316     }
317 
318     return;	// End of analog code
319   }
320 
321 #ifdef HAVE_USB_JS
322   if ( !joy_initialize_hid(os, &num_axes, &num_buttons ) )
323   {
324      ::close(os->fd);
325      error = 1;
326      return;
327   }
328 
329   cp = strrchr(os->fname, '/');
330   if (cp) {
331     if (findusbdev(&cp[1], name, sizeof(name)) == 0)
332       strcpy(name, &cp[1]);
333   }
334 
335   if ( num_axes > _JS_MAX_AXES )
336     num_axes = _JS_MAX_AXES ;
337 
338   for ( int i = 0 ; i < _JS_MAX_AXES ; i++ )
339   {
340     if ( os->axes_usage [ i ] == HUG_HAT_SWITCH )
341     {
342       max       [ i ] = 1.0f ;
343       center    [ i ] = 0.0f ;
344       min       [ i ] = -1.0f ;
345     }
346     else
347     {
348       max       [ i ] = os->axes_maximum [ i ];
349       min       [ i ] = os->axes_minimum [ i ];
350       center    [ i ] = (max [ i ] + min [ i ]) / 2;
351     }
352     dead_band [ i ] = 0.0f ;
353     saturate  [ i ] = 1.0f ;
354   }
355 #endif
356 }
357 
358 
359 
close()360 void jsJoystick::close ()
361 {
362   if (os) {
363     if ( ! error )
364       ::close ( os->fd ) ;
365 #ifdef HAVE_USB_JS
366     if (os->hids)
367       delete os->hids;
368     if (os->hid_data_buf)
369       delete os->hid_data_buf;
370 #endif
371     delete os;
372   }
373 }
374 
375 
jsJoystick(int ident)376 jsJoystick::jsJoystick ( int ident )
377 {
378   id = ident ;
379   error = 0;
380 
381   os = new struct os_specific_s;
382   memset(os, 0, sizeof(struct os_specific_s));
383   if (ident < USB_IDENT_OFFSET)
384     os->is_analog = 1;
385   if (os->is_analog)
386     sprintf(os->fname, "%s%d", AJSDEV, ident);
387   else
388     sprintf(os->fname, "%s%d", UHIDDEV, ident - USB_IDENT_OFFSET);
389   open () ;
390 }
391 
392 
rawRead(int * buttons,float * axes)393 void jsJoystick::rawRead ( int *buttons, float *axes )
394 {
395   int len, usage, page, d;
396   struct hid_item *h;
397 
398   if ( error )
399   {
400     if ( buttons )
401       *buttons = 0 ;
402 
403     if ( axes )
404       for ( int i = 0 ; i < num_axes ; i++ )
405         axes[i] = 1500.0f ;
406 
407     return ;
408   }
409 
410   if ( os->is_analog )
411   {
412     int status = ::read ( os->fd, &os->ajs, sizeof(os->ajs) );
413     if ( status != sizeof(os->ajs) ) {
414       perror ( os->fname ) ;
415       setError () ;
416       return ;
417     }
418     if ( buttons != NULL )
419       *buttons = ( os->ajs.b1 ? 1 : 0 ) | ( os->ajs.b2 ? 2 : 0 ) ;
420 
421     if ( axes != NULL )
422     {
423       if ( os->ajs.x >= -1000000000 )
424         os->cache_axes[0] = os->ajs.x;
425       if ( os->ajs.y >= -1000000000 )
426         os->cache_axes[1] = os->ajs.y;
427 
428       axes[0] = os->cache_axes[0];
429       axes[1] = os->cache_axes[1];
430     }
431 
432     return;
433   }
434 
435 #ifdef HAVE_USB_JS
436   while ((len = ::read(os->fd, os->hid_data_buf, os->hid_dlen)) == os->hid_dlen)
437   {
438     for (h = os->hids; h; h = h->next)
439     {
440       d = hid_get_data(os->hid_data_buf, h);
441 
442       page = HID_PAGE(h->usage);
443       usage = HID_USAGE(h->usage);
444 
445       if (page == HUP_GENERIC_DESKTOP)
446       {
447         for (int i = 0; i < num_axes; i++)
448           if (os->axes_usage[i] == usage)
449           {
450             if (usage == HUG_HAT_SWITCH)
451             {
452               if (d < 0 || d > 8)
453                 d = 0;	// safety
454               os->cache_axes[i] = (float)hatmap_x[d];
455               os->cache_axes[i + 1] = (float)hatmap_y[d];
456             }
457             else
458             {
459               os->cache_axes[i] = (float)d;
460             }
461             break;
462           }
463       }
464       else if (page == HUP_BUTTON)
465       {
466          if (usage > 0 && usage < _JS_MAX_BUTTONS + 1)
467          {
468            if (d)
469              os->cache_buttons |= (1 << usage - 1) ;
470            else
471              os->cache_buttons &= ~(1 << usage - 1) ;
472          }
473       }
474     }
475   }
476   if (len < 0 && errno != EAGAIN)
477   {
478     perror( os->fname ) ;
479     setError () ;
480     error = 1;
481   }
482   if ( buttons != NULL ) *buttons = os->cache_buttons ;
483   if ( axes    != NULL )
484     memcpy ( axes, os->cache_axes, sizeof(float) * num_axes ) ;
485 #endif
486 }
487 
jsInit()488 void jsInit () {}
489