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 2063 2006-01-05 21:21:55Z 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 "js.h"
40 
41 #if defined (UL_BSD)
42 
43 #if defined(__NetBSD__) || defined(__FreeBSD__)
44 #define HAVE_USB_JS	1
45 #endif
46 
47 #include <string.h>
48 #include <errno.h>
49 #include <sys/ioctl.h>
50 #if defined(__FreeBSD__)
51 # include <sys/joystick.h>
52 #else
53 # include <machine/joystick.h>		// For analog joysticks
54 #endif
55 #ifdef HAVE_USB_JS
56 #if defined(__NetBSD__)
57 #ifdef HAVE_USBHID_H
58 #include <usbhid.h>
59 #else
60 #include <usb.h>
61 #endif
62 #elif defined(__FreeBSD__)
63 extern "C" {
64 #  if __FreeBSD_version < 500000
65 #    include <libusbhid.h>
66 #  else
67 #    define HAVE_USBHID_H 1
68 #    include <usbhid.h>
69 #    include <dev/usb/usb_ioctl.h>
70 #  endif
71 }
72 #endif
73 #include <dev/usb/usb.h>
74 #include <dev/usb/usbhid.h>
75 
76 /* Compatibility with older usb.h revisions */
77 #if !defined(USB_MAX_DEVNAMES) && defined(MAXDEVNAMES)
78 #define USB_MAX_DEVNAMES MAXDEVNAMES
79 #endif
80 #endif
81 
82 static int hatmap_x[9] = { 0, 0, 1, 1, 1, 0, -1, -1, -1 };
83 static int hatmap_y[9] = { 0, 1, 1, 0, -1, -1, -1, 0, 1 };
84 struct os_specific_s {
85   char             fname [128 ];
86   int              fd;
87   int              is_analog;
88   // The following structure members are specific to analog joysticks
89   struct joystick  ajs;
90 #ifdef HAVE_USB_JS
91   // The following structure members are specific to USB joysticks
92   struct hid_item *hids;
93   int              hid_dlen;
94   int              hid_offset;
95   char            *hid_data_buf;
96   int              axes_usage [ _JS_MAX_AXES ] ;
97 #endif
98   // We keep button and axes state ourselves, as they might not be updated
99   // on every read of a USB device
100   int              cache_buttons ;
101   float            cache_axes [ _JS_MAX_AXES ] ;
102   float            axes_minimum [ _JS_MAX_AXES ] ;
103   float            axes_maximum [ _JS_MAX_AXES ] ;
104 };
105 
106 // Idents lower than USB_IDENT_OFFSET are for analog joysticks.
107 #define USB_IDENT_OFFSET	2
108 
109 #define USBDEV "/dev/usb"
110 #define UHIDDEV "/dev/uhid"
111 #define AJSDEV "/dev/joy"
112 
113 #ifdef HAVE_USB_JS
114 /*
115  * findusbdev (and its helper, walkusbdev) try to locate the full name
116  * of a USB device. If /dev/usbN isn't readable, we punt and return the
117  * uhidN device name. We warn the user of this situation once.
118  */
119 static char *
walkusbdev(int f,char * dev,char * out,int outlen)120 walkusbdev(int f, char *dev, char *out, int outlen)
121 {
122   return NULL;
123 }
124 
125 static int
findusbdev(char * name,char * out,int outlen)126 findusbdev(char *name, char *out, int outlen)
127 {
128   return 0;
129 }
130 
joy_initialize_hid(struct os_specific_s * os,int * num_axes,int * num_buttons)131 static int joy_initialize_hid(struct os_specific_s *os,
132 	int *num_axes, int *num_buttons)
133 {
134   int size, is_joystick;
135 #ifdef HAVE_USBHID_H
136   int report_id = 0;
137 #endif
138   struct hid_data *d;
139   struct hid_item h;
140   report_desc_t rd;
141 
142   if ((rd = hid_get_report_desc(os->fd)) == 0)
143     {
144       fprintf(stderr, "error: %s: %s", os->fname, strerror(errno));
145       return FALSE;
146     }
147 
148   os->hids = NULL;
149 
150 #ifdef HAVE_USBHID_H
151   if (ioctl(os->fd, USB_GET_REPORT_ID, &report_id) < 0)
152     {
153       fprintf(stderr, "error: %s: %s", os->fname, strerror(errno));
154       return FALSE;
155     }
156 
157   size = hid_report_size(rd, hid_input, report_id);
158 #else
159   size = hid_report_size(rd, 0, hid_input);
160 #endif
161   os->hid_data_buf = new char[size];
162   os->hid_dlen = size;
163 
164   is_joystick = 0;
165 #ifdef HAVE_USBHID_H
166   d = hid_start_parse(rd, 1 << hid_input, report_id);
167 #else
168   d = hid_start_parse(rd, 1 << hid_input);
169 #endif
170   while (hid_get_item(d, &h))
171   {
172     int usage, page, interesting_hid;
173 
174     page = HID_PAGE(h.usage);
175     usage = HID_USAGE(h.usage);
176 
177     /* This test is somewhat too simplistic, but this is how MicroSoft
178      * does, so I guess it works for all joysticks/game pads. */
179     is_joystick = is_joystick ||
180       (h.kind == hid_collection &&
181        page == HUP_GENERIC_DESKTOP &&
182        (usage == HUG_JOYSTICK || usage == HUG_GAME_PAD));
183 
184     if (h.kind != hid_input)
185       continue;
186 
187     if (!is_joystick)
188       continue;
189 
190     interesting_hid = TRUE;
191     if (page == HUP_GENERIC_DESKTOP)
192     {
193        switch(usage) {
194          case HUG_X:
195          case HUG_RX:
196          case HUG_Y:
197          case HUG_RY:
198          case HUG_Z:
199          case HUG_RZ:
200          case HUG_SLIDER:
201          case HUG_DIAL:
202            if (*num_axes < _JS_MAX_AXES)
203            {
204              os->axes_usage[*num_axes] = usage;
205              os->axes_minimum[*num_axes] = h.logical_minimum;
206              os->axes_maximum[*num_axes] = h.logical_maximum;
207              (*num_axes)++;
208            }
209            break;
210          case HUG_HAT_SWITCH:
211            if (*num_axes + 1 < _JS_MAX_AXES)	// Allocate two axes for a hat
212            {
213              os->axes_usage[*num_axes] = usage;
214              (*num_axes)++;
215              os->axes_usage[*num_axes] = usage;
216              (*num_axes)++;
217            }
218            break;
219          default:
220            interesting_hid = FALSE;
221       }
222     }
223     else if (page == HUP_BUTTON)
224     {
225       interesting_hid = (usage > 0) && (usage <= _JS_MAX_BUTTONS);
226 
227       if (interesting_hid && usage - 1 > *num_buttons)
228       {
229         *num_buttons = usage - 1;
230       }
231     }
232 
233     if (interesting_hid)
234       {
235         h.next = os->hids;
236         os->hids = new struct hid_item;
237         *os->hids = h;
238       }
239   }
240   hid_end_parse(d);
241 
242   return (os->hids != NULL);
243 }
244 #endif
245 
open()246 void jsJoystick::open ()
247 {
248   char *cp;
249 
250   name [0] = '\0' ;
251 
252   for ( int i = 0 ; i < _JS_MAX_AXES ; i++ )
253     os->cache_axes [ i ] = 0.0f ;
254 
255   os->cache_buttons = 0 ;
256 
257   os->fd = ::open ( os->fname, O_RDONLY | O_NONBLOCK) ;
258 
259   if (os->fd < 0 && errno == EACCES)
260     fprintf(stderr, "%s exists but is not readable by you\n", os->fname);
261 
262   error = ( os->fd < 0 ) ;
263 
264   if ( error )
265     return ;
266 
267   num_axes = 0;
268   num_buttons = 0;
269   if ( os->is_analog )
270   {
271     num_axes    =  2 ;
272     num_buttons = 32 ;
273     FILE *joyfile ;
274     char joyfname [ 1024 ] ;
275     int noargs, in_no_axes ;
276 
277     float axes  [ _JS_MAX_AXES ] ;
278     int buttons [ _JS_MAX_AXES ] ;
279 
280     rawRead ( buttons, axes ) ;
281     error = axes[0] < -1000000000.0f && axes[1] < -1000000000.0f ;
282     if ( error )
283       return ;
284 
285     sprintf( joyfname, "%s/.joy%drc", ::getenv ( "HOME" ), id ) ;
286 
287     joyfile = fopen ( joyfname, "r" ) ;
288     error = ( joyfile == NULL ) ;
289     if ( error )
290     {
291       ulSetError ( UL_WARNING, "unable to open calibration file %s (%s), joystick %i disabled (you can generate the calibration file with the plib-jscal utility)",
292 		   joyfname, strerror ( errno ), id + 1 );
293       return ;
294     }
295 
296     noargs = fscanf ( joyfile, "%d%f%f%f%f%f%f", &in_no_axes,
297                       &min [ 0 ], &center [ 0 ], &max [ 0 ],
298                       &min [ 1 ], &center [ 1 ], &max [ 1 ] ) ;
299     error = noargs != 7 || in_no_axes != _JS_MAX_AXES ;
300     fclose ( joyfile ) ;
301     if ( error )
302       return ;
303 
304     for ( int i = 0 ; i < _JS_MAX_AXES ; i++ )
305     {
306       dead_band [ i ] = 0.0f ;
307       saturate  [ i ] = 1.0f ;
308     }
309 
310     return;	// End of analog code
311   }
312 
313 #ifdef HAVE_USB_JS
314   if ( !joy_initialize_hid(os, &num_axes, &num_buttons ) )
315   {
316      ::close(os->fd);
317      error = 1;
318      return;
319   }
320 
321   cp = strrchr(os->fname, '/');
322   if (cp) {
323     if (findusbdev(&cp[1], name, sizeof(name)) == 0)
324       strcpy(name, &cp[1]);
325   }
326 
327   if ( num_axes > _JS_MAX_AXES )
328     num_axes = _JS_MAX_AXES ;
329 
330   for ( int i = 0 ; i < _JS_MAX_AXES ; i++ )
331   {
332     if ( os->axes_usage [ i ] == HUG_HAT_SWITCH )
333     {
334       max       [ i ] = 1.0f ;
335       center    [ i ] = 0.0f ;
336       min       [ i ] = -1.0f ;
337     }
338     else
339     {
340       max       [ i ] = os->axes_maximum [ i ];
341       min       [ i ] = os->axes_minimum [ i ];
342       center    [ i ] = (max [ i ] + min [ i ]) / 2.0 ;
343     }
344     dead_band [ i ] = 0.0f ;
345     saturate  [ i ] = 1.0f ;
346   }
347 #endif
348 }
349 
350 
351 
close()352 void jsJoystick::close ()
353 {
354   if (os) {
355     if ( ! error )
356       ::close ( os->fd ) ;
357 #ifdef HAVE_USB_JS
358     if (os->hids)
359       delete os->hids;
360     if (os->hid_data_buf)
361       delete os->hid_data_buf;
362 #endif
363     delete os;
364   }
365 }
366 
367 
jsJoystick(int ident)368 jsJoystick::jsJoystick ( int ident )
369 {
370   id = ident ;
371   error = 0;
372 
373   os = new struct os_specific_s;
374   memset(os, 0, sizeof(struct os_specific_s));
375   if (ident < USB_IDENT_OFFSET)
376     os->is_analog = 1;
377   if (os->is_analog)
378     sprintf(os->fname, "%s%d", AJSDEV, ident);
379   else
380     sprintf(os->fname, "%s%d", UHIDDEV, ident - USB_IDENT_OFFSET);
381   open () ;
382 }
383 
384 
rawRead(int * buttons,float * axes)385 void jsJoystick::rawRead ( int *buttons, float *axes )
386 {
387   int len, usage, page, d;
388   struct hid_item *h;
389 
390   if ( error )
391   {
392     if ( buttons )
393       *buttons = 0 ;
394 
395     if ( axes )
396       for ( int i = 0 ; i < num_axes ; i++ )
397         axes[i] = 1500.0f ;
398 
399     return ;
400   }
401 
402   if ( os->is_analog )
403   {
404     int status = ::read ( os->fd, &os->ajs, sizeof(os->ajs) );
405     if ( status != sizeof(os->ajs) ) {
406       perror ( os->fname ) ;
407       setError () ;
408       return ;
409     }
410     if ( buttons != NULL )
411       *buttons = ( os->ajs.b1 ? 1 : 0 ) | ( os->ajs.b2 ? 2 : 0 ) ;
412 
413     if ( axes != NULL )
414     {
415       if ( os->ajs.x >= -1000000000 )
416         os->cache_axes[0] = os->ajs.x;
417       if ( os->ajs.y >= -1000000000 )
418         os->cache_axes[1] = os->ajs.y;
419 
420       axes[0] = os->cache_axes[0];
421       axes[1] = os->cache_axes[1];
422     }
423 
424     return;
425   }
426 
427 #ifdef HAVE_USB_JS
428   while ((len = ::read(os->fd, os->hid_data_buf, os->hid_dlen)) == os->hid_dlen)
429   {
430     for (h = os->hids; h; h = h->next)
431     {
432       d = hid_get_data(os->hid_data_buf, h);
433 
434       page = HID_PAGE(h->usage);
435       usage = HID_USAGE(h->usage);
436 
437       if (page == HUP_GENERIC_DESKTOP)
438       {
439         for (int i = 0; i < num_axes; i++)
440           if (os->axes_usage[i] == usage)
441           {
442             if (usage == HUG_HAT_SWITCH)
443             {
444               if (d < 0 || d > 8)
445                 d = 0;	// safety
446               os->cache_axes[i] = (float)hatmap_x[d];
447               os->cache_axes[i + 1] = (float)hatmap_y[d];
448             }
449             else
450             {
451               os->cache_axes[i] = (float)d;
452             }
453             break;
454           }
455       }
456       else if (page == HUP_BUTTON)
457       {
458          if (usage > 0 && usage < _JS_MAX_BUTTONS + 1)
459          {
460            if (d)
461              os->cache_buttons |= (1 << usage - 1) ;
462            else
463              os->cache_buttons &= ~(1 << usage - 1) ;
464          }
465       }
466     }
467   }
468   if (len < 0 && errno != EAGAIN)
469   {
470     perror( os->fname ) ;
471     setError () ;
472     error = 1;
473   }
474   if ( buttons != NULL ) *buttons = os->cache_buttons ;
475   if ( axes    != NULL )
476     memcpy ( axes, os->cache_axes, sizeof(float) * num_axes ) ;
477 #endif
478 }
479 
jsInit()480 void jsInit () {}
481 
482 #endif
483