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