2 SDL - Simple DirectMedia Layer
3 Copyright (C) 1997-2009 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>
64 #include <osreldate.h>
66 #include <sys/joystick.h>
69 #if SDL_JOYSTICK_USBHID_MACHINE_JOYSTICK_H
70 #include <machine/joystick.h>
73 #include "SDL_joystick.h"
74 #include "../SDL_sysjoystick.h"
75 #include "../SDL_joystick_c.h"
77 #define MAX_UHID_JOYS 4
78 #define MAX_JOY_JOYS 2
79 #define MAX_JOYS (MAX_UHID_JOYS + MAX_JOY_JOYS)
81 #if defined(__FREEBSD__) && (__FreeBSD_kernel_version > 800063)
82 struct usb_ctl_report {
84 u_char ucr_data[1024]; /* filled data size will vary */
89 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__) && (__FreeBSD_kernel_version > 800063))
152 #define REP_BUF_DATA(rep) ((rep)->buf->ucr_data)
154 #define REP_BUF_DATA(rep) ((rep)->buf->data)
158 SDL_SYS_JoystickInit(void)
163 SDL_numjoysticks = 0;
165 SDL_memset(joynames, 0, sizeof(joynames));
166 SDL_memset(joydevnames, 0, sizeof(joydevnames));
168 for (i = 0; i < MAX_UHID_JOYS; i++) {
171 SDL_snprintf(s, SDL_arraysize(s), "/dev/uhid%d", i);
173 nj.index = SDL_numjoysticks;
174 joynames[nj.index] = strdup(s);
176 if (SDL_SYS_JoystickOpen(&nj) == 0) {
177 SDL_SYS_JoystickClose(&nj);
180 SDL_free(joynames[nj.index]);
181 joynames[nj.index] = NULL;
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);
188 joynames[SDL_numjoysticks++] = strdup(s);
193 /* Read the default USB HID usage table. */
196 return (SDL_numjoysticks);
200 SDL_SYS_JoystickName(int index)
202 if (joydevnames[index] != NULL) {
203 return (joydevnames[index]);
205 return (joynames[index]);
209 usage_to_joyaxe(unsigned usage)
214 joyaxe = JOYAXE_X; break;
216 joyaxe = JOYAXE_Y; break;
218 joyaxe = JOYAXE_Z; break;
220 joyaxe = JOYAXE_SLIDER; break;
222 joyaxe = JOYAXE_WHEEL; break;
224 joyaxe = JOYAXE_RX; break;
226 joyaxe = JOYAXE_RY; break;
228 joyaxe = JOYAXE_RZ; break;
236 hatval_to_sdl(Sint32 hatval)
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
243 if ((hatval & 7) == hatval)
244 result = hat_dir_map[hatval];
246 result = SDL_HAT_CENTERED;
252 SDL_SYS_JoystickOpen(SDL_Joystick *joy)
254 char *path = joynames[joy->index];
255 struct joystick_hwdata *hw;
256 struct hid_item hitem;
257 struct hid_data *hdata;
262 fd = open(path, O_RDONLY);
264 SDL_SetError("%s: %s", path, strerror(errno));
268 hw = (struct joystick_hwdata *)SDL_malloc(sizeof(struct joystick_hwdata));
276 hw->path = strdup(path);
283 if (! SDL_strncmp(path, "/dev/joy", 8)) {
284 hw->type = BSDJOY_JOY;
289 joydevnames[joy->index] = strdup("Gameport joystick");
292 hw->type = BSDJOY_UHID;
297 for (ax = 0; ax < JOYAXE_count; ax++)
298 hw->axis_map[ax] = -1;
300 hw->repdesc = hid_get_report_desc(fd);
301 if (hw->repdesc == NULL) {
302 SDL_SetError("%s: USB_GET_REPORT_DESC: %s", hw->path,
306 #if defined(__FREEBSD__) && (__FreeBSD_kernel_version > 800063)
307 rep->rid = hid_get_report_id(fd);
311 if (ioctl(fd, USB_GET_REPORT_ID, &rep->rid) < 0) {
313 rep->rid = -1; /* XXX */
315 if (report_alloc(rep, hw->repdesc, REPORT_INPUT) < 0) {
318 if (rep->size <= 0) {
319 SDL_SetError("%s: Input report descriptor has invalid length",
324 #if defined(USBHID_NEW) || (defined(__FREEBSD__) && __FreeBSD_kernel_version >= 500111)
325 hdata = hid_start_parse(hw->repdesc, 1 << hid_input, rep->rid);
327 hdata = hid_start_parse(hw->repdesc, 1 << hid_input);
330 SDL_SetError("%s: Cannot start HID parser", hw->path);
337 for (i=0; i<JOYAXE_count; i++)
338 hw->axis_map[i] = -1;
340 while (hid_get_item(hdata, &hitem) > 0) {
344 switch (hitem.kind) {
346 switch (HID_PAGE(hitem.usage)) {
347 case HUP_GENERIC_DESKTOP:
348 switch (HID_USAGE(hitem.usage)) {
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,
355 joydevnames[joy->index] = sp;
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);
365 hw->axis_map[joyaxe] = 1;
366 } else if (usage == HUG_HAT_SWITCH) {
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++;
388 /* The poll blocks the event thread. */
389 fcntl(fd, F_SETFL, O_NONBLOCK);
400 SDL_SYS_JoystickUpdate(SDL_Joystick *joy)
402 struct hid_item hitem;
403 struct hid_data *hdata;
405 int nbutton, naxe = -1;
408 #if defined(__FREEBSD__) || SDL_JOYSTICK_USBHID_MACHINE_JOYSTICK_H
409 struct joystick gameport;
411 if (joy->hwdata->type == BSDJOY_JOY) {
412 if (read(joy->hwdata->fd, &gameport, sizeof gameport) != sizeof gameport)
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;
419 if (joy->hwdata->x > joy->hwdata->xmax) {
420 joy->hwdata->xmax = joy->hwdata->x;
422 if (joy->hwdata->xmin == joy->hwdata->xmax) {
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);
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;
436 if (joy->hwdata->y > joy->hwdata->ymax) {
437 joy->hwdata->ymax = joy->hwdata->y;
439 if (joy->hwdata->ymin == joy->hwdata->ymax) {
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);
448 if (gameport.b1 != joy->buttons[0]) {
449 SDL_PrivateJoystickButton(joy, 0, gameport.b1);
451 if (gameport.b2 != joy->buttons[1]) {
452 SDL_PrivateJoystickButton(joy, 1, gameport.b2);
456 #endif /* defined(__FREEBSD__) || SDL_JOYSTICK_USBHID_MACHINE_JOYSTICK_H */
458 rep = &joy->hwdata->inreport;
460 if (read(joy->hwdata->fd, REP_BUF_DATA(rep), rep->size) != rep->size) {
463 #if defined(USBHID_NEW) || (defined(__FREEBSD__) && __FreeBSD_kernel_version >= 500111)
464 hdata = hid_start_parse(joy->hwdata->repdesc, 1 << hid_input, rep->rid);
466 hdata = hid_start_parse(joy->hwdata->repdesc, 1 << hid_input);
469 fprintf(stderr, "%s: Cannot start HID parser\n",
474 for (nbutton = 0; hid_get_item(hdata, &hitem) > 0;) {
475 switch (hitem.kind) {
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);
482 naxe = joy->hwdata->axis_map[joyaxe];
484 v = (Sint32)hid_get_data(REP_BUF_DATA(rep),
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);
491 } else if (usage == HUG_HAT_SWITCH) {
492 v = (Sint32)hid_get_data(REP_BUF_DATA(rep),
494 SDL_PrivateJoystickHat(joy, 0,
495 hatval_to_sdl(v)-hitem.logical_minimum);
500 v = (Sint32)hid_get_data(REP_BUF_DATA(rep),
502 if (joy->buttons[nbutton] != v) {
503 SDL_PrivateJoystickButton(joy,
516 hid_end_parse(hdata);
521 /* Function to close a joystick after use */
523 SDL_SYS_JoystickClose(SDL_Joystick *joy)
525 if (SDL_strncmp(joy->hwdata->path, "/dev/joy", 8)) {
526 report_free(&joy->hwdata->inreport);
527 hid_dispose_report_desc(joy->hwdata->repdesc);
529 close(joy->hwdata->fd);
530 SDL_free(joy->hwdata->path);
531 SDL_free(joy->hwdata);
537 SDL_SYS_JoystickQuit(void)
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]);
552 report_alloc(struct report *r, struct report_desc *rd, int repind)
557 len = hid_report_size(rd, r->rid, repinfo[repind].kind);
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);
563 len = hid_report_size(rd, repinfo[repind].kind, r->rid);
566 len = hid_report_size(rd, repinfo[repind].kind, &r->rid);
570 len = hid_report_size(rd, repinfo[repind].kind, r->rid);
572 len = hid_report_size(rd, repinfo[repind].kind, &r->rid);
577 SDL_SetError("Negative HID report size");
583 r->buf = SDL_malloc(sizeof(*r->buf) - sizeof(REP_BUF_DATA(r)) +
585 if (r->buf == NULL) {
593 r->status = SREPORT_CLEAN;
598 report_free(struct report *r)
600 if (r->buf != NULL) {
603 r->status = SREPORT_UNINIT;
606 #endif /* SDL_JOYSTICK_USBHID */