2 SDL - Simple DirectMedia Layer
3 Copyright (C) 1997-2012 Sam Lantinga
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.
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.
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
22 #include "SDL_config.h"
24 #ifdef SDL_JOYSTICK_USBHID
27 * Joystick driver for the uhid(4) interface found in OpenBSD,
30 * Maintainer: <vedge at csoft.org>
33 #include <sys/param.h>
39 #ifndef __FreeBSD_kernel_version
40 #define __FreeBSD_kernel_version __FreeBSD_version
43 #if defined(HAVE_USB_H)
47 #include <bus/usb/usb.h>
48 #include <bus/usb/usbhid.h>
50 #include <dev/usb/usb.h>
51 #include <dev/usb/usbhid.h>
54 #if defined(HAVE_USBHID_H)
56 #elif defined(HAVE_LIBUSB_H)
58 #elif defined(HAVE_LIBUSBHID_H)
59 #include <libusbhid.h>
62 #if defined(__FREEBSD__) || defined(__FreeBSD_kernel__)
64 #include <osreldate.h>
66 #if __FreeBSD_kernel_version > 800063
67 #include <dev/usb/usb_ioctl.h>
69 #include <sys/joystick.h>
72 #if SDL_JOYSTICK_USBHID_MACHINE_JOYSTICK_H
73 #include <machine/joystick.h>
76 #include "SDL_joystick.h"
77 #include "../SDL_sysjoystick.h"
78 #include "../SDL_joystick_c.h"
80 #define MAX_UHID_JOYS 4
81 #define MAX_JOY_JOYS 2
82 #define MAX_JOYS (MAX_UHID_JOYS + MAX_JOY_JOYS)
85 #if defined(__FREEBSD__) && (__FreeBSD_kernel_version > 800063)
86 struct usb_gen_descriptor *buf; /* Buffer */
88 struct usb_ctl_report *buf; /* Buffer */
90 size_t size; /* Buffer size */
91 int rid; /* Report ID */
103 } const repinfo[] = {
104 { UHID_INPUT_REPORT, hid_input, "input" },
105 { UHID_OUTPUT_REPORT, hid_output, "output" },
106 { UHID_FEATURE_REPORT, hid_feature, "feature" }
127 struct joystick_hwdata {
131 BSDJOY_UHID, /* uhid(4) */
132 BSDJOY_JOY /* joy(4) */
134 struct report_desc *repdesc;
135 struct report inreport;
136 int axis_map[JOYAXE_count]; /* map present JOYAXE_* to 0,1,..*/
145 static char *joynames[MAX_JOYS];
146 static char *joydevnames[MAX_JOYS];
148 static int report_alloc(struct report *, struct report_desc *, int);
149 static void report_free(struct report *);
151 #if defined(USBHID_UCR_DATA) || defined(__FreeBSD_kernel__)
152 #define REP_BUF_DATA(rep) ((rep)->buf->ucr_data)
153 #elif (defined(__FREEBSD__) && (__FreeBSD_kernel_version > 800063))
154 #define REP_BUF_DATA(rep) ((rep)->buf->ugd_data)
156 #define REP_BUF_DATA(rep) ((rep)->buf->data)
160 SDL_SYS_JoystickInit(void)
165 SDL_numjoysticks = 0;
167 SDL_memset(joynames, 0, sizeof(joynames));
168 SDL_memset(joydevnames, 0, sizeof(joydevnames));
170 for (i = 0; i < MAX_UHID_JOYS; i++) {
173 SDL_snprintf(s, SDL_arraysize(s), "/dev/uhid%d", i);
175 nj.index = SDL_numjoysticks;
176 joynames[nj.index] = strdup(s);
178 if (SDL_SYS_JoystickOpen(&nj) == 0) {
179 SDL_SYS_JoystickClose(&nj);
182 SDL_free(joynames[nj.index]);
183 joynames[nj.index] = NULL;
186 for (i = 0; i < MAX_JOY_JOYS; i++) {
187 SDL_snprintf(s, SDL_arraysize(s), "/dev/joy%d", i);
188 fd = open(s, O_RDONLY);
190 joynames[SDL_numjoysticks++] = strdup(s);
195 /* Read the default USB HID usage table. */
198 return (SDL_numjoysticks);
202 SDL_SYS_JoystickName(int index)
204 if (joydevnames[index] != NULL) {
205 return (joydevnames[index]);
207 return (joynames[index]);
211 usage_to_joyaxe(unsigned usage)
216 joyaxe = JOYAXE_X; break;
218 joyaxe = JOYAXE_Y; break;
220 joyaxe = JOYAXE_Z; break;
222 joyaxe = JOYAXE_SLIDER; break;
224 joyaxe = JOYAXE_WHEEL; break;
226 joyaxe = JOYAXE_RX; break;
228 joyaxe = JOYAXE_RY; break;
230 joyaxe = JOYAXE_RZ; break;
238 hatval_to_sdl(Sint32 hatval)
240 static const unsigned hat_dir_map[8] = {
241 SDL_HAT_UP, SDL_HAT_RIGHTUP, SDL_HAT_RIGHT, SDL_HAT_RIGHTDOWN,
242 SDL_HAT_DOWN, SDL_HAT_LEFTDOWN, SDL_HAT_LEFT, SDL_HAT_LEFTUP
245 if ((hatval & 7) == hatval)
246 result = hat_dir_map[hatval];
248 result = SDL_HAT_CENTERED;
254 SDL_SYS_JoystickOpen(SDL_Joystick *joy)
256 char *path = joynames[joy->index];
257 struct joystick_hwdata *hw;
258 struct hid_item hitem;
259 struct hid_data *hdata;
264 fd = open(path, O_RDONLY);
266 SDL_SetError("%s: %s", path, strerror(errno));
270 hw = (struct joystick_hwdata *)SDL_malloc(sizeof(struct joystick_hwdata));
278 hw->path = strdup(path);
285 if (! SDL_strncmp(path, "/dev/joy", 8)) {
286 hw->type = BSDJOY_JOY;
291 joydevnames[joy->index] = strdup("Gameport joystick");
294 hw->type = BSDJOY_UHID;
299 for (ax = 0; ax < JOYAXE_count; ax++)
300 hw->axis_map[ax] = -1;
302 hw->repdesc = hid_get_report_desc(fd);
303 if (hw->repdesc == NULL) {
304 SDL_SetError("%s: USB_GET_REPORT_DESC: %s", hw->path,
309 #if defined(__FREEBSD__) && (__FreeBSD_kernel_version > 800063) || defined(__FreeBSD_kernel__)
310 rep->rid = hid_get_report_id(fd);
313 if (ioctl(fd, USB_GET_REPORT_ID, &rep->rid) < 0) {
315 rep->rid = -1; /* XXX */
317 if (report_alloc(rep, hw->repdesc, REPORT_INPUT) < 0) {
320 if (rep->size <= 0) {
321 SDL_SetError("%s: Input report descriptor has invalid length",
326 #if defined(USBHID_NEW) || (defined(__FREEBSD__) && __FreeBSD_kernel_version >= 500111) || defined(__FreeBSD_kernel__)
327 hdata = hid_start_parse(hw->repdesc, 1 << hid_input, rep->rid);
329 hdata = hid_start_parse(hw->repdesc, 1 << hid_input);
332 SDL_SetError("%s: Cannot start HID parser", hw->path);
339 for (i=0; i<JOYAXE_count; i++)
340 hw->axis_map[i] = -1;
342 while (hid_get_item(hdata, &hitem) > 0) {
346 switch (hitem.kind) {
348 switch (HID_PAGE(hitem.usage)) {
349 case HUP_GENERIC_DESKTOP:
350 switch (HID_USAGE(hitem.usage)) {
353 s = hid_usage_in_page(hitem.usage);
354 sp = SDL_malloc(SDL_strlen(s) + 5);
355 SDL_snprintf(sp, SDL_strlen(s) + 5, "%s (%d)", s,
357 joydevnames[joy->index] = sp;
362 switch (HID_PAGE(hitem.usage)) {
363 case HUP_GENERIC_DESKTOP: {
364 unsigned usage = HID_USAGE(hitem.usage);
365 int joyaxe = usage_to_joyaxe(usage);
367 hw->axis_map[joyaxe] = 1;
368 } else if (usage == HUG_HAT_SWITCH) {
384 hid_end_parse(hdata);
385 for (i=0; i<JOYAXE_count; i++)
386 if (hw->axis_map[i] > 0)
387 hw->axis_map[i] = joy->naxes++;
390 /* The poll blocks the event thread. */
391 fcntl(fd, F_SETFL, O_NONBLOCK);
402 SDL_SYS_JoystickUpdate(SDL_Joystick *joy)
404 struct hid_item hitem;
405 struct hid_data *hdata;
407 int nbutton, naxe = -1;
410 #if defined(__FREEBSD__) || SDL_JOYSTICK_USBHID_MACHINE_JOYSTICK_H || defined(__FreeBSD_kernel__)
411 struct joystick gameport;
413 if (joy->hwdata->type == BSDJOY_JOY) {
414 if (read(joy->hwdata->fd, &gameport, sizeof gameport) != sizeof gameport)
416 if (abs(joy->hwdata->x - gameport.x) > 8) {
417 joy->hwdata->x = gameport.x;
418 if (joy->hwdata->x < joy->hwdata->xmin) {
419 joy->hwdata->xmin = joy->hwdata->x;
421 if (joy->hwdata->x > joy->hwdata->xmax) {
422 joy->hwdata->xmax = joy->hwdata->x;
424 if (joy->hwdata->xmin == joy->hwdata->xmax) {
428 v = (Sint32)joy->hwdata->x;
429 v -= (joy->hwdata->xmax + joy->hwdata->xmin + 1)/2;
430 v *= 32768/((joy->hwdata->xmax - joy->hwdata->xmin + 1)/2);
431 SDL_PrivateJoystickAxis(joy, 0, v);
433 if (abs(joy->hwdata->y - gameport.y) > 8) {
434 joy->hwdata->y = gameport.y;
435 if (joy->hwdata->y < joy->hwdata->ymin) {
436 joy->hwdata->ymin = joy->hwdata->y;
438 if (joy->hwdata->y > joy->hwdata->ymax) {
439 joy->hwdata->ymax = joy->hwdata->y;
441 if (joy->hwdata->ymin == joy->hwdata->ymax) {
445 v = (Sint32)joy->hwdata->y;
446 v -= (joy->hwdata->ymax + joy->hwdata->ymin + 1)/2;
447 v *= 32768/((joy->hwdata->ymax - joy->hwdata->ymin + 1)/2);
448 SDL_PrivateJoystickAxis(joy, 1, v);
450 if (gameport.b1 != joy->buttons[0]) {
451 SDL_PrivateJoystickButton(joy, 0, gameport.b1);
453 if (gameport.b2 != joy->buttons[1]) {
454 SDL_PrivateJoystickButton(joy, 1, gameport.b2);
458 #endif /* defined(__FREEBSD__) || SDL_JOYSTICK_USBHID_MACHINE_JOYSTICK_H */
460 rep = &joy->hwdata->inreport;
462 if (read(joy->hwdata->fd, REP_BUF_DATA(rep), rep->size) != rep->size) {
465 #if defined(USBHID_NEW) || (defined(__FREEBSD__) && __FreeBSD_kernel_version >= 500111) || defined(__FreeBSD_kernel__)
466 hdata = hid_start_parse(joy->hwdata->repdesc, 1 << hid_input, rep->rid);
468 hdata = hid_start_parse(joy->hwdata->repdesc, 1 << hid_input);
471 fprintf(stderr, "%s: Cannot start HID parser\n",
476 for (nbutton = 0; hid_get_item(hdata, &hitem) > 0;) {
477 switch (hitem.kind) {
479 switch (HID_PAGE(hitem.usage)) {
480 case HUP_GENERIC_DESKTOP: {
481 unsigned usage = HID_USAGE(hitem.usage);
482 int joyaxe = usage_to_joyaxe(usage);
484 naxe = joy->hwdata->axis_map[joyaxe];
486 v = (Sint32)hid_get_data(REP_BUF_DATA(rep),
488 v -= (hitem.logical_maximum + hitem.logical_minimum + 1)/2;
489 v *= 32768/((hitem.logical_maximum - hitem.logical_minimum + 1)/2);
490 if (v != joy->axes[naxe]) {
491 SDL_PrivateJoystickAxis(joy, naxe, v);
493 } else if (usage == HUG_HAT_SWITCH) {
494 v = (Sint32)hid_get_data(REP_BUF_DATA(rep),
496 SDL_PrivateJoystickHat(joy, 0,
497 hatval_to_sdl(v)-hitem.logical_minimum);
502 v = (Sint32)hid_get_data(REP_BUF_DATA(rep),
504 if (joy->buttons[nbutton] != v) {
505 SDL_PrivateJoystickButton(joy,
518 hid_end_parse(hdata);
523 /* Function to close a joystick after use */
525 SDL_SYS_JoystickClose(SDL_Joystick *joy)
527 if (SDL_strncmp(joy->hwdata->path, "/dev/joy", 8)) {
528 report_free(&joy->hwdata->inreport);
529 hid_dispose_report_desc(joy->hwdata->repdesc);
531 close(joy->hwdata->fd);
532 SDL_free(joy->hwdata->path);
533 SDL_free(joy->hwdata);
539 SDL_SYS_JoystickQuit(void)
543 for (i = 0; i < MAX_JOYS; i++) {
544 if (joynames[i] != NULL)
545 SDL_free(joynames[i]);
546 if (joydevnames[i] != NULL)
547 SDL_free(joydevnames[i]);
554 report_alloc(struct report *r, struct report_desc *rd, int repind)
559 len = hid_report_size(rd, r->rid, repinfo[repind].kind);
561 # if (__FreeBSD_kernel_version >= 460000) || defined(__FreeBSD_kernel__)
562 # if (__FreeBSD_kernel_version <= 500111)
563 len = hid_report_size(rd, r->rid, repinfo[repind].kind);
565 len = hid_report_size(rd, repinfo[repind].kind, r->rid);
568 len = hid_report_size(rd, repinfo[repind].kind, &r->rid);
572 len = hid_report_size(rd, repinfo[repind].kind, r->rid);
574 len = hid_report_size(rd, repinfo[repind].kind, &r->rid);
579 SDL_SetError("Negative HID report size");
585 r->buf = SDL_malloc(sizeof(*r->buf) - sizeof(REP_BUF_DATA(r)) +
587 if (r->buf == NULL) {
595 r->status = SREPORT_CLEAN;
600 report_free(struct report *r)
602 if (r->buf != NULL) {
605 r->status = SREPORT_UNINIT;
608 #endif /* SDL_JOYSTICK_USBHID */