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 ], ¢er [ 0 ], &max [ 0 ],
306 &min [ 1 ], ¢er [ 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