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