src/joystick/bsd/SDL_sysjoystick.c
author Sam Lantinga <slouken@libsdl.org>
Sat, 31 Dec 2011 09:28:07 -0500
changeset 6138 4c64952a58fb
parent 6080 eb0e11b096ab
child 6290 4cf8e87aff95
permissions -rwxr-xr-x
Happy New Year!
     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	4
    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) || defined(__FreeBSD_kernel__)
   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
   161 SDL_SYS_JoystickInit(void)
   162 {
   163     char s[16];
   164     int i, fd;
   165 
   166     SDL_numjoysticks = 0;
   167 
   168     SDL_memset(joynames, 0, sizeof(joynames));
   169     SDL_memset(joydevnames, 0, sizeof(joydevnames));
   170 
   171     for (i = 0; i < MAX_UHID_JOYS; i++) {
   172         SDL_Joystick nj;
   173 
   174         SDL_snprintf(s, SDL_arraysize(s), "/dev/uhid%d", i);
   175 
   176         nj.index = SDL_numjoysticks;
   177         joynames[nj.index] = strdup(s);
   178 
   179         if (SDL_SYS_JoystickOpen(&nj) == 0) {
   180             SDL_SYS_JoystickClose(&nj);
   181             SDL_numjoysticks++;
   182         } else {
   183             SDL_free(joynames[nj.index]);
   184             joynames[nj.index] = NULL;
   185         }
   186     }
   187     for (i = 0; i < MAX_JOY_JOYS; i++) {
   188         SDL_snprintf(s, SDL_arraysize(s), "/dev/joy%d", i);
   189         fd = open(s, O_RDONLY);
   190         if (fd != -1) {
   191             joynames[SDL_numjoysticks++] = strdup(s);
   192             close(fd);
   193         }
   194     }
   195 
   196     /* Read the default USB HID usage table. */
   197     hid_init(NULL);
   198 
   199     return (SDL_numjoysticks);
   200 }
   201 
   202 const char *
   203 SDL_SYS_JoystickName(int index)
   204 {
   205     if (joydevnames[index] != NULL) {
   206         return (joydevnames[index]);
   207     }
   208     return (joynames[index]);
   209 }
   210 
   211 static int
   212 usage_to_joyaxe(unsigned usage)
   213 {
   214     int joyaxe;
   215     switch (usage) {
   216     case HUG_X:
   217         joyaxe = JOYAXE_X;
   218         break;
   219     case HUG_Y:
   220         joyaxe = JOYAXE_Y;
   221         break;
   222     case HUG_Z:
   223         joyaxe = JOYAXE_Z;
   224         break;
   225     case HUG_SLIDER:
   226         joyaxe = JOYAXE_SLIDER;
   227         break;
   228     case HUG_WHEEL:
   229         joyaxe = JOYAXE_WHEEL;
   230         break;
   231     case HUG_RX:
   232         joyaxe = JOYAXE_RX;
   233         break;
   234     case HUG_RY:
   235         joyaxe = JOYAXE_RY;
   236         break;
   237     case HUG_RZ:
   238         joyaxe = JOYAXE_RZ;
   239         break;
   240     default:
   241         joyaxe = -1;
   242     }
   243     return joyaxe;
   244 }
   245 
   246 static unsigned
   247 hatval_to_sdl(Sint32 hatval)
   248 {
   249     static const unsigned hat_dir_map[8] = {
   250         SDL_HAT_UP, SDL_HAT_RIGHTUP, SDL_HAT_RIGHT, SDL_HAT_RIGHTDOWN,
   251         SDL_HAT_DOWN, SDL_HAT_LEFTDOWN, SDL_HAT_LEFT, SDL_HAT_LEFTUP
   252     };
   253     unsigned result;
   254     if ((hatval & 7) == hatval)
   255         result = hat_dir_map[hatval];
   256     else
   257         result = SDL_HAT_CENTERED;
   258     return result;
   259 }
   260 
   261 
   262 int
   263 SDL_SYS_JoystickOpen(SDL_Joystick * joy)
   264 {
   265     char *path = joynames[joy->index];
   266     struct joystick_hwdata *hw;
   267     struct hid_item hitem;
   268     struct hid_data *hdata;
   269     struct report *rep;
   270     int fd;
   271     int i;
   272 
   273     fd = open(path, O_RDONLY);
   274     if (fd == -1) {
   275         SDL_SetError("%s: %s", path, strerror(errno));
   276         return (-1);
   277     }
   278 
   279     hw = (struct joystick_hwdata *)
   280         SDL_malloc(sizeof(struct joystick_hwdata));
   281     if (hw == NULL) {
   282         SDL_OutOfMemory();
   283         close(fd);
   284         return (-1);
   285     }
   286     joy->hwdata = hw;
   287     hw->fd = fd;
   288     hw->path = strdup(path);
   289     if (!SDL_strncmp(path, "/dev/joy", 8)) {
   290         hw->type = BSDJOY_JOY;
   291         joy->naxes = 2;
   292         joy->nbuttons = 2;
   293         joy->nhats = 0;
   294         joy->nballs = 0;
   295         joydevnames[joy->index] = strdup("Gameport joystick");
   296         goto usbend;
   297     } else {
   298         hw->type = BSDJOY_UHID;
   299     }
   300 
   301     {
   302         int ax;
   303         for (ax = 0; ax < JOYAXE_count; ax++)
   304             hw->axis_map[ax] = -1;
   305     }
   306     hw->repdesc = hid_get_report_desc(fd);
   307     if (hw->repdesc == NULL) {
   308         SDL_SetError("%s: USB_GET_REPORT_DESC: %s", hw->path,
   309                      strerror(errno));
   310         goto usberr;
   311     }
   312     rep = &hw->inreport;
   313 #if defined(__FREEBSD__) && (__FreeBSD_kernel_version > 800063) || defined(__FreeBSD_kernel__)
   314     rep->rid = hid_get_report_id(fd);
   315     if (rep->rid < 0) {
   316 #else
   317     if (ioctl(fd, USB_GET_REPORT_ID, &rep->rid) < 0) {
   318 #endif
   319         rep->rid = -1;          /* XXX */
   320     }
   321     if (report_alloc(rep, hw->repdesc, REPORT_INPUT) < 0) {
   322         goto usberr;
   323     }
   324     if (rep->size <= 0) {
   325         SDL_SetError("%s: Input report descriptor has invalid length",
   326                      hw->path);
   327         goto usberr;
   328     }
   329 #if defined(USBHID_NEW) || (defined(__FREEBSD__) && __FreeBSD_kernel_version >= 500111) || defined(__FreeBSD_kernel__)
   330     hdata = hid_start_parse(hw->repdesc, 1 << hid_input, rep->rid);
   331 #else
   332     hdata = hid_start_parse(hw->repdesc, 1 << hid_input);
   333 #endif
   334     if (hdata == NULL) {
   335         SDL_SetError("%s: Cannot start HID parser", hw->path);
   336         goto usberr;
   337     }
   338     joy->naxes = 0;
   339     joy->nbuttons = 0;
   340     joy->nhats = 0;
   341     joy->nballs = 0;
   342     for (i = 0; i < JOYAXE_count; i++)
   343         hw->axis_map[i] = -1;
   344 
   345     while (hid_get_item(hdata, &hitem) > 0) {
   346         char *sp;
   347         const char *s;
   348 
   349         switch (hitem.kind) {
   350         case hid_collection:
   351             switch (HID_PAGE(hitem.usage)) {
   352             case HUP_GENERIC_DESKTOP:
   353                 switch (HID_USAGE(hitem.usage)) {
   354                 case HUG_JOYSTICK:
   355                 case HUG_GAME_PAD:
   356                     s = hid_usage_in_page(hitem.usage);
   357                     sp = SDL_malloc(SDL_strlen(s) + 5);
   358                     SDL_snprintf(sp, SDL_strlen(s) + 5, "%s (%d)",
   359                                  s, joy->index);
   360                     joydevnames[joy->index] = sp;
   361                 }
   362             }
   363             break;
   364         case hid_input:
   365             switch (HID_PAGE(hitem.usage)) {
   366             case HUP_GENERIC_DESKTOP:
   367                 {
   368                     unsigned usage = HID_USAGE(hitem.usage);
   369                     int joyaxe = usage_to_joyaxe(usage);
   370                     if (joyaxe >= 0) {
   371                         hw->axis_map[joyaxe] = 1;
   372                     } else if (usage == HUG_HAT_SWITCH) {
   373                         joy->nhats++;
   374                     }
   375                     break;
   376                 }
   377             case HUP_BUTTON:
   378                 joy->nbuttons++;
   379                 break;
   380             default:
   381                 break;
   382             }
   383             break;
   384         default:
   385             break;
   386         }
   387     }
   388     hid_end_parse(hdata);
   389     for (i = 0; i < JOYAXE_count; i++)
   390         if (hw->axis_map[i] > 0)
   391             hw->axis_map[i] = joy->naxes++;
   392 
   393   usbend:
   394     /* The poll blocks the event thread. */
   395     fcntl(fd, F_SETFL, O_NONBLOCK);
   396 
   397     return (0);
   398   usberr:
   399     close(hw->fd);
   400     SDL_free(hw->path);
   401     SDL_free(hw);
   402     return (-1);
   403 }
   404 
   405 void
   406 SDL_SYS_JoystickUpdate(SDL_Joystick * joy)
   407 {
   408     struct hid_item hitem;
   409     struct hid_data *hdata;
   410     struct report *rep;
   411     int nbutton, naxe = -1;
   412     Sint32 v;
   413 
   414 #if defined(__FREEBSD__) || SDL_JOYSTICK_USBHID_MACHINE_JOYSTICK_H || defined(__FreeBSD_kernel__)
   415     struct joystick gameport;
   416     static int x, y, xmin = 0xffff, ymin = 0xffff, xmax = 0, ymax = 0;
   417 
   418     if (joy->hwdata->type == BSDJOY_JOY) {
   419         if (read(joy->hwdata->fd, &gameport, sizeof gameport) !=
   420             sizeof gameport)
   421             return;
   422         if (abs(x - gameport.x) > 8) {
   423             x = gameport.x;
   424             if (x < xmin) {
   425                 xmin = x;
   426             }
   427             if (x > xmax) {
   428                 xmax = x;
   429             }
   430             if (xmin == xmax) {
   431                 xmin--;
   432                 xmax++;
   433             }
   434             v = (Sint32) x;
   435             v -= (xmax + xmin + 1) / 2;
   436             v *= 32768 / ((xmax - xmin + 1) / 2);
   437             SDL_PrivateJoystickAxis(joy, 0, v);
   438         }
   439         if (abs(y - gameport.y) > 8) {
   440             y = gameport.y;
   441             if (y < ymin) {
   442                 ymin = y;
   443             }
   444             if (y > ymax) {
   445                 ymax = y;
   446             }
   447             if (ymin == ymax) {
   448                 ymin--;
   449                 ymax++;
   450             }
   451             v = (Sint32) y;
   452             v -= (ymax + ymin + 1) / 2;
   453             v *= 32768 / ((ymax - ymin + 1) / 2);
   454             SDL_PrivateJoystickAxis(joy, 1, v);
   455         }
   456         if (gameport.b1 != joy->buttons[0]) {
   457             SDL_PrivateJoystickButton(joy, 0, gameport.b1);
   458         }
   459         if (gameport.b2 != joy->buttons[1]) {
   460             SDL_PrivateJoystickButton(joy, 1, gameport.b2);
   461         }
   462         return;
   463     }
   464 #endif /* defined(__FREEBSD__) || SDL_JOYSTICK_USBHID_MACHINE_JOYSTICK_H */
   465 
   466     rep = &joy->hwdata->inreport;
   467 
   468     if (read(joy->hwdata->fd, REP_BUF_DATA(rep), rep->size) != rep->size) {
   469         return;
   470     }
   471 #if defined(USBHID_NEW) || (defined(__FREEBSD__) && __FreeBSD_kernel_version >= 500111) || defined(__FreeBSD_kernel__)
   472     hdata = hid_start_parse(joy->hwdata->repdesc, 1 << hid_input, rep->rid);
   473 #else
   474     hdata = hid_start_parse(joy->hwdata->repdesc, 1 << hid_input);
   475 #endif
   476     if (hdata == NULL) {
   477         fprintf(stderr, "%s: Cannot start HID parser\n", joy->hwdata->path);
   478         return;
   479     }
   480 
   481     for (nbutton = 0; hid_get_item(hdata, &hitem) > 0;) {
   482         switch (hitem.kind) {
   483         case hid_input:
   484             switch (HID_PAGE(hitem.usage)) {
   485             case HUP_GENERIC_DESKTOP:
   486                 {
   487                     unsigned usage = HID_USAGE(hitem.usage);
   488                     int joyaxe = usage_to_joyaxe(usage);
   489                     if (joyaxe >= 0) {
   490                         naxe = joy->hwdata->axis_map[joyaxe];
   491                         /* scaleaxe */
   492                         v = (Sint32) hid_get_data(REP_BUF_DATA(rep), &hitem);
   493                         v -= (hitem.logical_maximum +
   494                               hitem.logical_minimum + 1) / 2;
   495                         v *= 32768 /
   496                             ((hitem.logical_maximum -
   497                               hitem.logical_minimum + 1) / 2);
   498                         if (v != joy->axes[naxe]) {
   499                             SDL_PrivateJoystickAxis(joy, naxe, v);
   500                         }
   501                     } else if (usage == HUG_HAT_SWITCH) {
   502                         v = (Sint32) hid_get_data(REP_BUF_DATA(rep), &hitem);
   503                         SDL_PrivateJoystickHat(joy, 0,
   504                                                hatval_to_sdl(v) -
   505                                                hitem.logical_minimum);
   506                     }
   507                     break;
   508                 }
   509             case HUP_BUTTON:
   510                 v = (Sint32) hid_get_data(REP_BUF_DATA(rep), &hitem);
   511                 if (joy->buttons[nbutton] != v) {
   512                     SDL_PrivateJoystickButton(joy, nbutton, v);
   513                 }
   514                 nbutton++;
   515                 break;
   516             default:
   517                 continue;
   518             }
   519             break;
   520         default:
   521             break;
   522         }
   523     }
   524     hid_end_parse(hdata);
   525 
   526     return;
   527 }
   528 
   529 /* Function to close a joystick after use */
   530 void
   531 SDL_SYS_JoystickClose(SDL_Joystick * joy)
   532 {
   533     if (SDL_strncmp(joy->hwdata->path, "/dev/joy", 8)) {
   534         report_free(&joy->hwdata->inreport);
   535         hid_dispose_report_desc(joy->hwdata->repdesc);
   536     }
   537     close(joy->hwdata->fd);
   538     SDL_free(joy->hwdata->path);
   539     SDL_free(joy->hwdata);
   540 
   541     return;
   542 }
   543 
   544 void
   545 SDL_SYS_JoystickQuit(void)
   546 {
   547     int i;
   548 
   549     for (i = 0; i < MAX_JOYS; i++) {
   550         if (joynames[i] != NULL)
   551             SDL_free(joynames[i]);
   552         if (joydevnames[i] != NULL)
   553             SDL_free(joydevnames[i]);
   554     }
   555 
   556     return;
   557 }
   558 
   559 static int
   560 report_alloc(struct report *r, struct report_desc *rd, int repind)
   561 {
   562     int len;
   563 
   564 #ifdef __DragonFly__
   565     len = hid_report_size(rd, r->rid, repinfo[repind].kind);
   566 #elif __FREEBSD__
   567 # if (__FreeBSD_kernel_version >= 460000) || defined(__FreeBSD_kernel__)
   568 #  if (__FreeBSD_kernel_version <= 500111)
   569     len = hid_report_size(rd, r->rid, repinfo[repind].kind);
   570 #  else
   571     len = hid_report_size(rd, repinfo[repind].kind, r->rid);
   572 #  endif
   573 # else
   574     len = hid_report_size(rd, repinfo[repind].kind, &r->rid);
   575 # endif
   576 #else
   577 # ifdef USBHID_NEW
   578     len = hid_report_size(rd, repinfo[repind].kind, r->rid);
   579 # else
   580     len = hid_report_size(rd, repinfo[repind].kind, &r->rid);
   581 # endif
   582 #endif
   583 
   584     if (len < 0) {
   585         SDL_SetError("Negative HID report size");
   586         return (-1);
   587     }
   588     r->size = len;
   589 
   590     if (r->size > 0) {
   591         r->buf = SDL_malloc(sizeof(*r->buf) - sizeof(REP_BUF_DATA(r)) +
   592                             r->size);
   593         if (r->buf == NULL) {
   594             SDL_OutOfMemory();
   595             return (-1);
   596         }
   597     } else {
   598         r->buf = NULL;
   599     }
   600 
   601     r->status = SREPORT_CLEAN;
   602     return (0);
   603 }
   604 
   605 static void
   606 report_free(struct report *r)
   607 {
   608     if (r->buf != NULL) {
   609         SDL_free(r->buf);
   610     }
   611     r->status = SREPORT_UNINIT;
   612 }
   613 
   614 #endif /* SDL_JOYSTICK_USBHID */
   615 /* vi: set ts=4 sw=4 expandtab: */