src/joystick/bsd/SDL_sysjoystick.c
author Sam Lantinga <slouken@libsdl.org>
Mon, 26 Nov 2012 22:27:49 -0800
changeset 6698 28ab2ef7bfc9
parent 6697 dbda91031456
child 6700 c8f3d3f0251e
permissions -rw-r--r--
Completed adding new hotplug stubs for the joystick implementations
     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     joy->instance_id = device_index;
   282     hw = (struct joystick_hwdata *)
   283         SDL_malloc(sizeof(struct joystick_hwdata));
   284     if (hw == NULL) {
   285         SDL_OutOfMemory();
   286         close(fd);
   287         return (-1);
   288     }
   289     joy->hwdata = hw;
   290     hw->fd = fd;
   291     hw->path = strdup(path);
   292     if (!SDL_strncmp(path, "/dev/joy", 8)) {
   293         hw->type = BSDJOY_JOY;
   294         joy->naxes = 2;
   295         joy->nbuttons = 2;
   296         joy->nhats = 0;
   297         joy->nballs = 0;
   298         joydevnames[joy->index] = strdup("Gameport joystick");
   299         goto usbend;
   300     } else {
   301         hw->type = BSDJOY_UHID;
   302     }
   303 
   304     {
   305         int ax;
   306         for (ax = 0; ax < JOYAXE_count; ax++)
   307             hw->axis_map[ax] = -1;
   308     }
   309     hw->repdesc = hid_get_report_desc(fd);
   310     if (hw->repdesc == NULL) {
   311         SDL_SetError("%s: USB_GET_REPORT_DESC: %s", hw->path,
   312                      strerror(errno));
   313         goto usberr;
   314     }
   315     rep = &hw->inreport;
   316 #if defined(__FREEBSD__) && (__FreeBSD_kernel_version > 800063) || defined(__FreeBSD_kernel__)
   317     rep->rid = hid_get_report_id(fd);
   318     if (rep->rid < 0) {
   319 #else
   320     if (ioctl(fd, USB_GET_REPORT_ID, &rep->rid) < 0) {
   321 #endif
   322         rep->rid = -1;          /* XXX */
   323     }
   324     if (report_alloc(rep, hw->repdesc, REPORT_INPUT) < 0) {
   325         goto usberr;
   326     }
   327     if (rep->size <= 0) {
   328         SDL_SetError("%s: Input report descriptor has invalid length",
   329                      hw->path);
   330         goto usberr;
   331     }
   332 #if defined(USBHID_NEW) || (defined(__FREEBSD__) && __FreeBSD_kernel_version >= 500111) || defined(__FreeBSD_kernel__)
   333     hdata = hid_start_parse(hw->repdesc, 1 << hid_input, rep->rid);
   334 #else
   335     hdata = hid_start_parse(hw->repdesc, 1 << hid_input);
   336 #endif
   337     if (hdata == NULL) {
   338         SDL_SetError("%s: Cannot start HID parser", hw->path);
   339         goto usberr;
   340     }
   341     joy->naxes = 0;
   342     joy->nbuttons = 0;
   343     joy->nhats = 0;
   344     joy->nballs = 0;
   345     for (i = 0; i < JOYAXE_count; i++)
   346         hw->axis_map[i] = -1;
   347 
   348     while (hid_get_item(hdata, &hitem) > 0) {
   349         char *sp;
   350         const char *s;
   351 
   352         switch (hitem.kind) {
   353         case hid_collection:
   354             switch (HID_PAGE(hitem.usage)) {
   355             case HUP_GENERIC_DESKTOP:
   356                 switch (HID_USAGE(hitem.usage)) {
   357                 case HUG_JOYSTICK:
   358                 case HUG_GAME_PAD:
   359                     s = hid_usage_in_page(hitem.usage);
   360                     sp = SDL_malloc(SDL_strlen(s) + 5);
   361                     SDL_snprintf(sp, SDL_strlen(s) + 5, "%s (%d)",
   362                                  s, joy->index);
   363                     joydevnames[joy->index] = sp;
   364                 }
   365             }
   366             break;
   367         case hid_input:
   368             switch (HID_PAGE(hitem.usage)) {
   369             case HUP_GENERIC_DESKTOP:
   370                 {
   371                     unsigned usage = HID_USAGE(hitem.usage);
   372                     int joyaxe = usage_to_joyaxe(usage);
   373                     if (joyaxe >= 0) {
   374                         hw->axis_map[joyaxe] = 1;
   375                     } else if (usage == HUG_HAT_SWITCH) {
   376                         joy->nhats++;
   377                     }
   378                     break;
   379                 }
   380             case HUP_BUTTON:
   381                 joy->nbuttons++;
   382                 break;
   383             default:
   384                 break;
   385             }
   386             break;
   387         default:
   388             break;
   389         }
   390     }
   391     hid_end_parse(hdata);
   392     for (i = 0; i < JOYAXE_count; i++)
   393         if (hw->axis_map[i] > 0)
   394             hw->axis_map[i] = joy->naxes++;
   395 
   396   usbend:
   397     /* The poll blocks the event thread. */
   398     fcntl(fd, F_SETFL, O_NONBLOCK);
   399 
   400     return (0);
   401   usberr:
   402     close(hw->fd);
   403     SDL_free(hw->path);
   404     SDL_free(hw);
   405     return (-1);
   406 }
   407 
   408 void
   409 SDL_SYS_JoystickUpdate(SDL_Joystick * joy)
   410 {
   411     struct hid_item hitem;
   412     struct hid_data *hdata;
   413     struct report *rep;
   414     int nbutton, naxe = -1;
   415     Sint32 v;
   416 
   417 #if defined(__FREEBSD__) || SDL_JOYSTICK_USBHID_MACHINE_JOYSTICK_H || defined(__FreeBSD_kernel__)
   418     struct joystick gameport;
   419     static int x, y, xmin = 0xffff, ymin = 0xffff, xmax = 0, ymax = 0;
   420 
   421     if (joy->hwdata->type == BSDJOY_JOY) {
   422         if (read(joy->hwdata->fd, &gameport, sizeof gameport) !=
   423             sizeof gameport)
   424             return;
   425         if (abs(x - gameport.x) > 8) {
   426             x = gameport.x;
   427             if (x < xmin) {
   428                 xmin = x;
   429             }
   430             if (x > xmax) {
   431                 xmax = x;
   432             }
   433             if (xmin == xmax) {
   434                 xmin--;
   435                 xmax++;
   436             }
   437             v = (Sint32) x;
   438             v -= (xmax + xmin + 1) / 2;
   439             v *= 32768 / ((xmax - xmin + 1) / 2);
   440             SDL_PrivateJoystickAxis(joy, 0, v);
   441         }
   442         if (abs(y - gameport.y) > 8) {
   443             y = gameport.y;
   444             if (y < ymin) {
   445                 ymin = y;
   446             }
   447             if (y > ymax) {
   448                 ymax = y;
   449             }
   450             if (ymin == ymax) {
   451                 ymin--;
   452                 ymax++;
   453             }
   454             v = (Sint32) y;
   455             v -= (ymax + ymin + 1) / 2;
   456             v *= 32768 / ((ymax - ymin + 1) / 2);
   457             SDL_PrivateJoystickAxis(joy, 1, v);
   458         }
   459         if (gameport.b1 != joy->buttons[0]) {
   460             SDL_PrivateJoystickButton(joy, 0, gameport.b1);
   461         }
   462         if (gameport.b2 != joy->buttons[1]) {
   463             SDL_PrivateJoystickButton(joy, 1, gameport.b2);
   464         }
   465         return;
   466     }
   467 #endif /* defined(__FREEBSD__) || SDL_JOYSTICK_USBHID_MACHINE_JOYSTICK_H */
   468 
   469     rep = &joy->hwdata->inreport;
   470 
   471     if (read(joy->hwdata->fd, REP_BUF_DATA(rep), rep->size) != rep->size) {
   472         return;
   473     }
   474 #if defined(USBHID_NEW) || (defined(__FREEBSD__) && __FreeBSD_kernel_version >= 500111) || defined(__FreeBSD_kernel__)
   475     hdata = hid_start_parse(joy->hwdata->repdesc, 1 << hid_input, rep->rid);
   476 #else
   477     hdata = hid_start_parse(joy->hwdata->repdesc, 1 << hid_input);
   478 #endif
   479     if (hdata == NULL) {
   480         fprintf(stderr, "%s: Cannot start HID parser\n", joy->hwdata->path);
   481         return;
   482     }
   483 
   484     for (nbutton = 0; hid_get_item(hdata, &hitem) > 0;) {
   485         switch (hitem.kind) {
   486         case hid_input:
   487             switch (HID_PAGE(hitem.usage)) {
   488             case HUP_GENERIC_DESKTOP:
   489                 {
   490                     unsigned usage = HID_USAGE(hitem.usage);
   491                     int joyaxe = usage_to_joyaxe(usage);
   492                     if (joyaxe >= 0) {
   493                         naxe = joy->hwdata->axis_map[joyaxe];
   494                         /* scaleaxe */
   495                         v = (Sint32) hid_get_data(REP_BUF_DATA(rep), &hitem);
   496                         v -= (hitem.logical_maximum +
   497                               hitem.logical_minimum + 1) / 2;
   498                         v *= 32768 /
   499                             ((hitem.logical_maximum -
   500                               hitem.logical_minimum + 1) / 2);
   501                         if (v != joy->axes[naxe]) {
   502                             SDL_PrivateJoystickAxis(joy, naxe, v);
   503                         }
   504                     } else if (usage == HUG_HAT_SWITCH) {
   505                         v = (Sint32) hid_get_data(REP_BUF_DATA(rep), &hitem);
   506                         SDL_PrivateJoystickHat(joy, 0,
   507                                                hatval_to_sdl(v) -
   508                                                hitem.logical_minimum);
   509                     }
   510                     break;
   511                 }
   512             case HUP_BUTTON:
   513                 v = (Sint32) hid_get_data(REP_BUF_DATA(rep), &hitem);
   514                 if (joy->buttons[nbutton] != v) {
   515                     SDL_PrivateJoystickButton(joy, nbutton, v);
   516                 }
   517                 nbutton++;
   518                 break;
   519             default:
   520                 continue;
   521             }
   522             break;
   523         default:
   524             break;
   525         }
   526     }
   527     hid_end_parse(hdata);
   528 
   529     return;
   530 }
   531 
   532 /* Function to close a joystick after use */
   533 void
   534 SDL_SYS_JoystickClose(SDL_Joystick * joy)
   535 {
   536     if (SDL_strncmp(joy->hwdata->path, "/dev/joy", 8)) {
   537         report_free(&joy->hwdata->inreport);
   538         hid_dispose_report_desc(joy->hwdata->repdesc);
   539     }
   540     close(joy->hwdata->fd);
   541     SDL_free(joy->hwdata->path);
   542     SDL_free(joy->hwdata);
   543 
   544     return;
   545 }
   546 
   547 void
   548 SDL_SYS_JoystickQuit(void)
   549 {
   550     int i;
   551 
   552     for (i = 0; i < MAX_JOYS; i++) {
   553         if (joynames[i] != NULL)
   554             SDL_free(joynames[i]);
   555         if (joydevnames[i] != NULL)
   556             SDL_free(joydevnames[i]);
   557     }
   558 
   559     return;
   560 }
   561 
   562 /* Function to perform the mapping from device index to the instance id for this index */
   563 SDL_JoystickID SDL_SYS_GetInstanceIdOfDeviceIndex(int index)
   564 {
   565     return index;
   566 }
   567 
   568 /* Function to determine is this joystick is attached to the system right now */
   569 int SDL_SYS_JoystickAttached(SDL_Joystick *joystick)
   570 {
   571     return 1;
   572 }
   573 
   574 int SDL_SYS_NumJoysticks()
   575 {
   576     return SDL_SYS_numjoysticks;
   577 }
   578 
   579 int SDL_SYS_JoystickNeedsPolling()
   580 {
   581     return 0;
   582 }
   583 
   584 void SDL_SYS_JoystickDetect()
   585 {
   586 }
   587 
   588 JoystickGUID SDL_SYS_PrivateJoystickGetDeviceGUID( int device_index )
   589 {
   590     JoystickGUID guid;
   591     // the GUID is just the first 16 chars of the name for now
   592     const char *name = SDL_SYS_JoystickNameForIndex( device_index );
   593     SDL_zero( guid );
   594     SDL_memcpy( &guid, name, SDL_min( sizeof(guid), SDL_strlen( name ) ) );
   595     return guid;
   596 }
   597 
   598 JoystickGUID SDL_SYS_PrivateJoystickGetGUID(SDL_Joystick * joystick)
   599 {
   600     JoystickGUID guid;
   601     // the GUID is just the first 16 chars of the name for now
   602     const char *name = joystick->name;
   603     SDL_zero( guid );
   604     SDL_memcpy( &guid, name, SDL_min( sizeof(guid), SDL_strlen( name ) ) );
   605     return guid;
   606 }
   607 
   608 static int
   609 report_alloc(struct report *r, struct report_desc *rd, int repind)
   610 {
   611     int len;
   612 
   613 #ifdef __DragonFly__
   614     len = hid_report_size(rd, r->rid, repinfo[repind].kind);
   615 #elif __FREEBSD__
   616 # if (__FreeBSD_kernel_version >= 460000) || defined(__FreeBSD_kernel__)
   617 #  if (__FreeBSD_kernel_version <= 500111)
   618     len = hid_report_size(rd, r->rid, repinfo[repind].kind);
   619 #  else
   620     len = hid_report_size(rd, repinfo[repind].kind, r->rid);
   621 #  endif
   622 # else
   623     len = hid_report_size(rd, repinfo[repind].kind, &r->rid);
   624 # endif
   625 #else
   626 # ifdef USBHID_NEW
   627     len = hid_report_size(rd, repinfo[repind].kind, r->rid);
   628 # else
   629     len = hid_report_size(rd, repinfo[repind].kind, &r->rid);
   630 # endif
   631 #endif
   632 
   633     if (len < 0) {
   634         SDL_SetError("Negative HID report size");
   635         return (-1);
   636     }
   637     r->size = len;
   638 
   639     if (r->size > 0) {
   640         r->buf = SDL_malloc(sizeof(*r->buf) - sizeof(REP_BUF_DATA(r)) +
   641                             r->size);
   642         if (r->buf == NULL) {
   643             SDL_OutOfMemory();
   644             return (-1);
   645         }
   646     } else {
   647         r->buf = NULL;
   648     }
   649 
   650     r->status = SREPORT_CLEAN;
   651     return (0);
   652 }
   653 
   654 static void
   655 report_free(struct report *r)
   656 {
   657     if (r->buf != NULL) {
   658         SDL_free(r->buf);
   659     }
   660     r->status = SREPORT_UNINIT;
   661 }
   662 
   663 #endif /* SDL_JOYSTICK_USBHID */
   664 
   665 /* vi: set ts=4 sw=4 expandtab: */