src/joystick/bsd/SDL_sysjoystick.c
author Sam Lantinga <slouken@libsdl.org>
Fri, 15 Feb 2013 08:47:44 -0800
changeset 6885 700f1b25f77f
parent 6744 4ac2bb6dc80c
child 6963 4658b1101200
permissions -rw-r--r--
Happy New Year!
     1 /*
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2013 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 static 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 int SDL_SYS_NumJoysticks()
   204 {
   205     return SDL_SYS_numjoysticks;
   206 }
   207 
   208 void SDL_SYS_JoystickDetect()
   209 {
   210 }
   211 
   212 SDL_bool SDL_SYS_JoystickNeedsPolling()
   213 {
   214     return SDL_FALSE;
   215 }
   216 
   217 const char *
   218 SDL_SYS_JoystickNameForDeviceIndex(int device_index)
   219 {
   220     if (joydevnames[device_index] != NULL) {
   221         return (joydevnames[device_index]);
   222     }
   223     return (joynames[device_index]);
   224 }
   225 
   226 /* Function to perform the mapping from device index to the instance id for this index */
   227 SDL_JoystickID SDL_SYS_GetInstanceIdOfDeviceIndex(int device_index)
   228 {
   229     return device_index;
   230 }
   231 
   232 static int
   233 usage_to_joyaxe(unsigned usage)
   234 {
   235     int joyaxe;
   236     switch (usage) {
   237     case HUG_X:
   238         joyaxe = JOYAXE_X;
   239         break;
   240     case HUG_Y:
   241         joyaxe = JOYAXE_Y;
   242         break;
   243     case HUG_Z:
   244         joyaxe = JOYAXE_Z;
   245         break;
   246     case HUG_SLIDER:
   247         joyaxe = JOYAXE_SLIDER;
   248         break;
   249     case HUG_WHEEL:
   250         joyaxe = JOYAXE_WHEEL;
   251         break;
   252     case HUG_RX:
   253         joyaxe = JOYAXE_RX;
   254         break;
   255     case HUG_RY:
   256         joyaxe = JOYAXE_RY;
   257         break;
   258     case HUG_RZ:
   259         joyaxe = JOYAXE_RZ;
   260         break;
   261     default:
   262         joyaxe = -1;
   263     }
   264     return joyaxe;
   265 }
   266 
   267 static unsigned
   268 hatval_to_sdl(Sint32 hatval)
   269 {
   270     static const unsigned hat_dir_map[8] = {
   271         SDL_HAT_UP, SDL_HAT_RIGHTUP, SDL_HAT_RIGHT, SDL_HAT_RIGHTDOWN,
   272         SDL_HAT_DOWN, SDL_HAT_LEFTDOWN, SDL_HAT_LEFT, SDL_HAT_LEFTUP
   273     };
   274     unsigned result;
   275     if ((hatval & 7) == hatval)
   276         result = hat_dir_map[hatval];
   277     else
   278         result = SDL_HAT_CENTERED;
   279     return result;
   280 }
   281 
   282 
   283 int
   284 SDL_SYS_JoystickOpen(SDL_Joystick * joy, int device_index)
   285 {
   286     char *path = joynames[device_index];
   287     struct joystick_hwdata *hw;
   288     struct hid_item hitem;
   289     struct hid_data *hdata;
   290     struct report *rep;
   291     int fd;
   292     int i;
   293 
   294     fd = open(path, O_RDONLY);
   295     if (fd == -1) {
   296         SDL_SetError("%s: %s", path, strerror(errno));
   297         return (-1);
   298     }
   299 
   300     joy->instance_id = device_index;
   301     hw = (struct joystick_hwdata *)
   302         SDL_malloc(sizeof(struct joystick_hwdata));
   303     if (hw == NULL) {
   304         SDL_OutOfMemory();
   305         close(fd);
   306         return (-1);
   307     }
   308     joy->hwdata = hw;
   309     hw->fd = fd;
   310     hw->path = strdup(path);
   311     if (!SDL_strncmp(path, "/dev/joy", 8)) {
   312         hw->type = BSDJOY_JOY;
   313         joy->naxes = 2;
   314         joy->nbuttons = 2;
   315         joy->nhats = 0;
   316         joy->nballs = 0;
   317         joydevnames[device_index] = strdup("Gameport joystick");
   318         goto usbend;
   319     } else {
   320         hw->type = BSDJOY_UHID;
   321     }
   322 
   323     {
   324         int ax;
   325         for (ax = 0; ax < JOYAXE_count; ax++)
   326             hw->axis_map[ax] = -1;
   327     }
   328     hw->repdesc = hid_get_report_desc(fd);
   329     if (hw->repdesc == NULL) {
   330         SDL_SetError("%s: USB_GET_REPORT_DESC: %s", hw->path,
   331                      strerror(errno));
   332         goto usberr;
   333     }
   334     rep = &hw->inreport;
   335 #if defined(__FREEBSD__) && (__FreeBSD_kernel_version > 800063) || defined(__FreeBSD_kernel__)
   336     rep->rid = hid_get_report_id(fd);
   337     if (rep->rid < 0) {
   338 #else
   339     if (ioctl(fd, USB_GET_REPORT_ID, &rep->rid) < 0) {
   340 #endif
   341         rep->rid = -1;          /* XXX */
   342     }
   343     if (report_alloc(rep, hw->repdesc, REPORT_INPUT) < 0) {
   344         goto usberr;
   345     }
   346     if (rep->size <= 0) {
   347         SDL_SetError("%s: Input report descriptor has invalid length",
   348                      hw->path);
   349         goto usberr;
   350     }
   351 #if defined(USBHID_NEW) || (defined(__FREEBSD__) && __FreeBSD_kernel_version >= 500111) || defined(__FreeBSD_kernel__)
   352     hdata = hid_start_parse(hw->repdesc, 1 << hid_input, rep->rid);
   353 #else
   354     hdata = hid_start_parse(hw->repdesc, 1 << hid_input);
   355 #endif
   356     if (hdata == NULL) {
   357         SDL_SetError("%s: Cannot start HID parser", hw->path);
   358         goto usberr;
   359     }
   360     joy->naxes = 0;
   361     joy->nbuttons = 0;
   362     joy->nhats = 0;
   363     joy->nballs = 0;
   364     for (i = 0; i < JOYAXE_count; i++)
   365         hw->axis_map[i] = -1;
   366 
   367     while (hid_get_item(hdata, &hitem) > 0) {
   368         char *sp;
   369         const char *s;
   370 
   371         switch (hitem.kind) {
   372         case hid_collection:
   373             switch (HID_PAGE(hitem.usage)) {
   374             case HUP_GENERIC_DESKTOP:
   375                 switch (HID_USAGE(hitem.usage)) {
   376                 case HUG_JOYSTICK:
   377                 case HUG_GAME_PAD:
   378                     s = hid_usage_in_page(hitem.usage);
   379                     sp = SDL_malloc(SDL_strlen(s) + 5);
   380                     SDL_snprintf(sp, SDL_strlen(s) + 5, "%s (%d)",
   381                                  s, device_index);
   382                     joydevnames[device_index] = sp;
   383                 }
   384             }
   385             break;
   386         case hid_input:
   387             switch (HID_PAGE(hitem.usage)) {
   388             case HUP_GENERIC_DESKTOP:
   389                 {
   390                     unsigned usage = HID_USAGE(hitem.usage);
   391                     int joyaxe = usage_to_joyaxe(usage);
   392                     if (joyaxe >= 0) {
   393                         hw->axis_map[joyaxe] = 1;
   394                     } else if (usage == HUG_HAT_SWITCH) {
   395                         joy->nhats++;
   396                     }
   397                     break;
   398                 }
   399             case HUP_BUTTON:
   400                 joy->nbuttons++;
   401                 break;
   402             default:
   403                 break;
   404             }
   405             break;
   406         default:
   407             break;
   408         }
   409     }
   410     hid_end_parse(hdata);
   411     for (i = 0; i < JOYAXE_count; i++)
   412         if (hw->axis_map[i] > 0)
   413             hw->axis_map[i] = joy->naxes++;
   414 
   415   usbend:
   416     /* The poll blocks the event thread. */
   417     fcntl(fd, F_SETFL, O_NONBLOCK);
   418 
   419     return (0);
   420   usberr:
   421     close(hw->fd);
   422     SDL_free(hw->path);
   423     SDL_free(hw);
   424     return (-1);
   425 }
   426 
   427 /* Function to determine is this joystick is attached to the system right now */
   428 SDL_bool SDL_SYS_JoystickAttached(SDL_Joystick *joystick)
   429 {
   430     return SDL_TRUE;
   431 }
   432 
   433 void
   434 SDL_SYS_JoystickUpdate(SDL_Joystick * joy)
   435 {
   436     struct hid_item hitem;
   437     struct hid_data *hdata;
   438     struct report *rep;
   439     int nbutton, naxe = -1;
   440     Sint32 v;
   441 
   442 #if defined(__FREEBSD__) || SDL_JOYSTICK_USBHID_MACHINE_JOYSTICK_H || defined(__FreeBSD_kernel__)
   443     struct joystick gameport;
   444     static int x, y, xmin = 0xffff, ymin = 0xffff, xmax = 0, ymax = 0;
   445 
   446     if (joy->hwdata->type == BSDJOY_JOY) {
   447         if (read(joy->hwdata->fd, &gameport, sizeof gameport) !=
   448             sizeof gameport)
   449             return;
   450         if (abs(x - gameport.x) > 8) {
   451             x = gameport.x;
   452             if (x < xmin) {
   453                 xmin = x;
   454             }
   455             if (x > xmax) {
   456                 xmax = x;
   457             }
   458             if (xmin == xmax) {
   459                 xmin--;
   460                 xmax++;
   461             }
   462             v = (Sint32) x;
   463             v -= (xmax + xmin + 1) / 2;
   464             v *= 32768 / ((xmax - xmin + 1) / 2);
   465             SDL_PrivateJoystickAxis(joy, 0, v);
   466         }
   467         if (abs(y - gameport.y) > 8) {
   468             y = gameport.y;
   469             if (y < ymin) {
   470                 ymin = y;
   471             }
   472             if (y > ymax) {
   473                 ymax = y;
   474             }
   475             if (ymin == ymax) {
   476                 ymin--;
   477                 ymax++;
   478             }
   479             v = (Sint32) y;
   480             v -= (ymax + ymin + 1) / 2;
   481             v *= 32768 / ((ymax - ymin + 1) / 2);
   482             SDL_PrivateJoystickAxis(joy, 1, v);
   483         }
   484         if (gameport.b1 != joy->buttons[0]) {
   485             SDL_PrivateJoystickButton(joy, 0, gameport.b1);
   486         }
   487         if (gameport.b2 != joy->buttons[1]) {
   488             SDL_PrivateJoystickButton(joy, 1, gameport.b2);
   489         }
   490         return;
   491     }
   492 #endif /* defined(__FREEBSD__) || SDL_JOYSTICK_USBHID_MACHINE_JOYSTICK_H */
   493 
   494     rep = &joy->hwdata->inreport;
   495 
   496     if (read(joy->hwdata->fd, REP_BUF_DATA(rep), rep->size) != rep->size) {
   497         return;
   498     }
   499 #if defined(USBHID_NEW) || (defined(__FREEBSD__) && __FreeBSD_kernel_version >= 500111) || defined(__FreeBSD_kernel__)
   500     hdata = hid_start_parse(joy->hwdata->repdesc, 1 << hid_input, rep->rid);
   501 #else
   502     hdata = hid_start_parse(joy->hwdata->repdesc, 1 << hid_input);
   503 #endif
   504     if (hdata == NULL) {
   505         fprintf(stderr, "%s: Cannot start HID parser\n", joy->hwdata->path);
   506         return;
   507     }
   508 
   509     for (nbutton = 0; hid_get_item(hdata, &hitem) > 0;) {
   510         switch (hitem.kind) {
   511         case hid_input:
   512             switch (HID_PAGE(hitem.usage)) {
   513             case HUP_GENERIC_DESKTOP:
   514                 {
   515                     unsigned usage = HID_USAGE(hitem.usage);
   516                     int joyaxe = usage_to_joyaxe(usage);
   517                     if (joyaxe >= 0) {
   518                         naxe = joy->hwdata->axis_map[joyaxe];
   519                         /* scaleaxe */
   520                         v = (Sint32) hid_get_data(REP_BUF_DATA(rep), &hitem);
   521                         v -= (hitem.logical_maximum +
   522                               hitem.logical_minimum + 1) / 2;
   523                         v *= 32768 /
   524                             ((hitem.logical_maximum -
   525                               hitem.logical_minimum + 1) / 2);
   526                         if (v != joy->axes[naxe]) {
   527                             SDL_PrivateJoystickAxis(joy, naxe, v);
   528                         }
   529                     } else if (usage == HUG_HAT_SWITCH) {
   530                         v = (Sint32) hid_get_data(REP_BUF_DATA(rep), &hitem);
   531                         SDL_PrivateJoystickHat(joy, 0,
   532                                                hatval_to_sdl(v) -
   533                                                hitem.logical_minimum);
   534                     }
   535                     break;
   536                 }
   537             case HUP_BUTTON:
   538                 v = (Sint32) hid_get_data(REP_BUF_DATA(rep), &hitem);
   539                 if (joy->buttons[nbutton] != v) {
   540                     SDL_PrivateJoystickButton(joy, nbutton, v);
   541                 }
   542                 nbutton++;
   543                 break;
   544             default:
   545                 continue;
   546             }
   547             break;
   548         default:
   549             break;
   550         }
   551     }
   552     hid_end_parse(hdata);
   553 
   554     return;
   555 }
   556 
   557 /* Function to close a joystick after use */
   558 void
   559 SDL_SYS_JoystickClose(SDL_Joystick * joy)
   560 {
   561     if (SDL_strncmp(joy->hwdata->path, "/dev/joy", 8)) {
   562         report_free(&joy->hwdata->inreport);
   563         hid_dispose_report_desc(joy->hwdata->repdesc);
   564     }
   565     close(joy->hwdata->fd);
   566     SDL_free(joy->hwdata->path);
   567     SDL_free(joy->hwdata);
   568 
   569     return;
   570 }
   571 
   572 void
   573 SDL_SYS_JoystickQuit(void)
   574 {
   575     int i;
   576 
   577     for (i = 0; i < MAX_JOYS; i++) {
   578         if (joynames[i] != NULL)
   579             SDL_free(joynames[i]);
   580         if (joydevnames[i] != NULL)
   581             SDL_free(joydevnames[i]);
   582     }
   583 
   584     return;
   585 }
   586 
   587 SDL_JoystickGUID SDL_SYS_JoystickGetDeviceGUID( int device_index )
   588 {
   589     SDL_JoystickGUID guid;
   590     // the GUID is just the first 16 chars of the name for now
   591     const char *name = SDL_SYS_JoystickNameForDeviceIndex( device_index );
   592     SDL_zero( guid );
   593     SDL_memcpy( &guid, name, SDL_min( sizeof(guid), SDL_strlen( name ) ) );
   594     return guid;
   595 }
   596 
   597 SDL_JoystickGUID SDL_SYS_JoystickGetGUID(SDL_Joystick * joystick)
   598 {
   599     SDL_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: */