2 Simple DirectMedia Layer
3 Copyright (C) 1997-2020 Sam Lantinga <slouken@libsdl.org>
5 This software is provided 'as-is', without any express or implied
6 warranty. In no event will the authors be held liable for any damages
7 arising from the use of this software.
9 Permission is granted to anyone to use this software for any purpose,
10 including commercial applications, and to alter it and redistribute it
11 freely, subject to the following restrictions:
13 1. The origin of this software must not be misrepresented; you must not
14 claim that you wrote the original software. If you use this software
15 in a product, an acknowledgment in the product documentation would be
16 appreciated but is not required.
17 2. Altered source versions must be plainly marked as such, and must not be
18 misrepresented as being the original software.
19 3. This notice may not be removed or altered from any source distribution.
21 #include "../../SDL_internal.h"
23 #ifdef SDL_JOYSTICK_USBHID
26 * Joystick driver for the uhid(4) interface found in OpenBSD,
29 * Maintainer: <vedge at csoft.org>
32 #include <sys/param.h>
38 #ifndef __FreeBSD_kernel_version
39 #define __FreeBSD_kernel_version __FreeBSD_version
42 #if defined(HAVE_USB_H)
46 #include <bus/u4b/usb.h>
47 #include <bus/u4b/usbhid.h>
49 #include <dev/usb/usb.h>
50 #include <dev/usb/usbhid.h>
53 #if defined(HAVE_USBHID_H)
55 #elif defined(HAVE_LIBUSB_H)
57 #elif defined(HAVE_LIBUSBHID_H)
58 #include <libusbhid.h>
61 #if defined(__FREEBSD__) || defined(__FreeBSD_kernel__)
62 #include <osreldate.h>
63 #if __FreeBSD_kernel_version > 800063
64 #include <dev/usb/usb_ioctl.h>
66 #include <sys/joystick.h>
67 #elif defined(__DragonFly__)
68 #include <bus/u4b/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 64
81 #define MAX_JOY_JOYS 2
82 #define MAX_JOYS (MAX_UHID_JOYS + MAX_JOY_JOYS)
86 #define HUG_DPAD_UP 0x90
87 #define HUG_DPAD_DOWN 0x91
88 #define HUG_DPAD_RIGHT 0x92
89 #define HUG_DPAD_LEFT 0x93
91 #define HAT_CENTERED 0x00
93 #define HAT_RIGHT 0x02
96 #define HAT_RIGHTUP (HAT_RIGHT|HAT_UP)
97 #define HAT_RIGHTDOWN (HAT_RIGHT|HAT_DOWN)
98 #define HAT_LEFTUP (HAT_LEFT|HAT_UP)
99 #define HAT_LEFTDOWN (HAT_LEFT|HAT_DOWN)
101 /* calculate the value from the state of the dpad */
103 dpad_to_sdl(Sint32 *dpad)
109 return HAT_RIGHTDOWN;
112 } else if (dpad[3]) {
119 } else if (dpad[0]) {
121 } else if (dpad[1]) {
130 #if defined(__FREEBSD__) && (__FreeBSD_kernel_version > 900000) || \
131 defined(__DragonFly__)
132 void *buf; /* Buffer */
133 #elif defined(__FREEBSD__) && (__FreeBSD_kernel_version > 800063)
134 struct usb_gen_descriptor *buf; /* Buffer */
136 struct usb_ctl_report *buf; /* Buffer */
138 size_t size; /* Buffer size */
139 int rid; /* Report ID */
153 } const repinfo[] = {
154 {UHID_INPUT_REPORT, hid_input, "input"},
155 {UHID_OUTPUT_REPORT, hid_output, "output"},
156 {UHID_FEATURE_REPORT, hid_feature, "feature"}
179 struct joystick_hwdata
185 BSDJOY_UHID, /* uhid(4) */
186 BSDJOY_JOY /* joy(4) */
188 struct report_desc *repdesc;
189 struct report inreport;
190 int axis_map[JOYAXE_count]; /* map present JOYAXE_* to 0,1,.. */
193 static char *joynames[MAX_JOYS];
194 static char *joydevnames[MAX_JOYS];
196 static int report_alloc(struct report *, struct report_desc *, int);
197 static void report_free(struct report *);
199 #if defined(USBHID_UCR_DATA) || (defined(__FreeBSD_kernel__) && __FreeBSD_kernel_version <= 800063)
200 #define REP_BUF_DATA(rep) ((rep)->buf->ucr_data)
201 #elif (defined(__FREEBSD__) && (__FreeBSD_kernel_version > 900000)) || \
202 defined(__DragonFly__)
203 #define REP_BUF_DATA(rep) ((rep)->buf)
204 #elif (defined(__FREEBSD__) && (__FreeBSD_kernel_version > 800063))
205 #define REP_BUF_DATA(rep) ((rep)->buf->ugd_data)
207 #define REP_BUF_DATA(rep) ((rep)->buf->data)
210 static int numjoysticks = 0;
212 static int BSD_JoystickOpen(SDL_Joystick *joy, int device_index);
213 static void BSD_JoystickClose(SDL_Joystick *joy);
216 BSD_JoystickInit(void)
223 SDL_memset(joynames, 0, sizeof(joynames));
224 SDL_memset(joydevnames, 0, sizeof(joydevnames));
226 for (i = 0; i < MAX_UHID_JOYS; i++) {
229 SDL_snprintf(s, SDL_arraysize(s), "/dev/uhid%d", i);
231 joynames[numjoysticks] = SDL_strdup(s);
233 if (BSD_JoystickOpen(&nj, numjoysticks) == 0) {
234 BSD_JoystickClose(&nj);
237 SDL_free(joynames[numjoysticks]);
238 joynames[numjoysticks] = NULL;
241 for (i = 0; i < MAX_JOY_JOYS; i++) {
242 SDL_snprintf(s, SDL_arraysize(s), "/dev/joy%d", i);
243 fd = open(s, O_RDONLY);
245 joynames[numjoysticks++] = SDL_strdup(s);
250 /* Read the default USB HID usage table. */
253 return (numjoysticks);
257 BSD_JoystickGetCount(void)
263 BSD_JoystickDetect(void)
268 BSD_JoystickGetDeviceName(int device_index)
270 if (joydevnames[device_index] != NULL) {
271 return (joydevnames[device_index]);
273 return (joynames[device_index]);
277 BSD_JoystickGetDevicePlayerIndex(int device_index)
283 BSD_JoystickSetDevicePlayerIndex(int device_index, int player_index)
287 /* Function to perform the mapping from device index to the instance id for this index */
288 static SDL_JoystickID
289 BSD_JoystickGetDeviceInstanceID(int device_index)
295 usage_to_joyaxe(unsigned usage)
309 joyaxe = JOYAXE_SLIDER;
312 joyaxe = JOYAXE_WHEEL;
330 hatval_to_sdl(Sint32 hatval)
332 static const unsigned hat_dir_map[8] = {
333 SDL_HAT_UP, SDL_HAT_RIGHTUP, SDL_HAT_RIGHT, SDL_HAT_RIGHTDOWN,
334 SDL_HAT_DOWN, SDL_HAT_LEFTDOWN, SDL_HAT_LEFT, SDL_HAT_LEFTUP
337 if ((hatval & 7) == hatval)
338 result = hat_dir_map[hatval];
340 result = SDL_HAT_CENTERED;
346 BSD_JoystickOpen(SDL_Joystick *joy, int device_index)
348 char *path = joynames[device_index];
349 struct joystick_hwdata *hw;
350 struct hid_item hitem;
351 struct hid_data *hdata;
352 struct report *rep = NULL;
353 #if defined(__NetBSD__)
354 usb_device_descriptor_t udd;
355 struct usb_string_desc usd;
360 fd = open(path, O_RDONLY);
362 return SDL_SetError("%s: %s", path, strerror(errno));
365 joy->instance_id = device_index;
366 hw = (struct joystick_hwdata *)
367 SDL_malloc(sizeof(struct joystick_hwdata));
370 return SDL_OutOfMemory();
374 hw->path = SDL_strdup(path);
375 if (!SDL_strncmp(path, "/dev/joy", 8)) {
376 hw->type = BSDJOY_JOY;
381 joydevnames[device_index] = SDL_strdup("Gameport joystick");
384 hw->type = BSDJOY_UHID;
389 for (ax = 0; ax < JOYAXE_count; ax++)
390 hw->axis_map[ax] = -1;
392 hw->repdesc = hid_get_report_desc(fd);
393 if (hw->repdesc == NULL) {
394 SDL_SetError("%s: USB_GET_REPORT_DESC: %s", hw->path,
399 #if defined(__FREEBSD__) && (__FreeBSD_kernel_version > 800063) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
400 rep->rid = hid_get_report_id(fd);
403 if (ioctl(fd, USB_GET_REPORT_ID, &rep->rid) < 0) {
405 rep->rid = -1; /* XXX */
407 #if defined(__NetBSD__)
408 if (ioctl(fd, USB_GET_DEVICE_DESC, &udd) == -1)
411 /* Get default language */
412 usd.usd_string_index = USB_LANGUAGE_TABLE;
413 usd.usd_language_id = 0;
414 if (ioctl(fd, USB_GET_STRING_DESC, &usd) == -1 || usd.usd_desc.bLength < 4) {
415 usd.usd_language_id = 0;
417 usd.usd_language_id = UGETW(usd.usd_desc.bString[0]);
420 usd.usd_string_index = udd.iProduct;
421 if (ioctl(fd, USB_GET_STRING_DESC, &usd) == 0) {
423 char *new_name = NULL;
425 for (i = 0; i < (usd.usd_desc.bLength >> 1) - 1 && i < sizeof(str) - 1; i++) {
426 str[i] = UGETW(usd.usd_desc.bString[i]);
429 asprintf(&new_name, "%s @ %s", str, path);
430 if (new_name != NULL) {
431 SDL_free(joydevnames[numjoysticks]);
432 joydevnames[numjoysticks] = new_name;
437 if (report_alloc(rep, hw->repdesc, REPORT_INPUT) < 0) {
440 if (rep->size <= 0) {
441 SDL_SetError("%s: Input report descriptor has invalid length",
445 #if defined(USBHID_NEW) || (defined(__FREEBSD__) && __FreeBSD_kernel_version >= 500111) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
446 hdata = hid_start_parse(hw->repdesc, 1 << hid_input, rep->rid);
448 hdata = hid_start_parse(hw->repdesc, 1 << hid_input);
451 SDL_SetError("%s: Cannot start HID parser", hw->path);
458 for (i = 0; i < JOYAXE_count; i++)
459 hw->axis_map[i] = -1;
461 while (hid_get_item(hdata, &hitem) > 0) {
465 switch (hitem.kind) {
467 switch (HID_PAGE(hitem.usage)) {
468 case HUP_GENERIC_DESKTOP:
469 switch (HID_USAGE(hitem.usage)) {
472 s = hid_usage_in_page(hitem.usage);
473 sp = SDL_malloc(SDL_strlen(s) + 5);
474 SDL_snprintf(sp, SDL_strlen(s) + 5, "%s (%d)",
476 joydevnames[device_index] = sp;
481 switch (HID_PAGE(hitem.usage)) {
482 case HUP_GENERIC_DESKTOP:
484 unsigned usage = HID_USAGE(hitem.usage);
485 int joyaxe = usage_to_joyaxe(usage);
487 hw->axis_map[joyaxe] = 1;
488 } else if (usage == HUG_HAT_SWITCH
490 || usage == HUG_DPAD_UP
508 hid_end_parse(hdata);
509 for (i = 0; i < JOYAXE_count; i++)
510 if (hw->axis_map[i] > 0)
511 hw->axis_map[i] = joy->naxes++;
513 if (joy->naxes == 0 && joy->nbuttons == 0 && joy->nhats == 0 && joy->nballs == 0) {
514 SDL_SetError("%s: Not a joystick, ignoring", hw->path);
519 /* The poll blocks the event thread. */
520 fcntl(fd, F_SETFL, O_NONBLOCK);
522 /* Flush pending events */
524 while (read(joy->hwdata->fd, REP_BUF_DATA(rep), rep->size) == rep->size)
538 BSD_JoystickUpdate(SDL_Joystick *joy)
540 struct hid_item hitem;
541 struct hid_data *hdata;
543 int nbutton, naxe = -1;
546 Sint32 dpad[4] = {0, 0, 0, 0};
549 #if defined(__FREEBSD__) || SDL_JOYSTICK_USBHID_MACHINE_JOYSTICK_H || defined(__FreeBSD_kernel__) || defined(__DragonFly_)
550 struct joystick gameport;
551 static int x, y, xmin = 0xffff, ymin = 0xffff, xmax = 0, ymax = 0;
553 if (joy->hwdata->type == BSDJOY_JOY) {
554 while (read(joy->hwdata->fd, &gameport, sizeof gameport) == sizeof gameport) {
555 if (abs(x - gameport.x) > 8) {
568 v -= (xmax + xmin + 1) / 2;
569 v *= 32768 / ((xmax - xmin + 1) / 2);
570 SDL_PrivateJoystickAxis(joy, 0, v);
572 if (abs(y - gameport.y) > 8) {
585 v -= (ymax + ymin + 1) / 2;
586 v *= 32768 / ((ymax - ymin + 1) / 2);
587 SDL_PrivateJoystickAxis(joy, 1, v);
589 SDL_PrivateJoystickButton(joy, 0, gameport.b1);
590 SDL_PrivateJoystickButton(joy, 1, gameport.b2);
594 #endif /* defined(__FREEBSD__) || SDL_JOYSTICK_USBHID_MACHINE_JOYSTICK_H */
596 rep = &joy->hwdata->inreport;
598 while (read(joy->hwdata->fd, REP_BUF_DATA(rep), rep->size) == rep->size) {
599 #if defined(USBHID_NEW) || (defined(__FREEBSD__) && __FreeBSD_kernel_version >= 500111) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
600 hdata = hid_start_parse(joy->hwdata->repdesc, 1 << hid_input, rep->rid);
602 hdata = hid_start_parse(joy->hwdata->repdesc, 1 << hid_input);
605 /*fprintf(stderr, "%s: Cannot start HID parser\n", joy->hwdata->path);*/
609 for (nbutton = 0; hid_get_item(hdata, &hitem) > 0;) {
610 switch (hitem.kind) {
612 switch (HID_PAGE(hitem.usage)) {
613 case HUP_GENERIC_DESKTOP:
615 unsigned usage = HID_USAGE(hitem.usage);
616 int joyaxe = usage_to_joyaxe(usage);
618 naxe = joy->hwdata->axis_map[joyaxe];
620 v = (Sint32) hid_get_data(REP_BUF_DATA(rep), &hitem);
621 v -= (hitem.logical_maximum +
622 hitem.logical_minimum + 1) / 2;
624 ((hitem.logical_maximum -
625 hitem.logical_minimum + 1) / 2);
626 SDL_PrivateJoystickAxis(joy, naxe, v);
627 } else if (usage == HUG_HAT_SWITCH) {
628 v = (Sint32) hid_get_data(REP_BUF_DATA(rep), &hitem);
629 SDL_PrivateJoystickHat(joy, 0,
631 hitem.logical_minimum);
634 else if (usage == HUG_DPAD_UP) {
635 dpad[0] = (Sint32) hid_get_data(REP_BUF_DATA(rep), &hitem);
636 SDL_PrivateJoystickHat(joy, 0, dpad_to_sdl(dpad));
638 else if (usage == HUG_DPAD_DOWN) {
639 dpad[1] = (Sint32) hid_get_data(REP_BUF_DATA(rep), &hitem);
640 SDL_PrivateJoystickHat(joy, 0, dpad_to_sdl(dpad));
642 else if (usage == HUG_DPAD_RIGHT) {
643 dpad[2] = (Sint32) hid_get_data(REP_BUF_DATA(rep), &hitem);
644 SDL_PrivateJoystickHat(joy, 0, dpad_to_sdl(dpad));
646 else if (usage == HUG_DPAD_LEFT) {
647 dpad[3] = (Sint32) hid_get_data(REP_BUF_DATA(rep), &hitem);
648 SDL_PrivateJoystickHat(joy, 0, dpad_to_sdl(dpad));
654 v = (Sint32) hid_get_data(REP_BUF_DATA(rep), &hitem);
655 SDL_PrivateJoystickButton(joy, nbutton, v);
666 hid_end_parse(hdata);
670 /* Function to close a joystick after use */
672 BSD_JoystickClose(SDL_Joystick *joy)
674 if (SDL_strncmp(joy->hwdata->path, "/dev/joy", 8)) {
675 report_free(&joy->hwdata->inreport);
676 hid_dispose_report_desc(joy->hwdata->repdesc);
678 close(joy->hwdata->fd);
679 SDL_free(joy->hwdata->path);
680 SDL_free(joy->hwdata);
684 BSD_JoystickQuit(void)
688 for (i = 0; i < MAX_JOYS; i++) {
689 SDL_free(joynames[i]);
690 SDL_free(joydevnames[i]);
696 static SDL_JoystickGUID
697 BSD_JoystickGetDeviceGUID( int device_index )
699 SDL_JoystickGUID guid;
700 /* the GUID is just the first 16 chars of the name for now */
701 const char *name = BSD_JoystickGetDeviceName( device_index );
703 SDL_memcpy( &guid, name, SDL_min( sizeof(guid), SDL_strlen( name ) ) );
708 report_alloc(struct report *r, struct report_desc *rd, int repind)
713 len = hid_report_size(rd, repinfo[repind].kind, r->rid);
715 # if (__FreeBSD_kernel_version >= 460000) || defined(__FreeBSD_kernel__)
716 # if (__FreeBSD_kernel_version <= 500111)
717 len = hid_report_size(rd, r->rid, repinfo[repind].kind);
719 len = hid_report_size(rd, repinfo[repind].kind, r->rid);
722 len = hid_report_size(rd, repinfo[repind].kind, &r->rid);
726 len = hid_report_size(rd, repinfo[repind].kind, r->rid);
728 len = hid_report_size(rd, repinfo[repind].kind, &r->rid);
733 return SDL_SetError("Negative HID report size");
738 #if defined(__FREEBSD__) && (__FreeBSD_kernel_version > 900000) || defined(__DragonFly__)
739 r->buf = SDL_malloc(r->size);
741 r->buf = SDL_malloc(sizeof(*r->buf) - sizeof(REP_BUF_DATA(r)) +
744 if (r->buf == NULL) {
745 return SDL_OutOfMemory();
751 r->status = SREPORT_CLEAN;
756 report_free(struct report *r)
759 r->status = SREPORT_UNINIT;
763 BSD_JoystickRumble(SDL_Joystick *joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble)
765 return SDL_Unsupported();
769 BSD_JoystickRumbleTriggers(SDL_Joystick *joystick, Uint16 left_rumble, Uint16 right_rumble)
771 return SDL_Unsupported();
775 BSD_JoystickGetGamepadMapping(int device_index, SDL_GamepadMapping *out)
781 BSD_JoystickHasLED(SDL_Joystick *joystick)
787 BSD_JoystickSetLED(SDL_Joystick *joystick, Uint8 red, Uint8 green, Uint8 blue)
789 return SDL_Unsupported();
793 BSD_JoystickSetSensorsEnabled(SDL_Joystick *joystick, SDL_bool enabled)
795 return SDL_Unsupported();
798 SDL_JoystickDriver SDL_BSD_JoystickDriver =
801 BSD_JoystickGetCount,
803 BSD_JoystickGetDeviceName,
804 BSD_JoystickGetDevicePlayerIndex,
805 BSD_JoystickSetDevicePlayerIndex,
806 BSD_JoystickGetDeviceGUID,
807 BSD_JoystickGetDeviceInstanceID,
810 BSD_JoystickRumbleTriggers,
813 BSD_JoystickSetSensorsEnabled,
817 BSD_JoystickGetGamepadMapping
820 #endif /* SDL_JOYSTICK_USBHID */
822 /* vi: set ts=4 sw=4 expandtab: */