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