src/joystick/bsd/SDL_sysjoystick.c
author Ryan C. Gordon <icculus@icculus.org>
Sun, 24 Nov 2013 23:56:17 -0500
changeset 8093 b43765095a6f
parent 7733 957d7137867c
child 8149 681eb46b8ac4
permissions -rw-r--r--
Make internal SDL sources include SDL_internal.h instead of SDL_config.h

The new header will include SDL_config.h, but allows for other global stuff.
     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_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 SDL_bool SDL_SYS_JoystickNeedsPolling()
   217 {
   218     return SDL_FALSE;
   219 }
   220 
   221 const char *
   222 SDL_SYS_JoystickNameForDeviceIndex(int device_index)
   223 {
   224     if (joydevnames[device_index] != NULL) {
   225         return (joydevnames[device_index]);
   226     }
   227     return (joynames[device_index]);
   228 }
   229 
   230 /* Function to perform the mapping from device index to the instance id for this index */
   231 SDL_JoystickID SDL_SYS_GetInstanceIdOfDeviceIndex(int device_index)
   232 {
   233     return device_index;
   234 }
   235 
   236 static int
   237 usage_to_joyaxe(unsigned usage)
   238 {
   239     int joyaxe;
   240     switch (usage) {
   241     case HUG_X:
   242         joyaxe = JOYAXE_X;
   243         break;
   244     case HUG_Y:
   245         joyaxe = JOYAXE_Y;
   246         break;
   247     case HUG_Z:
   248         joyaxe = JOYAXE_Z;
   249         break;
   250     case HUG_SLIDER:
   251         joyaxe = JOYAXE_SLIDER;
   252         break;
   253     case HUG_WHEEL:
   254         joyaxe = JOYAXE_WHEEL;
   255         break;
   256     case HUG_RX:
   257         joyaxe = JOYAXE_RX;
   258         break;
   259     case HUG_RY:
   260         joyaxe = JOYAXE_RY;
   261         break;
   262     case HUG_RZ:
   263         joyaxe = JOYAXE_RZ;
   264         break;
   265     default:
   266         joyaxe = -1;
   267     }
   268     return joyaxe;
   269 }
   270 
   271 static unsigned
   272 hatval_to_sdl(Sint32 hatval)
   273 {
   274     static const unsigned hat_dir_map[8] = {
   275         SDL_HAT_UP, SDL_HAT_RIGHTUP, SDL_HAT_RIGHT, SDL_HAT_RIGHTDOWN,
   276         SDL_HAT_DOWN, SDL_HAT_LEFTDOWN, SDL_HAT_LEFT, SDL_HAT_LEFTUP
   277     };
   278     unsigned result;
   279     if ((hatval & 7) == hatval)
   280         result = hat_dir_map[hatval];
   281     else
   282         result = SDL_HAT_CENTERED;
   283     return result;
   284 }
   285 
   286 
   287 int
   288 SDL_SYS_JoystickOpen(SDL_Joystick * joy, int device_index)
   289 {
   290     char *path = joynames[device_index];
   291     struct joystick_hwdata *hw;
   292     struct hid_item hitem;
   293     struct hid_data *hdata;
   294     struct report *rep;
   295     int fd;
   296     int i;
   297 
   298     fd = open(path, O_RDONLY);
   299     if (fd == -1) {
   300         return SDL_SetError("%s: %s", path, strerror(errno));
   301     }
   302 
   303     joy->instance_id = device_index;
   304     hw = (struct joystick_hwdata *)
   305         SDL_malloc(sizeof(struct joystick_hwdata));
   306     if (hw == NULL) {
   307         close(fd);
   308         return SDL_OutOfMemory();
   309     }
   310     joy->hwdata = hw;
   311     hw->fd = fd;
   312     hw->path = strdup(path);
   313     if (!SDL_strncmp(path, "/dev/joy", 8)) {
   314         hw->type = BSDJOY_JOY;
   315         joy->naxes = 2;
   316         joy->nbuttons = 2;
   317         joy->nhats = 0;
   318         joy->nballs = 0;
   319         joydevnames[device_index] = strdup("Gameport joystick");
   320         goto usbend;
   321     } else {
   322         hw->type = BSDJOY_UHID;
   323     }
   324 
   325     {
   326         int ax;
   327         for (ax = 0; ax < JOYAXE_count; ax++)
   328             hw->axis_map[ax] = -1;
   329     }
   330     hw->repdesc = hid_get_report_desc(fd);
   331     if (hw->repdesc == NULL) {
   332         SDL_SetError("%s: USB_GET_REPORT_DESC: %s", hw->path,
   333                      strerror(errno));
   334         goto usberr;
   335     }
   336     rep = &hw->inreport;
   337 #if defined(__FREEBSD__) && (__FreeBSD_kernel_version > 800063) || defined(__FreeBSD_kernel__)
   338     rep->rid = hid_get_report_id(fd);
   339     if (rep->rid < 0) {
   340 #else
   341     if (ioctl(fd, USB_GET_REPORT_ID, &rep->rid) < 0) {
   342 #endif
   343         rep->rid = -1;          /* XXX */
   344     }
   345     if (report_alloc(rep, hw->repdesc, REPORT_INPUT) < 0) {
   346         goto usberr;
   347     }
   348     if (rep->size <= 0) {
   349         SDL_SetError("%s: Input report descriptor has invalid length",
   350                      hw->path);
   351         goto usberr;
   352     }
   353 #if defined(USBHID_NEW) || (defined(__FREEBSD__) && __FreeBSD_kernel_version >= 500111) || defined(__FreeBSD_kernel__)
   354     hdata = hid_start_parse(hw->repdesc, 1 << hid_input, rep->rid);
   355 #else
   356     hdata = hid_start_parse(hw->repdesc, 1 << hid_input);
   357 #endif
   358     if (hdata == NULL) {
   359         SDL_SetError("%s: Cannot start HID parser", hw->path);
   360         goto usberr;
   361     }
   362     joy->naxes = 0;
   363     joy->nbuttons = 0;
   364     joy->nhats = 0;
   365     joy->nballs = 0;
   366     for (i = 0; i < JOYAXE_count; i++)
   367         hw->axis_map[i] = -1;
   368 
   369     while (hid_get_item(hdata, &hitem) > 0) {
   370         char *sp;
   371         const char *s;
   372 
   373         switch (hitem.kind) {
   374         case hid_collection:
   375             switch (HID_PAGE(hitem.usage)) {
   376             case HUP_GENERIC_DESKTOP:
   377                 switch (HID_USAGE(hitem.usage)) {
   378                 case HUG_JOYSTICK:
   379                 case HUG_GAME_PAD:
   380                     s = hid_usage_in_page(hitem.usage);
   381                     sp = SDL_malloc(SDL_strlen(s) + 5);
   382                     SDL_snprintf(sp, SDL_strlen(s) + 5, "%s (%d)",
   383                                  s, device_index);
   384                     joydevnames[device_index] = sp;
   385                 }
   386             }
   387             break;
   388         case hid_input:
   389             switch (HID_PAGE(hitem.usage)) {
   390             case HUP_GENERIC_DESKTOP:
   391                 {
   392                     unsigned usage = HID_USAGE(hitem.usage);
   393                     int joyaxe = usage_to_joyaxe(usage);
   394                     if (joyaxe >= 0) {
   395                         hw->axis_map[joyaxe] = 1;
   396                     } else if (usage == HUG_HAT_SWITCH) {
   397                         joy->nhats++;
   398                     }
   399                     break;
   400                 }
   401             case HUP_BUTTON:
   402                 joy->nbuttons++;
   403                 break;
   404             default:
   405                 break;
   406             }
   407             break;
   408         default:
   409             break;
   410         }
   411     }
   412     hid_end_parse(hdata);
   413     for (i = 0; i < JOYAXE_count; i++)
   414         if (hw->axis_map[i] > 0)
   415             hw->axis_map[i] = joy->naxes++;
   416 
   417   usbend:
   418     /* The poll blocks the event thread. */
   419     fcntl(fd, F_SETFL, O_NONBLOCK);
   420 
   421     return (0);
   422   usberr:
   423     close(hw->fd);
   424     SDL_free(hw->path);
   425     SDL_free(hw);
   426     return (-1);
   427 }
   428 
   429 /* Function to determine is this joystick is attached to the system right now */
   430 SDL_bool SDL_SYS_JoystickAttached(SDL_Joystick *joystick)
   431 {
   432     return SDL_TRUE;
   433 }
   434 
   435 void
   436 SDL_SYS_JoystickUpdate(SDL_Joystick * joy)
   437 {
   438     struct hid_item hitem;
   439     struct hid_data *hdata;
   440     struct report *rep;
   441     int nbutton, naxe = -1;
   442     Sint32 v;
   443 
   444 #if defined(__FREEBSD__) || SDL_JOYSTICK_USBHID_MACHINE_JOYSTICK_H || defined(__FreeBSD_kernel__)
   445     struct joystick gameport;
   446     static int x, y, xmin = 0xffff, ymin = 0xffff, xmax = 0, ymax = 0;
   447 
   448     if (joy->hwdata->type == BSDJOY_JOY) {
   449         while (read(joy->hwdata->fd, &gameport, sizeof gameport) == sizeof gameport) {
   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         }
   491         return;
   492     }
   493 #endif /* defined(__FREEBSD__) || SDL_JOYSTICK_USBHID_MACHINE_JOYSTICK_H */
   494 
   495     rep = &joy->hwdata->inreport;
   496 
   497     while (read(joy->hwdata->fd, REP_BUF_DATA(rep), rep->size) == rep->size) {
   498 #if defined(USBHID_NEW) || (defined(__FREEBSD__) && __FreeBSD_kernel_version >= 500111) || defined(__FreeBSD_kernel__)
   499         hdata = hid_start_parse(joy->hwdata->repdesc, 1 << hid_input, rep->rid);
   500 #else
   501         hdata = hid_start_parse(joy->hwdata->repdesc, 1 << hid_input);
   502 #endif
   503         if (hdata == NULL) {
   504             /*fprintf(stderr, "%s: Cannot start HID parser\n", joy->hwdata->path);*/
   505             continue;
   506         }
   507 
   508         for (nbutton = 0; hid_get_item(hdata, &hitem) > 0;) {
   509             switch (hitem.kind) {
   510             case hid_input:
   511                 switch (HID_PAGE(hitem.usage)) {
   512                 case HUP_GENERIC_DESKTOP:
   513                     {
   514                         unsigned usage = HID_USAGE(hitem.usage);
   515                         int joyaxe = usage_to_joyaxe(usage);
   516                         if (joyaxe >= 0) {
   517                             naxe = joy->hwdata->axis_map[joyaxe];
   518                             /* scaleaxe */
   519                             v = (Sint32) hid_get_data(REP_BUF_DATA(rep), &hitem);
   520                             v -= (hitem.logical_maximum +
   521                                   hitem.logical_minimum + 1) / 2;
   522                             v *= 32768 /
   523                                 ((hitem.logical_maximum -
   524                                   hitem.logical_minimum + 1) / 2);
   525                             if (v != joy->axes[naxe]) {
   526                                 SDL_PrivateJoystickAxis(joy, naxe, v);
   527                             }
   528                         } else if (usage == HUG_HAT_SWITCH) {
   529                             v = (Sint32) hid_get_data(REP_BUF_DATA(rep), &hitem);
   530                             SDL_PrivateJoystickHat(joy, 0,
   531                                                    hatval_to_sdl(v) -
   532                                                    hitem.logical_minimum);
   533                         }
   534                         break;
   535                     }
   536                 case HUP_BUTTON:
   537                     v = (Sint32) hid_get_data(REP_BUF_DATA(rep), &hitem);
   538                     if (joy->buttons[nbutton] != v) {
   539                         SDL_PrivateJoystickButton(joy, nbutton, v);
   540                     }
   541                     nbutton++;
   542                     break;
   543                 default:
   544                     continue;
   545                 }
   546                 break;
   547             default:
   548                 break;
   549             }
   550         }
   551         hid_end_parse(hdata);
   552     }
   553 }
   554 
   555 /* Function to close a joystick after use */
   556 void
   557 SDL_SYS_JoystickClose(SDL_Joystick * joy)
   558 {
   559     if (SDL_strncmp(joy->hwdata->path, "/dev/joy", 8)) {
   560         report_free(&joy->hwdata->inreport);
   561         hid_dispose_report_desc(joy->hwdata->repdesc);
   562     }
   563     close(joy->hwdata->fd);
   564     SDL_free(joy->hwdata->path);
   565     SDL_free(joy->hwdata);
   566 
   567     return;
   568 }
   569 
   570 void
   571 SDL_SYS_JoystickQuit(void)
   572 {
   573     int i;
   574 
   575     for (i = 0; i < MAX_JOYS; i++) {
   576         SDL_free(joynames[i]);
   577         SDL_free(joydevnames[i]);
   578     }
   579 
   580     return;
   581 }
   582 
   583 SDL_JoystickGUID SDL_SYS_JoystickGetDeviceGUID( int device_index )
   584 {
   585     SDL_JoystickGUID guid;
   586     /* the GUID is just the first 16 chars of the name for now */
   587     const char *name = SDL_SYS_JoystickNameForDeviceIndex( device_index );
   588     SDL_zero( guid );
   589     SDL_memcpy( &guid, name, SDL_min( sizeof(guid), SDL_strlen( name ) ) );
   590     return guid;
   591 }
   592 
   593 SDL_JoystickGUID SDL_SYS_JoystickGetGUID(SDL_Joystick * joystick)
   594 {
   595     SDL_JoystickGUID guid;
   596     /* the GUID is just the first 16 chars of the name for now */
   597     const char *name = joystick->name;
   598     SDL_zero( guid );
   599     SDL_memcpy( &guid, name, SDL_min( sizeof(guid), SDL_strlen( name ) ) );
   600     return guid;
   601 }
   602 
   603 static int
   604 report_alloc(struct report *r, struct report_desc *rd, int repind)
   605 {
   606     int len;
   607 
   608 #ifdef __DragonFly__
   609     len = hid_report_size(rd, r->rid, repinfo[repind].kind);
   610 #elif __FREEBSD__
   611 # if (__FreeBSD_kernel_version >= 460000) || defined(__FreeBSD_kernel__)
   612 #  if (__FreeBSD_kernel_version <= 500111)
   613     len = hid_report_size(rd, r->rid, repinfo[repind].kind);
   614 #  else
   615     len = hid_report_size(rd, repinfo[repind].kind, r->rid);
   616 #  endif
   617 # else
   618     len = hid_report_size(rd, repinfo[repind].kind, &r->rid);
   619 # endif
   620 #else
   621 # ifdef USBHID_NEW
   622     len = hid_report_size(rd, repinfo[repind].kind, r->rid);
   623 # else
   624     len = hid_report_size(rd, repinfo[repind].kind, &r->rid);
   625 # endif
   626 #endif
   627 
   628     if (len < 0) {
   629         return SDL_SetError("Negative HID report size");
   630     }
   631     r->size = len;
   632 
   633     if (r->size > 0) {
   634 #if defined(__FREEBSD__) && (__FreeBSD_kernel_version > 900000)
   635         r->buf = SDL_malloc(r->size);
   636 #else
   637         r->buf = SDL_malloc(sizeof(*r->buf) - sizeof(REP_BUF_DATA(r)) +
   638                             r->size);
   639 #endif
   640         if (r->buf == NULL) {
   641             return SDL_OutOfMemory();
   642         }
   643     } else {
   644         r->buf = NULL;
   645     }
   646 
   647     r->status = SREPORT_CLEAN;
   648     return 0;
   649 }
   650 
   651 static void
   652 report_free(struct report *r)
   653 {
   654     SDL_free(r->buf);
   655     r->status = SREPORT_UNINIT;
   656 }
   657 
   658 #endif /* SDL_JOYSTICK_USBHID */
   659 
   660 /* vi: set ts=4 sw=4 expandtab: */