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