• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2   Simple DirectMedia Layer
3   Copyright (C) 1997-2016 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   64
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
SDL_SYS_JoystickInit(void)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] = SDL_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++] = SDL_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 
SDL_SYS_NumJoysticks()207 int SDL_SYS_NumJoysticks()
208 {
209     return SDL_SYS_numjoysticks;
210 }
211 
SDL_SYS_JoystickDetect()212 void SDL_SYS_JoystickDetect()
213 {
214 }
215 
216 const char *
SDL_SYS_JoystickNameForDeviceIndex(int device_index)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 */
SDL_SYS_GetInstanceIdOfDeviceIndex(int device_index)226 SDL_JoystickID SDL_SYS_GetInstanceIdOfDeviceIndex(int device_index)
227 {
228     return device_index;
229 }
230 
231 static int
usage_to_joyaxe(unsigned usage)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
hatval_to_sdl(Sint32 hatval)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
SDL_SYS_JoystickOpen(SDL_Joystick * joy,int device_index)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 = NULL;
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 = SDL_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] = SDL_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 defined(__NetBSD__)
341     usb_device_descriptor_t udd;
342     struct usb_string_desc usd;
343     if (ioctl(fd, USB_GET_DEVICE_DESC, &udd) == -1)
344         goto desc_failed;
345 
346     /* Get default language */
347     usd.usd_string_index = USB_LANGUAGE_TABLE;
348     usd.usd_language_id = 0;
349     if (ioctl(fd, USB_GET_STRING_DESC, &usd) == -1 || usd.usd_desc.bLength < 4) {
350         usd.usd_language_id = 0;
351     } else {
352         usd.usd_language_id = UGETW(usd.usd_desc.bString[0]);
353     }
354 
355     usd.usd_string_index = udd.iProduct;
356     if (ioctl(fd, USB_GET_STRING_DESC, &usd) == 0) {
357         char str[128];
358         char *new_name = NULL;
359         int i;
360         for (i = 0; i < (usd.usd_desc.bLength >> 1) - 1 && i < sizeof(str) - 1; i++) {
361             str[i] = UGETW(usd.usd_desc.bString[i]);
362         }
363         str[i] = '\0';
364         asprintf(&new_name, "%s @ %s", str, path);
365         if (new_name != NULL) {
366             SDL_free(joydevnames[SDL_SYS_numjoysticks]);
367             joydevnames[SDL_SYS_numjoysticks] = new_name;
368         }
369     }
370 desc_failed:
371 #endif
372     if (report_alloc(rep, hw->repdesc, REPORT_INPUT) < 0) {
373         goto usberr;
374     }
375     if (rep->size <= 0) {
376         SDL_SetError("%s: Input report descriptor has invalid length",
377                      hw->path);
378         goto usberr;
379     }
380 #if defined(USBHID_NEW) || (defined(__FREEBSD__) && __FreeBSD_kernel_version >= 500111) || defined(__FreeBSD_kernel__)
381     hdata = hid_start_parse(hw->repdesc, 1 << hid_input, rep->rid);
382 #else
383     hdata = hid_start_parse(hw->repdesc, 1 << hid_input);
384 #endif
385     if (hdata == NULL) {
386         SDL_SetError("%s: Cannot start HID parser", hw->path);
387         goto usberr;
388     }
389     joy->naxes = 0;
390     joy->nbuttons = 0;
391     joy->nhats = 0;
392     joy->nballs = 0;
393     for (i = 0; i < JOYAXE_count; i++)
394         hw->axis_map[i] = -1;
395 
396     while (hid_get_item(hdata, &hitem) > 0) {
397         char *sp;
398         const char *s;
399 
400         switch (hitem.kind) {
401         case hid_collection:
402             switch (HID_PAGE(hitem.usage)) {
403             case HUP_GENERIC_DESKTOP:
404                 switch (HID_USAGE(hitem.usage)) {
405                 case HUG_JOYSTICK:
406                 case HUG_GAME_PAD:
407                     s = hid_usage_in_page(hitem.usage);
408                     sp = SDL_malloc(SDL_strlen(s) + 5);
409                     SDL_snprintf(sp, SDL_strlen(s) + 5, "%s (%d)",
410                                  s, device_index);
411                     joydevnames[device_index] = sp;
412                 }
413             }
414             break;
415         case hid_input:
416             switch (HID_PAGE(hitem.usage)) {
417             case HUP_GENERIC_DESKTOP:
418                 {
419                     unsigned usage = HID_USAGE(hitem.usage);
420                     int joyaxe = usage_to_joyaxe(usage);
421                     if (joyaxe >= 0) {
422                         hw->axis_map[joyaxe] = 1;
423                     } else if (usage == HUG_HAT_SWITCH) {
424                         joy->nhats++;
425                     }
426                     break;
427                 }
428             case HUP_BUTTON:
429                 joy->nbuttons++;
430                 break;
431             default:
432                 break;
433             }
434             break;
435         default:
436             break;
437         }
438     }
439     hid_end_parse(hdata);
440     for (i = 0; i < JOYAXE_count; i++)
441         if (hw->axis_map[i] > 0)
442             hw->axis_map[i] = joy->naxes++;
443 
444     if (joy->naxes == 0 && joy->nbuttons == 0 && joy->nhats == 0 && joy->nballs == 0) {
445         SDL_SetError("%s: Not a joystick, ignoring", hw->path);
446         goto usberr;
447     }
448 
449   usbend:
450     /* The poll blocks the event thread. */
451     fcntl(fd, F_SETFL, O_NONBLOCK);
452 #ifdef __NetBSD__
453     /* Flush pending events */
454     if (rep) {
455         while (read(joy->hwdata->fd, REP_BUF_DATA(rep), rep->size) == rep->size)
456             ;
457     }
458 #endif
459 
460     return (0);
461   usberr:
462     close(hw->fd);
463     SDL_free(hw->path);
464     SDL_free(hw);
465     return (-1);
466 }
467 
468 /* Function to determine if this joystick is attached to the system right now */
469 SDL_bool SDL_SYS_JoystickAttached(SDL_Joystick *joystick)
470 {
471     return SDL_TRUE;
472 }
473 
474 void
475 SDL_SYS_JoystickUpdate(SDL_Joystick * joy)
476 {
477     struct hid_item hitem;
478     struct hid_data *hdata;
479     struct report *rep;
480     int nbutton, naxe = -1;
481     Sint32 v;
482 
483 #if defined(__FREEBSD__) || SDL_JOYSTICK_USBHID_MACHINE_JOYSTICK_H || defined(__FreeBSD_kernel__)
484     struct joystick gameport;
485     static int x, y, xmin = 0xffff, ymin = 0xffff, xmax = 0, ymax = 0;
486 
487     if (joy->hwdata->type == BSDJOY_JOY) {
488         while (read(joy->hwdata->fd, &gameport, sizeof gameport) == sizeof gameport) {
489             if (abs(x - gameport.x) > 8) {
490                 x = gameport.x;
491                 if (x < xmin) {
492                     xmin = x;
493                 }
494                 if (x > xmax) {
495                     xmax = x;
496                 }
497                 if (xmin == xmax) {
498                     xmin--;
499                     xmax++;
500                 }
501                 v = (Sint32) x;
502                 v -= (xmax + xmin + 1) / 2;
503                 v *= 32768 / ((xmax - xmin + 1) / 2);
504                 SDL_PrivateJoystickAxis(joy, 0, v);
505             }
506             if (abs(y - gameport.y) > 8) {
507                 y = gameport.y;
508                 if (y < ymin) {
509                     ymin = y;
510                 }
511                 if (y > ymax) {
512                     ymax = y;
513                 }
514                 if (ymin == ymax) {
515                     ymin--;
516                     ymax++;
517                 }
518                 v = (Sint32) y;
519                 v -= (ymax + ymin + 1) / 2;
520                 v *= 32768 / ((ymax - ymin + 1) / 2);
521                 SDL_PrivateJoystickAxis(joy, 1, v);
522             }
523             if (gameport.b1 != joy->buttons[0]) {
524                 SDL_PrivateJoystickButton(joy, 0, gameport.b1);
525             }
526             if (gameport.b2 != joy->buttons[1]) {
527                 SDL_PrivateJoystickButton(joy, 1, gameport.b2);
528             }
529         }
530         return;
531     }
532 #endif /* defined(__FREEBSD__) || SDL_JOYSTICK_USBHID_MACHINE_JOYSTICK_H */
533 
534     rep = &joy->hwdata->inreport;
535 
536     while (read(joy->hwdata->fd, REP_BUF_DATA(rep), rep->size) == rep->size) {
537 #if defined(USBHID_NEW) || (defined(__FREEBSD__) && __FreeBSD_kernel_version >= 500111) || defined(__FreeBSD_kernel__)
538         hdata = hid_start_parse(joy->hwdata->repdesc, 1 << hid_input, rep->rid);
539 #else
540         hdata = hid_start_parse(joy->hwdata->repdesc, 1 << hid_input);
541 #endif
542         if (hdata == NULL) {
543             /*fprintf(stderr, "%s: Cannot start HID parser\n", joy->hwdata->path);*/
544             continue;
545         }
546 
547         for (nbutton = 0; hid_get_item(hdata, &hitem) > 0;) {
548             switch (hitem.kind) {
549             case hid_input:
550                 switch (HID_PAGE(hitem.usage)) {
551                 case HUP_GENERIC_DESKTOP:
552                     {
553                         unsigned usage = HID_USAGE(hitem.usage);
554                         int joyaxe = usage_to_joyaxe(usage);
555                         if (joyaxe >= 0) {
556                             naxe = joy->hwdata->axis_map[joyaxe];
557                             /* scaleaxe */
558                             v = (Sint32) hid_get_data(REP_BUF_DATA(rep), &hitem);
559                             v -= (hitem.logical_maximum +
560                                   hitem.logical_minimum + 1) / 2;
561                             v *= 32768 /
562                                 ((hitem.logical_maximum -
563                                   hitem.logical_minimum + 1) / 2);
564                             if (v != joy->axes[naxe]) {
565                                 SDL_PrivateJoystickAxis(joy, naxe, v);
566                             }
567                         } else if (usage == HUG_HAT_SWITCH) {
568                             v = (Sint32) hid_get_data(REP_BUF_DATA(rep), &hitem);
569                             SDL_PrivateJoystickHat(joy, 0,
570                                                    hatval_to_sdl(v) -
571                                                    hitem.logical_minimum);
572                         }
573                         break;
574                     }
575                 case HUP_BUTTON:
576                     v = (Sint32) hid_get_data(REP_BUF_DATA(rep), &hitem);
577                     if (joy->buttons[nbutton] != v) {
578                         SDL_PrivateJoystickButton(joy, nbutton, v);
579                     }
580                     nbutton++;
581                     break;
582                 default:
583                     continue;
584                 }
585                 break;
586             default:
587                 break;
588             }
589         }
590         hid_end_parse(hdata);
591     }
592 }
593 
594 /* Function to close a joystick after use */
595 void
596 SDL_SYS_JoystickClose(SDL_Joystick * joy)
597 {
598     if (SDL_strncmp(joy->hwdata->path, "/dev/joy", 8)) {
599         report_free(&joy->hwdata->inreport);
600         hid_dispose_report_desc(joy->hwdata->repdesc);
601     }
602     close(joy->hwdata->fd);
603     SDL_free(joy->hwdata->path);
604     SDL_free(joy->hwdata);
605 }
606 
607 void
608 SDL_SYS_JoystickQuit(void)
609 {
610     int i;
611 
612     for (i = 0; i < MAX_JOYS; i++) {
613         SDL_free(joynames[i]);
614         SDL_free(joydevnames[i]);
615     }
616 
617     return;
618 }
619 
620 SDL_JoystickGUID SDL_SYS_JoystickGetDeviceGUID( int device_index )
621 {
622     SDL_JoystickGUID guid;
623     /* the GUID is just the first 16 chars of the name for now */
624     const char *name = SDL_SYS_JoystickNameForDeviceIndex( device_index );
625     SDL_zero( guid );
626     SDL_memcpy( &guid, name, SDL_min( sizeof(guid), SDL_strlen( name ) ) );
627     return guid;
628 }
629 
630 SDL_JoystickGUID SDL_SYS_JoystickGetGUID(SDL_Joystick * joystick)
631 {
632     SDL_JoystickGUID guid;
633     /* the GUID is just the first 16 chars of the name for now */
634     const char *name = joystick->name;
635     SDL_zero( guid );
636     SDL_memcpy( &guid, name, SDL_min( sizeof(guid), SDL_strlen( name ) ) );
637     return guid;
638 }
639 
640 static int
641 report_alloc(struct report *r, struct report_desc *rd, int repind)
642 {
643     int len;
644 
645 #ifdef __DragonFly__
646     len = hid_report_size(rd, r->rid, repinfo[repind].kind);
647 #elif __FREEBSD__
648 # if (__FreeBSD_kernel_version >= 460000) || defined(__FreeBSD_kernel__)
649 #  if (__FreeBSD_kernel_version <= 500111)
650     len = hid_report_size(rd, r->rid, repinfo[repind].kind);
651 #  else
652     len = hid_report_size(rd, repinfo[repind].kind, r->rid);
653 #  endif
654 # else
655     len = hid_report_size(rd, repinfo[repind].kind, &r->rid);
656 # endif
657 #else
658 # ifdef USBHID_NEW
659     len = hid_report_size(rd, repinfo[repind].kind, r->rid);
660 # else
661     len = hid_report_size(rd, repinfo[repind].kind, &r->rid);
662 # endif
663 #endif
664 
665     if (len < 0) {
666         return SDL_SetError("Negative HID report size");
667     }
668     r->size = len;
669 
670     if (r->size > 0) {
671 #if defined(__FREEBSD__) && (__FreeBSD_kernel_version > 900000)
672         r->buf = SDL_malloc(r->size);
673 #else
674         r->buf = SDL_malloc(sizeof(*r->buf) - sizeof(REP_BUF_DATA(r)) +
675                             r->size);
676 #endif
677         if (r->buf == NULL) {
678             return SDL_OutOfMemory();
679         }
680     } else {
681         r->buf = NULL;
682     }
683 
684     r->status = SREPORT_CLEAN;
685     return 0;
686 }
687 
688 static void
689 report_free(struct report *r)
690 {
691     SDL_free(r->buf);
692     r->status = SREPORT_UNINIT;
693 }
694 
695 #endif /* SDL_JOYSTICK_USBHID */
696 
697 /* vi: set ts=4 sw=4 expandtab: */
698