src/joystick/bsd/SDL_sysjoystick.c
author Sam Lantinga <slouken@libsdl.org>
Fri, 08 Apr 2011 13:03:26 -0700
changeset 5535 96594ac5fd1a
parent 5262 b530ef003506
child 6080 eb0e11b096ab
permissions -rw-r--r--
SDL 1.3 is now under the zlib license.
     1 /*
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2011 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 #include <sys/joystick.h>
    66 #endif
    67 
    68 #if SDL_JOYSTICK_USBHID_MACHINE_JOYSTICK_H
    69 #include <machine/joystick.h>
    70 #endif
    71 
    72 #include "SDL_joystick.h"
    73 #include "../SDL_sysjoystick.h"
    74 #include "../SDL_joystick_c.h"
    75 
    76 #define MAX_UHID_JOYS	4
    77 #define MAX_JOY_JOYS	2
    78 #define MAX_JOYS	(MAX_UHID_JOYS + MAX_JOY_JOYS)
    79 
    80 #if defined(__FREEBSD__) && (__FreeBSD_kernel_version > 800063) && false
    81 struct usb_ctl_report {
    82     int     ucr_report;
    83     u_char  ucr_data[1024]; /* filled data size will vary */
    84 };
    85 #endif
    86 
    87 struct report
    88 {
    89     struct usb_ctl_report *buf; /* Buffer */
    90     size_t size;                /* Buffer size */
    91     int rid;                    /* Report ID */
    92     enum
    93     {
    94         SREPORT_UNINIT,
    95         SREPORT_CLEAN,
    96         SREPORT_DIRTY
    97     } status;
    98 };
    99 
   100 static struct
   101 {
   102     int uhid_report;
   103     hid_kind_t kind;
   104     const char *name;
   105 } const repinfo[] = {
   106     {UHID_INPUT_REPORT, hid_input, "input"},
   107     {UHID_OUTPUT_REPORT, hid_output, "output"},
   108     {UHID_FEATURE_REPORT, hid_feature, "feature"}
   109 };
   110 
   111 enum
   112 {
   113     REPORT_INPUT = 0,
   114     REPORT_OUTPUT = 1,
   115     REPORT_FEATURE = 2
   116 };
   117 
   118 enum
   119 {
   120     JOYAXE_X,
   121     JOYAXE_Y,
   122     JOYAXE_Z,
   123     JOYAXE_SLIDER,
   124     JOYAXE_WHEEL,
   125     JOYAXE_RX,
   126     JOYAXE_RY,
   127     JOYAXE_RZ,
   128     JOYAXE_count
   129 };
   130 
   131 struct joystick_hwdata
   132 {
   133     int fd;
   134     char *path;
   135     enum
   136     {
   137         BSDJOY_UHID,            /* uhid(4) */
   138         BSDJOY_JOY              /* joy(4) */
   139     } type;
   140     struct report_desc *repdesc;
   141     struct report inreport;
   142     int axis_map[JOYAXE_count]; /* map present JOYAXE_* to 0,1,.. */
   143 };
   144 
   145 static char *joynames[MAX_JOYS];
   146 static char *joydevnames[MAX_JOYS];
   147 
   148 static int report_alloc(struct report *, struct report_desc *, int);
   149 static void report_free(struct report *);
   150 
   151 #if defined(USBHID_UCR_DATA) || (defined(__FREEBSD__) && (__FreeBSD_kernel_version > 800063)) || defined(__FreeBSD_kernel__)
   152 #define REP_BUF_DATA(rep) ((rep)->buf->ucr_data)
   153 #else
   154 #define REP_BUF_DATA(rep) ((rep)->buf->data)
   155 #endif
   156 
   157 int
   158 SDL_SYS_JoystickInit(void)
   159 {
   160     char s[16];
   161     int i, fd;
   162 
   163     SDL_numjoysticks = 0;
   164 
   165     SDL_memset(joynames, 0, sizeof(joynames));
   166     SDL_memset(joydevnames, 0, sizeof(joydevnames));
   167 
   168     for (i = 0; i < MAX_UHID_JOYS; i++) {
   169         SDL_Joystick nj;
   170 
   171         SDL_snprintf(s, SDL_arraysize(s), "/dev/uhid%d", i);
   172 
   173         nj.index = SDL_numjoysticks;
   174         joynames[nj.index] = strdup(s);
   175 
   176         if (SDL_SYS_JoystickOpen(&nj) == 0) {
   177             SDL_SYS_JoystickClose(&nj);
   178             SDL_numjoysticks++;
   179         } else {
   180             SDL_free(joynames[nj.index]);
   181             joynames[nj.index] = NULL;
   182         }
   183     }
   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);
   187         if (fd != -1) {
   188             joynames[SDL_numjoysticks++] = strdup(s);
   189             close(fd);
   190         }
   191     }
   192 
   193     /* Read the default USB HID usage table. */
   194     hid_init(NULL);
   195 
   196     return (SDL_numjoysticks);
   197 }
   198 
   199 const char *
   200 SDL_SYS_JoystickName(int index)
   201 {
   202     if (joydevnames[index] != NULL) {
   203         return (joydevnames[index]);
   204     }
   205     return (joynames[index]);
   206 }
   207 
   208 static int
   209 usage_to_joyaxe(unsigned usage)
   210 {
   211     int joyaxe;
   212     switch (usage) {
   213     case HUG_X:
   214         joyaxe = JOYAXE_X;
   215         break;
   216     case HUG_Y:
   217         joyaxe = JOYAXE_Y;
   218         break;
   219     case HUG_Z:
   220         joyaxe = JOYAXE_Z;
   221         break;
   222     case HUG_SLIDER:
   223         joyaxe = JOYAXE_SLIDER;
   224         break;
   225     case HUG_WHEEL:
   226         joyaxe = JOYAXE_WHEEL;
   227         break;
   228     case HUG_RX:
   229         joyaxe = JOYAXE_RX;
   230         break;
   231     case HUG_RY:
   232         joyaxe = JOYAXE_RY;
   233         break;
   234     case HUG_RZ:
   235         joyaxe = JOYAXE_RZ;
   236         break;
   237     default:
   238         joyaxe = -1;
   239     }
   240     return joyaxe;
   241 }
   242 
   243 static unsigned
   244 hatval_to_sdl(Sint32 hatval)
   245 {
   246     static const unsigned hat_dir_map[8] = {
   247         SDL_HAT_UP, SDL_HAT_RIGHTUP, SDL_HAT_RIGHT, SDL_HAT_RIGHTDOWN,
   248         SDL_HAT_DOWN, SDL_HAT_LEFTDOWN, SDL_HAT_LEFT, SDL_HAT_LEFTUP
   249     };
   250     unsigned result;
   251     if ((hatval & 7) == hatval)
   252         result = hat_dir_map[hatval];
   253     else
   254         result = SDL_HAT_CENTERED;
   255     return result;
   256 }
   257 
   258 
   259 int
   260 SDL_SYS_JoystickOpen(SDL_Joystick * joy)
   261 {
   262     char *path = joynames[joy->index];
   263     struct joystick_hwdata *hw;
   264     struct hid_item hitem;
   265     struct hid_data *hdata;
   266     struct report *rep;
   267     int fd;
   268     int i;
   269 
   270     fd = open(path, O_RDONLY);
   271     if (fd == -1) {
   272         SDL_SetError("%s: %s", path, strerror(errno));
   273         return (-1);
   274     }
   275 
   276     hw = (struct joystick_hwdata *)
   277         SDL_malloc(sizeof(struct joystick_hwdata));
   278     if (hw == NULL) {
   279         SDL_OutOfMemory();
   280         close(fd);
   281         return (-1);
   282     }
   283     joy->hwdata = hw;
   284     hw->fd = fd;
   285     hw->path = strdup(path);
   286     if (!SDL_strncmp(path, "/dev/joy", 8)) {
   287         hw->type = BSDJOY_JOY;
   288         joy->naxes = 2;
   289         joy->nbuttons = 2;
   290         joy->nhats = 0;
   291         joy->nballs = 0;
   292         joydevnames[joy->index] = strdup("Gameport joystick");
   293         goto usbend;
   294     } else {
   295         hw->type = BSDJOY_UHID;
   296     }
   297 
   298     {
   299         int ax;
   300         for (ax = 0; ax < JOYAXE_count; ax++)
   301             hw->axis_map[ax] = -1;
   302     }
   303     hw->repdesc = hid_get_report_desc(fd);
   304     if (hw->repdesc == NULL) {
   305         SDL_SetError("%s: USB_GET_REPORT_DESC: %s", hw->path,
   306                      strerror(errno));
   307         goto usberr;
   308     }
   309     rep = &hw->inreport;
   310 #if defined(__FREEBSD__) && (__FreeBSD_kernel_version > 800063) || defined(__FreeBSD_kernel__)
   311     rep->rid = hid_get_report_id(fd);
   312     if (rep->rid < 0) {
   313 #else
   314     if (ioctl(fd, USB_GET_REPORT_ID, &rep->rid) < 0) {
   315 #endif
   316         rep->rid = -1;          /* XXX */
   317     }
   318     if (report_alloc(rep, hw->repdesc, REPORT_INPUT) < 0) {
   319         goto usberr;
   320     }
   321     if (rep->size <= 0) {
   322         SDL_SetError("%s: Input report descriptor has invalid length",
   323                      hw->path);
   324         goto usberr;
   325     }
   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);
   328 #else
   329     hdata = hid_start_parse(hw->repdesc, 1 << hid_input);
   330 #endif
   331     if (hdata == NULL) {
   332         SDL_SetError("%s: Cannot start HID parser", hw->path);
   333         goto usberr;
   334     }
   335     joy->naxes = 0;
   336     joy->nbuttons = 0;
   337     joy->nhats = 0;
   338     joy->nballs = 0;
   339     for (i = 0; i < JOYAXE_count; i++)
   340         hw->axis_map[i] = -1;
   341 
   342     while (hid_get_item(hdata, &hitem) > 0) {
   343         char *sp;
   344         const char *s;
   345 
   346         switch (hitem.kind) {
   347         case hid_collection:
   348             switch (HID_PAGE(hitem.usage)) {
   349             case HUP_GENERIC_DESKTOP:
   350                 switch (HID_USAGE(hitem.usage)) {
   351                 case HUG_JOYSTICK:
   352                 case HUG_GAME_PAD:
   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)",
   356                                  s, joy->index);
   357                     joydevnames[joy->index] = sp;
   358                 }
   359             }
   360             break;
   361         case hid_input:
   362             switch (HID_PAGE(hitem.usage)) {
   363             case HUP_GENERIC_DESKTOP:
   364                 {
   365                     unsigned usage = HID_USAGE(hitem.usage);
   366                     int joyaxe = usage_to_joyaxe(usage);
   367                     if (joyaxe >= 0) {
   368                         hw->axis_map[joyaxe] = 1;
   369                     } else if (usage == HUG_HAT_SWITCH) {
   370                         joy->nhats++;
   371                     }
   372                     break;
   373                 }
   374             case HUP_BUTTON:
   375                 joy->nbuttons++;
   376                 break;
   377             default:
   378                 break;
   379             }
   380             break;
   381         default:
   382             break;
   383         }
   384     }
   385     hid_end_parse(hdata);
   386     for (i = 0; i < JOYAXE_count; i++)
   387         if (hw->axis_map[i] > 0)
   388             hw->axis_map[i] = joy->naxes++;
   389 
   390   usbend:
   391     /* The poll blocks the event thread. */
   392     fcntl(fd, F_SETFL, O_NONBLOCK);
   393 
   394     return (0);
   395   usberr:
   396     close(hw->fd);
   397     SDL_free(hw->path);
   398     SDL_free(hw);
   399     return (-1);
   400 }
   401 
   402 void
   403 SDL_SYS_JoystickUpdate(SDL_Joystick * joy)
   404 {
   405     struct hid_item hitem;
   406     struct hid_data *hdata;
   407     struct report *rep;
   408     int nbutton, naxe = -1;
   409     Sint32 v;
   410 
   411 #if defined(__FREEBSD__) || SDL_JOYSTICK_USBHID_MACHINE_JOYSTICK_H || defined(__FreeBSD_kernel__)
   412     struct joystick gameport;
   413     static int x, y, xmin = 0xffff, ymin = 0xffff, xmax = 0, ymax = 0;
   414 
   415     if (joy->hwdata->type == BSDJOY_JOY) {
   416         if (read(joy->hwdata->fd, &gameport, sizeof gameport) !=
   417             sizeof gameport)
   418             return;
   419         if (abs(x - gameport.x) > 8) {
   420             x = gameport.x;
   421             if (x < xmin) {
   422                 xmin = x;
   423             }
   424             if (x > xmax) {
   425                 xmax = x;
   426             }
   427             if (xmin == xmax) {
   428                 xmin--;
   429                 xmax++;
   430             }
   431             v = (Sint32) x;
   432             v -= (xmax + xmin + 1) / 2;
   433             v *= 32768 / ((xmax - xmin + 1) / 2);
   434             SDL_PrivateJoystickAxis(joy, 0, v);
   435         }
   436         if (abs(y - gameport.y) > 8) {
   437             y = gameport.y;
   438             if (y < ymin) {
   439                 ymin = y;
   440             }
   441             if (y > ymax) {
   442                 ymax = y;
   443             }
   444             if (ymin == ymax) {
   445                 ymin--;
   446                 ymax++;
   447             }
   448             v = (Sint32) y;
   449             v -= (ymax + ymin + 1) / 2;
   450             v *= 32768 / ((ymax - ymin + 1) / 2);
   451             SDL_PrivateJoystickAxis(joy, 1, v);
   452         }
   453         if (gameport.b1 != joy->buttons[0]) {
   454             SDL_PrivateJoystickButton(joy, 0, gameport.b1);
   455         }
   456         if (gameport.b2 != joy->buttons[1]) {
   457             SDL_PrivateJoystickButton(joy, 1, gameport.b2);
   458         }
   459         return;
   460     }
   461 #endif /* defined(__FREEBSD__) || SDL_JOYSTICK_USBHID_MACHINE_JOYSTICK_H */
   462 
   463     rep = &joy->hwdata->inreport;
   464 
   465     if (read(joy->hwdata->fd, REP_BUF_DATA(rep), rep->size) != rep->size) {
   466         return;
   467     }
   468 #if defined(USBHID_NEW) || (defined(__FREEBSD__) && __FreeBSD_kernel_version >= 500111) || defined(__FreeBSD_kernel__)
   469     hdata = hid_start_parse(joy->hwdata->repdesc, 1 << hid_input, rep->rid);
   470 #else
   471     hdata = hid_start_parse(joy->hwdata->repdesc, 1 << hid_input);
   472 #endif
   473     if (hdata == NULL) {
   474         fprintf(stderr, "%s: Cannot start HID parser\n", joy->hwdata->path);
   475         return;
   476     }
   477 
   478     for (nbutton = 0; hid_get_item(hdata, &hitem) > 0;) {
   479         switch (hitem.kind) {
   480         case hid_input:
   481             switch (HID_PAGE(hitem.usage)) {
   482             case HUP_GENERIC_DESKTOP:
   483                 {
   484                     unsigned usage = HID_USAGE(hitem.usage);
   485                     int joyaxe = usage_to_joyaxe(usage);
   486                     if (joyaxe >= 0) {
   487                         naxe = joy->hwdata->axis_map[joyaxe];
   488                         /* scaleaxe */
   489                         v = (Sint32) hid_get_data(REP_BUF_DATA(rep), &hitem);
   490                         v -= (hitem.logical_maximum +
   491                               hitem.logical_minimum + 1) / 2;
   492                         v *= 32768 /
   493                             ((hitem.logical_maximum -
   494                               hitem.logical_minimum + 1) / 2);
   495                         if (v != joy->axes[naxe]) {
   496                             SDL_PrivateJoystickAxis(joy, naxe, v);
   497                         }
   498                     } else if (usage == HUG_HAT_SWITCH) {
   499                         v = (Sint32) hid_get_data(REP_BUF_DATA(rep), &hitem);
   500                         SDL_PrivateJoystickHat(joy, 0,
   501                                                hatval_to_sdl(v) -
   502                                                hitem.logical_minimum);
   503                     }
   504                     break;
   505                 }
   506             case HUP_BUTTON:
   507                 v = (Sint32) hid_get_data(REP_BUF_DATA(rep), &hitem);
   508                 if (joy->buttons[nbutton] != v) {
   509                     SDL_PrivateJoystickButton(joy, nbutton, v);
   510                 }
   511                 nbutton++;
   512                 break;
   513             default:
   514                 continue;
   515             }
   516             break;
   517         default:
   518             break;
   519         }
   520     }
   521     hid_end_parse(hdata);
   522 
   523     return;
   524 }
   525 
   526 /* Function to close a joystick after use */
   527 void
   528 SDL_SYS_JoystickClose(SDL_Joystick * joy)
   529 {
   530     if (SDL_strncmp(joy->hwdata->path, "/dev/joy", 8)) {
   531         report_free(&joy->hwdata->inreport);
   532         hid_dispose_report_desc(joy->hwdata->repdesc);
   533     }
   534     close(joy->hwdata->fd);
   535     SDL_free(joy->hwdata->path);
   536     SDL_free(joy->hwdata);
   537 
   538     return;
   539 }
   540 
   541 void
   542 SDL_SYS_JoystickQuit(void)
   543 {
   544     int i;
   545 
   546     for (i = 0; i < MAX_JOYS; i++) {
   547         if (joynames[i] != NULL)
   548             SDL_free(joynames[i]);
   549         if (joydevnames[i] != NULL)
   550             SDL_free(joydevnames[i]);
   551     }
   552 
   553     return;
   554 }
   555 
   556 static int
   557 report_alloc(struct report *r, struct report_desc *rd, int repind)
   558 {
   559     int len;
   560 
   561 #ifdef __DragonFly__
   562     len = hid_report_size(rd, r->rid, repinfo[repind].kind);
   563 #elif __FREEBSD__
   564 # if (__FreeBSD_kernel_version >= 460000) || defined(__FreeBSD_kernel__)
   565 #  if (__FreeBSD_kernel_version <= 500111)
   566     len = hid_report_size(rd, r->rid, repinfo[repind].kind);
   567 #  else
   568     len = hid_report_size(rd, repinfo[repind].kind, r->rid);
   569 #  endif
   570 # else
   571     len = hid_report_size(rd, repinfo[repind].kind, &r->rid);
   572 # endif
   573 #else
   574 # ifdef USBHID_NEW
   575     len = hid_report_size(rd, repinfo[repind].kind, r->rid);
   576 # else
   577     len = hid_report_size(rd, repinfo[repind].kind, &r->rid);
   578 # endif
   579 #endif
   580 
   581     if (len < 0) {
   582         SDL_SetError("Negative HID report size");
   583         return (-1);
   584     }
   585     r->size = len;
   586 
   587     if (r->size > 0) {
   588         r->buf = SDL_malloc(sizeof(*r->buf) - sizeof(REP_BUF_DATA(r)) +
   589                             r->size);
   590         if (r->buf == NULL) {
   591             SDL_OutOfMemory();
   592             return (-1);
   593         }
   594     } else {
   595         r->buf = NULL;
   596     }
   597 
   598     r->status = SREPORT_CLEAN;
   599     return (0);
   600 }
   601 
   602 static void
   603 report_free(struct report *r)
   604 {
   605     if (r->buf != NULL) {
   606         SDL_free(r->buf);
   607     }
   608     r->status = SREPORT_UNINIT;
   609 }
   610 
   611 #endif /* SDL_JOYSTICK_USBHID */
   612 /* vi: set ts=4 sw=4 expandtab: */