src/joystick/bsd/SDL_sysjoystick.c
author Sam Lantinga <slouken@libsdl.org>
Sun, 28 May 2006 13:04:16 +0000
branchSDL-1.3
changeset 1662 782fd950bd46
parent 1635 92947e3a18db
child 1668 4da1ee79c9af
permissions -rw-r--r--
Revamp of the video system in progress - adding support for multiple displays, multiple windows, and a full video mode selection API.

WARNING: None of the video drivers have been updated for the new API yet! The API is still under design and very fluid.

The code is now run through a consistent indent format:
indent -i4 -nut -nsc -br -ce

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