SDL-1.2.14
[sdl_omap.git] / src / joystick / bsd / SDL_sysjoystick.c
CommitLineData
e14743d1 1/*
2 SDL - Simple DirectMedia Layer
3 Copyright (C) 1997-2009 Sam Lantinga
4
5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 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 Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public
16 License along with this library; if not, write to the Free Software
17 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18
19 Sam Lantinga
20 slouken@libsdl.org
21*/
22#include "SDL_config.h"
23
24#ifdef SDL_JOYSTICK_USBHID
25
26/*
27 * Joystick driver for the uhid(4) interface found in OpenBSD,
28 * NetBSD and FreeBSD.
29 *
30 * Maintainer: <vedge at csoft.org>
31 */
32
33#include <sys/param.h>
34
35#include <unistd.h>
36#include <fcntl.h>
37#include <errno.h>
38
39#ifndef __FreeBSD_kernel_version
40#define __FreeBSD_kernel_version __FreeBSD_version
41#endif
42
43#if defined(HAVE_USB_H)
44#include <usb.h>
45#endif
46#ifdef __DragonFly__
47#include <bus/usb/usb.h>
48#include <bus/usb/usbhid.h>
49#else
50#include <dev/usb/usb.h>
51#include <dev/usb/usbhid.h>
52#endif
53
54#if defined(HAVE_USBHID_H)
55#include <usbhid.h>
56#elif defined(HAVE_LIBUSB_H)
57#include <libusb.h>
58#elif defined(HAVE_LIBUSBHID_H)
59#include <libusbhid.h>
60#endif
61
62#ifdef __FREEBSD__
63#ifndef __DragonFly__
64#include <osreldate.h>
65#endif
66#include <sys/joystick.h>
67#endif
68
69#if SDL_JOYSTICK_USBHID_MACHINE_JOYSTICK_H
70#include <machine/joystick.h>
71#endif
72
73#include "SDL_joystick.h"
74#include "../SDL_sysjoystick.h"
75#include "../SDL_joystick_c.h"
76
77#define MAX_UHID_JOYS 4
78#define MAX_JOY_JOYS 2
79#define MAX_JOYS (MAX_UHID_JOYS + MAX_JOY_JOYS)
80
81#if defined(__FREEBSD__) && (__FreeBSD_kernel_version > 800063)
82struct usb_ctl_report {
83 int ucr_report;
84 u_char ucr_data[1024]; /* filled data size will vary */
85};
86#endif
87
88struct report {
89 struct usb_ctl_report *buf; /* Buffer */
90 size_t size; /* Buffer size */
91 int rid; /* Report ID */
92 enum {
93 SREPORT_UNINIT,
94 SREPORT_CLEAN,
95 SREPORT_DIRTY
96 } status;
97};
98
99static struct {
100 int uhid_report;
101 hid_kind_t kind;
102 const char *name;
103} const repinfo[] = {
104 { UHID_INPUT_REPORT, hid_input, "input" },
105 { UHID_OUTPUT_REPORT, hid_output, "output" },
106 { UHID_FEATURE_REPORT, hid_feature, "feature" }
107};
108
109enum {
110 REPORT_INPUT = 0,
111 REPORT_OUTPUT = 1,
112 REPORT_FEATURE = 2
113};
114
115enum {
116 JOYAXE_X,
117 JOYAXE_Y,
118 JOYAXE_Z,
119 JOYAXE_SLIDER,
120 JOYAXE_WHEEL,
121 JOYAXE_RX,
122 JOYAXE_RY,
123 JOYAXE_RZ,
124 JOYAXE_count
125};
126
127struct joystick_hwdata {
128 int fd;
129 char *path;
130 enum {
131 BSDJOY_UHID, /* uhid(4) */
132 BSDJOY_JOY /* joy(4) */
133 } type;
134 struct report_desc *repdesc;
135 struct report inreport;
136 int axis_map[JOYAXE_count]; /* map present JOYAXE_* to 0,1,..*/
137 int x;
138 int y;
139 int xmin;
140 int ymin;
141 int xmax;
142 int ymax;
143};
144
145static char *joynames[MAX_JOYS];
146static char *joydevnames[MAX_JOYS];
147
148static int report_alloc(struct report *, struct report_desc *, int);
149static void report_free(struct report *);
150
151#if defined(USBHID_UCR_DATA) || (defined(__FREEBSD__) && (__FreeBSD_kernel_version > 800063))
152#define REP_BUF_DATA(rep) ((rep)->buf->ucr_data)
153#else
154#define REP_BUF_DATA(rep) ((rep)->buf->data)
155#endif
156
157int
158SDL_SYS_JoystickInit(void)
159{
160 char s[16];
161 int i, fd;
162
163 SDL_numjoysticks = 0;
164
165 SDL_memset(joynames, 0, sizeof(joynames));
166 SDL_memset(joydevnames, 0, sizeof(joydevnames));
167
168 for (i = 0; i < MAX_UHID_JOYS; i++) {
169 SDL_Joystick nj;
170
171 SDL_snprintf(s, SDL_arraysize(s), "/dev/uhid%d", i);
172
173 nj.index = SDL_numjoysticks;
174 joynames[nj.index] = strdup(s);
175
176 if (SDL_SYS_JoystickOpen(&nj) == 0) {
177 SDL_SYS_JoystickClose(&nj);
178 SDL_numjoysticks++;
179 } else {
180 SDL_free(joynames[nj.index]);
181 joynames[nj.index] = NULL;
182 }
183 }
184 for (i = 0; i < MAX_JOY_JOYS; i++) {
185 SDL_snprintf(s, SDL_arraysize(s), "/dev/joy%d", i);
186 fd = open(s, O_RDONLY);
187 if (fd != -1) {
188 joynames[SDL_numjoysticks++] = strdup(s);
189 close(fd);
190 }
191 }
192
193 /* Read the default USB HID usage table. */
194 hid_init(NULL);
195
196 return (SDL_numjoysticks);
197}
198
199const char *
200SDL_SYS_JoystickName(int index)
201{
202 if (joydevnames[index] != NULL) {
203 return (joydevnames[index]);
204 }
205 return (joynames[index]);
206}
207
208static int
209usage_to_joyaxe(unsigned usage)
210{
211 int joyaxe;
212 switch (usage) {
213 case HUG_X:
214 joyaxe = JOYAXE_X; break;
215 case HUG_Y:
216 joyaxe = JOYAXE_Y; break;
217 case HUG_Z:
218 joyaxe = JOYAXE_Z; break;
219 case HUG_SLIDER:
220 joyaxe = JOYAXE_SLIDER; break;
221 case HUG_WHEEL:
222 joyaxe = JOYAXE_WHEEL; break;
223 case HUG_RX:
224 joyaxe = JOYAXE_RX; break;
225 case HUG_RY:
226 joyaxe = JOYAXE_RY; break;
227 case HUG_RZ:
228 joyaxe = JOYAXE_RZ; break;
229 default:
230 joyaxe = -1;
231 }
232 return joyaxe;
233}
234
235static unsigned
236hatval_to_sdl(Sint32 hatval)
237{
238 static const unsigned hat_dir_map[8] = {
239 SDL_HAT_UP, SDL_HAT_RIGHTUP, SDL_HAT_RIGHT, SDL_HAT_RIGHTDOWN,
240 SDL_HAT_DOWN, SDL_HAT_LEFTDOWN, SDL_HAT_LEFT, SDL_HAT_LEFTUP
241 };
242 unsigned result;
243 if ((hatval & 7) == hatval)
244 result = hat_dir_map[hatval];
245 else
246 result = SDL_HAT_CENTERED;
247 return result;
248}
249
250
251int
252SDL_SYS_JoystickOpen(SDL_Joystick *joy)
253{
254 char *path = joynames[joy->index];
255 struct joystick_hwdata *hw;
256 struct hid_item hitem;
257 struct hid_data *hdata;
258 struct report *rep;
259 int fd;
260 int i;
261
262 fd = open(path, O_RDONLY);
263 if (fd == -1) {
264 SDL_SetError("%s: %s", path, strerror(errno));
265 return (-1);
266 }
267
268 hw = (struct joystick_hwdata *)SDL_malloc(sizeof(struct joystick_hwdata));
269 if (hw == NULL) {
270 SDL_OutOfMemory();
271 close(fd);
272 return (-1);
273 }
274 joy->hwdata = hw;
275 hw->fd = fd;
276 hw->path = strdup(path);
277 hw->x = 0;
278 hw->y = 0;
279 hw->xmin = 0xffff;
280 hw->ymin = 0xffff;
281 hw->xmax = 0;
282 hw->ymax = 0;
283 if (! SDL_strncmp(path, "/dev/joy", 8)) {
284 hw->type = BSDJOY_JOY;
285 joy->naxes = 2;
286 joy->nbuttons = 2;
287 joy->nhats = 0;
288 joy->nballs = 0;
289 joydevnames[joy->index] = strdup("Gameport joystick");
290 goto usbend;
291 } else {
292 hw->type = BSDJOY_UHID;
293 }
294
295 {
296 int ax;
297 for (ax = 0; ax < JOYAXE_count; ax++)
298 hw->axis_map[ax] = -1;
299 }
300 hw->repdesc = hid_get_report_desc(fd);
301 if (hw->repdesc == NULL) {
302 SDL_SetError("%s: USB_GET_REPORT_DESC: %s", hw->path,
303 strerror(errno));
304 goto usberr;
305 }
306#if defined(__FREEBSD__) && (__FreeBSD_kernel_version > 800063)
307 rep->rid = hid_get_report_id(fd);
308 if (rep->rid < 0) {
309#else
310 rep = &hw->inreport;
311 if (ioctl(fd, USB_GET_REPORT_ID, &rep->rid) < 0) {
312#endif
313 rep->rid = -1; /* XXX */
314 }
315 if (report_alloc(rep, hw->repdesc, REPORT_INPUT) < 0) {
316 goto usberr;
317 }
318 if (rep->size <= 0) {
319 SDL_SetError("%s: Input report descriptor has invalid length",
320 hw->path);
321 goto usberr;
322 }
323
324#if defined(USBHID_NEW) || (defined(__FREEBSD__) && __FreeBSD_kernel_version >= 500111)
325 hdata = hid_start_parse(hw->repdesc, 1 << hid_input, rep->rid);
326#else
327 hdata = hid_start_parse(hw->repdesc, 1 << hid_input);
328#endif
329 if (hdata == NULL) {
330 SDL_SetError("%s: Cannot start HID parser", hw->path);
331 goto usberr;
332 }
333 joy->naxes = 0;
334 joy->nbuttons = 0;
335 joy->nhats = 0;
336 joy->nballs = 0;
337 for (i=0; i<JOYAXE_count; i++)
338 hw->axis_map[i] = -1;
339
340 while (hid_get_item(hdata, &hitem) > 0) {
341 char *sp;
342 const char *s;
343
344 switch (hitem.kind) {
345 case hid_collection:
346 switch (HID_PAGE(hitem.usage)) {
347 case HUP_GENERIC_DESKTOP:
348 switch (HID_USAGE(hitem.usage)) {
349 case HUG_JOYSTICK:
350 case HUG_GAME_PAD:
351 s = hid_usage_in_page(hitem.usage);
352 sp = SDL_malloc(SDL_strlen(s) + 5);
353 SDL_snprintf(sp, SDL_strlen(s) + 5, "%s (%d)", s,
354 joy->index);
355 joydevnames[joy->index] = sp;
356 }
357 }
358 break;
359 case hid_input:
360 switch (HID_PAGE(hitem.usage)) {
361 case HUP_GENERIC_DESKTOP: {
362 unsigned usage = HID_USAGE(hitem.usage);
363 int joyaxe = usage_to_joyaxe(usage);
364 if (joyaxe >= 0) {
365 hw->axis_map[joyaxe] = 1;
366 } else if (usage == HUG_HAT_SWITCH) {
367 joy->nhats++;
368 }
369 break;
370 }
371 case HUP_BUTTON:
372 joy->nbuttons++;
373 break;
374 default:
375 break;
376 }
377 break;
378 default:
379 break;
380 }
381 }
382 hid_end_parse(hdata);
383 for (i=0; i<JOYAXE_count; i++)
384 if (hw->axis_map[i] > 0)
385 hw->axis_map[i] = joy->naxes++;
386
387usbend:
388 /* The poll blocks the event thread. */
389 fcntl(fd, F_SETFL, O_NONBLOCK);
390
391 return (0);
392usberr:
393 close(hw->fd);
394 SDL_free(hw->path);
395 SDL_free(hw);
396 return (-1);
397}
398
399void
400SDL_SYS_JoystickUpdate(SDL_Joystick *joy)
401{
402 struct hid_item hitem;
403 struct hid_data *hdata;
404 struct report *rep;
405 int nbutton, naxe = -1;
406 Sint32 v;
407
408#if defined(__FREEBSD__) || SDL_JOYSTICK_USBHID_MACHINE_JOYSTICK_H
409 struct joystick gameport;
410
411 if (joy->hwdata->type == BSDJOY_JOY) {
412 if (read(joy->hwdata->fd, &gameport, sizeof gameport) != sizeof gameport)
413 return;
414 if (abs(joy->hwdata->x - gameport.x) > 8) {
415 joy->hwdata->x = gameport.x;
416 if (joy->hwdata->x < joy->hwdata->xmin) {
417 joy->hwdata->xmin = joy->hwdata->x;
418 }
419 if (joy->hwdata->x > joy->hwdata->xmax) {
420 joy->hwdata->xmax = joy->hwdata->x;
421 }
422 if (joy->hwdata->xmin == joy->hwdata->xmax) {
423 joy->hwdata->xmin--;
424 joy->hwdata->xmax++;
425 }
426 v = (Sint32)joy->hwdata->x;
427 v -= (joy->hwdata->xmax + joy->hwdata->xmin + 1)/2;
428 v *= 32768/((joy->hwdata->xmax - joy->hwdata->xmin + 1)/2);
429 SDL_PrivateJoystickAxis(joy, 0, v);
430 }
431 if (abs(joy->hwdata->y - gameport.y) > 8) {
432 joy->hwdata->y = gameport.y;
433 if (joy->hwdata->y < joy->hwdata->ymin) {
434 joy->hwdata->ymin = joy->hwdata->y;
435 }
436 if (joy->hwdata->y > joy->hwdata->ymax) {
437 joy->hwdata->ymax = joy->hwdata->y;
438 }
439 if (joy->hwdata->ymin == joy->hwdata->ymax) {
440 joy->hwdata->ymin--;
441 joy->hwdata->ymax++;
442 }
443 v = (Sint32)joy->hwdata->y;
444 v -= (joy->hwdata->ymax + joy->hwdata->ymin + 1)/2;
445 v *= 32768/((joy->hwdata->ymax - joy->hwdata->ymin + 1)/2);
446 SDL_PrivateJoystickAxis(joy, 1, v);
447 }
448 if (gameport.b1 != joy->buttons[0]) {
449 SDL_PrivateJoystickButton(joy, 0, gameport.b1);
450 }
451 if (gameport.b2 != joy->buttons[1]) {
452 SDL_PrivateJoystickButton(joy, 1, gameport.b2);
453 }
454 return;
455 }
456#endif /* defined(__FREEBSD__) || SDL_JOYSTICK_USBHID_MACHINE_JOYSTICK_H */
457
458 rep = &joy->hwdata->inreport;
459
460 if (read(joy->hwdata->fd, REP_BUF_DATA(rep), rep->size) != rep->size) {
461 return;
462 }
463#if defined(USBHID_NEW) || (defined(__FREEBSD__) && __FreeBSD_kernel_version >= 500111)
464 hdata = hid_start_parse(joy->hwdata->repdesc, 1 << hid_input, rep->rid);
465#else
466 hdata = hid_start_parse(joy->hwdata->repdesc, 1 << hid_input);
467#endif
468 if (hdata == NULL) {
469 fprintf(stderr, "%s: Cannot start HID parser\n",
470 joy->hwdata->path);
471 return;
472 }
473
474 for (nbutton = 0; hid_get_item(hdata, &hitem) > 0;) {
475 switch (hitem.kind) {
476 case hid_input:
477 switch (HID_PAGE(hitem.usage)) {
478 case HUP_GENERIC_DESKTOP: {
479 unsigned usage = HID_USAGE(hitem.usage);
480 int joyaxe = usage_to_joyaxe(usage);
481 if (joyaxe >= 0) {
482 naxe = joy->hwdata->axis_map[joyaxe];
483 /* scaleaxe */
484 v = (Sint32)hid_get_data(REP_BUF_DATA(rep),
485 &hitem);
486 v -= (hitem.logical_maximum + hitem.logical_minimum + 1)/2;
487 v *= 32768/((hitem.logical_maximum - hitem.logical_minimum + 1)/2);
488 if (v != joy->axes[naxe]) {
489 SDL_PrivateJoystickAxis(joy, naxe, v);
490 }
491 } else if (usage == HUG_HAT_SWITCH) {
492 v = (Sint32)hid_get_data(REP_BUF_DATA(rep),
493 &hitem);
494 SDL_PrivateJoystickHat(joy, 0,
495 hatval_to_sdl(v)-hitem.logical_minimum);
496 }
497 break;
498 }
499 case HUP_BUTTON:
500 v = (Sint32)hid_get_data(REP_BUF_DATA(rep),
501 &hitem);
502 if (joy->buttons[nbutton] != v) {
503 SDL_PrivateJoystickButton(joy,
504 nbutton, v);
505 }
506 nbutton++;
507 break;
508 default:
509 continue;
510 }
511 break;
512 default:
513 break;
514 }
515 }
516 hid_end_parse(hdata);
517
518 return;
519}
520
521/* Function to close a joystick after use */
522void
523SDL_SYS_JoystickClose(SDL_Joystick *joy)
524{
525 if (SDL_strncmp(joy->hwdata->path, "/dev/joy", 8)) {
526 report_free(&joy->hwdata->inreport);
527 hid_dispose_report_desc(joy->hwdata->repdesc);
528 }
529 close(joy->hwdata->fd);
530 SDL_free(joy->hwdata->path);
531 SDL_free(joy->hwdata);
532
533 return;
534}
535
536void
537SDL_SYS_JoystickQuit(void)
538{
539 int i;
540
541 for (i = 0; i < MAX_JOYS; i++) {
542 if (joynames[i] != NULL)
543 SDL_free(joynames[i]);
544 if (joydevnames[i] != NULL)
545 SDL_free(joydevnames[i]);
546 }
547
548 return;
549}
550
551static int
552report_alloc(struct report *r, struct report_desc *rd, int repind)
553{
554 int len;
555
556#ifdef __DragonFly__
557 len = hid_report_size(rd, r->rid, repinfo[repind].kind);
558#elif __FREEBSD__
559# if (__FreeBSD_kernel_version >= 460000) || defined(__FreeBSD_kernel__)
560# if (__FreeBSD_kernel_version <= 500111)
561 len = hid_report_size(rd, r->rid, repinfo[repind].kind);
562# else
563 len = hid_report_size(rd, repinfo[repind].kind, r->rid);
564# endif
565# else
566 len = hid_report_size(rd, repinfo[repind].kind, &r->rid);
567# endif
568#else
569# ifdef USBHID_NEW
570 len = hid_report_size(rd, repinfo[repind].kind, r->rid);
571# else
572 len = hid_report_size(rd, repinfo[repind].kind, &r->rid);
573# endif
574#endif
575
576 if (len < 0) {
577 SDL_SetError("Negative HID report size");
578 return (-1);
579 }
580 r->size = len;
581
582 if (r->size > 0) {
583 r->buf = SDL_malloc(sizeof(*r->buf) - sizeof(REP_BUF_DATA(r)) +
584 r->size);
585 if (r->buf == NULL) {
586 SDL_OutOfMemory();
587 return (-1);
588 }
589 } else {
590 r->buf = NULL;
591 }
592
593 r->status = SREPORT_CLEAN;
594 return (0);
595}
596
597static void
598report_free(struct report *r)
599{
600 if (r->buf != NULL) {
601 SDL_free(r->buf);
602 }
603 r->status = SREPORT_UNINIT;
604}
605
606#endif /* SDL_JOYSTICK_USBHID */