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