src/joystick/bsd/SDL_sysjoystick.c
author Sam Lantinga <slouken@libsdl.org>
Mon, 26 Nov 2012 22:09:58 -0800
changeset 6697 dbda91031456
parent 6401 a4bd9821a796
child 6698 28ab2ef7bfc9
permissions -rw-r--r--
Updated BSD joystick code for the new API
     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         nj.index = SDL_SYS_numjoysticks;
   179         joynames[nj.index] = strdup(s);
   180 
   181         if (SDL_SYS_JoystickOpen(&nj, nj.index) == 0) {
   182             SDL_SYS_JoystickClose(&nj);
   183             SDL_SYS_numjoysticks++;
   184         } else {
   185             SDL_free(joynames[nj.index]);
   186             joynames[nj.index] = NULL;
   187         }
   188     }
   189     for (i = 0; i < MAX_JOY_JOYS; i++) {
   190         SDL_snprintf(s, SDL_arraysize(s), "/dev/joy%d", i);
   191         fd = open(s, O_RDONLY);
   192         if (fd != -1) {
   193             joynames[SDL_SYS_numjoysticks++] = strdup(s);
   194             close(fd);
   195         }
   196     }
   197 
   198     /* Read the default USB HID usage table. */
   199     hid_init(NULL);
   200 
   201     return (SDL_SYS_numjoysticks);
   202 }
   203 
   204 const char *
   205 SDL_SYS_JoystickNameForIndex(int index)
   206 {
   207     if (joydevnames[index] != NULL) {
   208         return (joydevnames[index]);
   209     }
   210     return (joynames[index]);
   211 }
   212 
   213 static int
   214 usage_to_joyaxe(unsigned usage)
   215 {
   216     int joyaxe;
   217     switch (usage) {
   218     case HUG_X:
   219         joyaxe = JOYAXE_X;
   220         break;
   221     case HUG_Y:
   222         joyaxe = JOYAXE_Y;
   223         break;
   224     case HUG_Z:
   225         joyaxe = JOYAXE_Z;
   226         break;
   227     case HUG_SLIDER:
   228         joyaxe = JOYAXE_SLIDER;
   229         break;
   230     case HUG_WHEEL:
   231         joyaxe = JOYAXE_WHEEL;
   232         break;
   233     case HUG_RX:
   234         joyaxe = JOYAXE_RX;
   235         break;
   236     case HUG_RY:
   237         joyaxe = JOYAXE_RY;
   238         break;
   239     case HUG_RZ:
   240         joyaxe = JOYAXE_RZ;
   241         break;
   242     default:
   243         joyaxe = -1;
   244     }
   245     return joyaxe;
   246 }
   247 
   248 static unsigned
   249 hatval_to_sdl(Sint32 hatval)
   250 {
   251     static const unsigned hat_dir_map[8] = {
   252         SDL_HAT_UP, SDL_HAT_RIGHTUP, SDL_HAT_RIGHT, SDL_HAT_RIGHTDOWN,
   253         SDL_HAT_DOWN, SDL_HAT_LEFTDOWN, SDL_HAT_LEFT, SDL_HAT_LEFTUP
   254     };
   255     unsigned result;
   256     if ((hatval & 7) == hatval)
   257         result = hat_dir_map[hatval];
   258     else
   259         result = SDL_HAT_CENTERED;
   260     return result;
   261 }
   262 
   263 
   264 int
   265 SDL_SYS_JoystickOpen(SDL_Joystick * joy, int device_index)
   266 {
   267     char *path = joynames[device_index];
   268     struct joystick_hwdata *hw;
   269     struct hid_item hitem;
   270     struct hid_data *hdata;
   271     struct report *rep;
   272     int fd;
   273     int i;
   274 
   275     fd = open(path, O_RDONLY);
   276     if (fd == -1) {
   277         SDL_SetError("%s: %s", path, strerror(errno));
   278         return (-1);
   279     }
   280 
   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[joy->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, joy->index);
   362                     joydevnames[joy->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: */