1 /*
2     SDL - Simple DirectMedia Layer
3     Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002  Sam Lantinga
4 
5     This library is free software; you can redistribute it and/or
6     modify it under the terms of the GNU Library General Public
7     License as published by the Free Software Foundation; either
8     version 2 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     Library General Public License for more details.
14 
15     You should have received a copy of the GNU Library General Public
16     License along with this library; if not, write to the Free
17     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18 
19     Sam Lantinga
20     slouken@devolution.com
21 */
22 
23 /*
24  * Joystick driver for the uhid(4) interface found in OpenBSD,
25  * NetBSD and FreeBSD.
26  *
27  * Maintainer: <vedge at csoft.org>
28  */
29 
30 #ifdef SAVE_RCSID
31 static char rcsid =
32  "@(#) $Id: SDL_sysjoystick.c,v 1.13 2002/10/05 05:32:49 slouken Exp $";
33 #endif
34 
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <unistd.h>
38 #include <fcntl.h>
39 #include <string.h>
40 #include <errno.h>
41 
42 #include "mysnprintf.h"
43 
44 #include <dev/usb/usb.h>
45 #include <dev/usb/usbhid.h>
46 
47 #if defined(HAVE_USBHID_H)
48 #include <usbhid.h>
49 #elif defined(HAVE_LIBUSB_H)
50 #include <libusb.h>
51 #elif defined(HAVE_LIBUSBHID_H)
52 #include <libusbhid.h>
53 #elif defined(HAVE_USB_H)
54 #include <usb.h>
55 #endif
56 
57 #ifdef __FREEBSD__
58 #ifndef __DragonFly__
59 #include <osreldate.h>
60 #endif
61 #include <sys/joystick.h>
62 #endif
63 
64 #include "SDL_error.h"
65 #include "SDL_joystick.h"
66 #include "SDL_sysjoystick.h"
67 #include "SDL_joystick_c.h"
68 
69 #define MAX_UHID_JOYS 4
70 #define MAX_JOY_JOYS 2
71 #define MAX_JOYS (MAX_UHID_JOYS + MAX_JOY_JOYS)
72 
73 #if defined(__FREEBSD__) && (__FreeBSD_version > 800063)
74 struct usb_ctl_report {
75 	int ucr_report;
76 	u_char ucr_data[1024]; /* filled data size will vary */
77     };
78 #endif
79 
80 struct report {
81     struct usb_ctl_report *buf;  /* Buffer */
82     size_t size;  /* Buffer size */
83     int rid;  /* Report ID */
84     enum {
85         SREPORT_UNINIT,
86         SREPORT_CLEAN,
87         SREPORT_DIRTY
88     } status;
89 };
90 
91 static struct {
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     REPORT_INPUT = 0,
103     REPORT_OUTPUT = 1,
104     REPORT_FEATURE = 2
105 };
106 
107 enum {
108     JOYAXE_X,
109     JOYAXE_Y,
110     JOYAXE_Z,
111     JOYAXE_SLIDER,
112     JOYAXE_WHEEL
113 };
114 
115 struct joystick_hwdata {
116     int fd;
117     char *path;
118     enum {
119         BSDJOY_UHID,  /* uhid(4) */
120         BSDJOY_JOY  /* joy(4) */
121     } type;
122     struct report_desc *repdesc;
123     struct report inreport;
124 #if 0
125     int axismin[];
126     int axismax[];
127 #endif
128 };
129 
130 static char *joynames[MAX_JOYS];
131 static char *joydevnames[MAX_JOYS];
132 
133 static int report_alloc(struct report *, struct report_desc *, int);
134 static void report_free(struct report *);
135 
136 #if defined(USBHID_UCR_DATA) || (defined(__FREEBSD__) && (__FreeBSD_version > 800063))
137 #define REP_BUF_DATA(rep) ((rep)->buf->ucr_data)
138 #else
139 #define REP_BUF_DATA(rep) ((rep)->buf->data)
140 #endif
141 
142 int
SDL_SYS_JoystickInit(void)143 SDL_SYS_JoystickInit(void)
144 {
145     char s[16];
146     int i, fd;
147 
148     int SDL_numjoysticks = 0;
149 
150     memset(joynames, NULL, sizeof(joynames));
151     memset(joydevnames, NULL, sizeof(joydevnames));
152 
153     for (i = 0; i < MAX_UHID_JOYS; i++) {
154             mysnprintf(s, 16, "/dev/uhid%d", i);
155         fd = open(s, O_RDWR);
156         if (fd > 0) {
157             joynames[SDL_numjoysticks++] = strdup(s);
158             close(fd);
159         }
160     }
161     for (i = 0; i < MAX_JOY_JOYS; i++) {
162         mysnprintf(s, 16, "/dev/joy%d", i);
163         fd = open(s, O_RDWR);
164         if (fd > 0) {
165             joynames[SDL_numjoysticks++] = strdup(s);
166             close(fd);
167         }
168     }
169 
170     /* Read the default USB HID usage table. */
171     hid_init(NULL);
172 
173     return (SDL_numjoysticks);
174 }
175 
176 const char *
SDL_SYS_JoystickName(int index)177 SDL_SYS_JoystickName(int index)
178 {
179     if (joydevnames[index] != NULL) {
180         return (joydevnames[index]);
181     }
182     return (joynames[index]);
183 }
184 
185 int
SDL_SYS_JoystickOpen(SDL_Joystick * joy)186 SDL_SYS_JoystickOpen(SDL_Joystick *joy)
187 {
188     char *path = joynames[joy->index];
189     struct joystick_hwdata *hw;
190     struct hid_item hitem;
191     struct hid_data *hdata;
192     struct report *rep;
193     int fd;
194 
195     fd = open(path, O_RDWR);
196     if (fd < 0) {
197         SDL_SetError("%s: %s", path, strerror(errno));
198         return (-1);
199     }
200 
201     hw = (struct joystick_hwdata *)malloc(sizeof(struct joystick_hwdata));
202     if (hw == NULL) {
203         SDL_OutOfMemory();
204         close(fd);
205         return (-1);
206     }
207     joy->hwdata = hw;
208     hw->fd = fd;
209     hw->path = strdup(path);
210     hw->type = BSDJOY_UHID;
211     hw->repdesc = hid_get_report_desc(fd);
212     if (hw->repdesc == NULL) {
213         SDL_SetError("%s: USB_GET_REPORT_DESC: %s", hw->path,
214             strerror(errno));
215         goto usberr;
216     }
217 
218     rep = &hw->inreport;
219     if (report_alloc(rep, hw->repdesc, REPORT_INPUT) < 0) {
220         goto usberr;
221     }
222 
223     if (rep->size <= 0) {
224             SDL_SetError("%s: Input report descriptor has invalid length",
225             hw->path);
226         goto usberr;
227     }
228 
229 #ifdef USBHID_NEW
230     hdata = hid_start_parse(hw->repdesc, 1 << hid_input, rep->rid);
231 #else
232     hdata = hid_start_parse(hw->repdesc, 1 << hid_input);
233 #endif
234     if (hdata == NULL) {
235         SDL_SetError("%s: Cannot start HID parser", hw->path);
236         goto usberr;
237     }
238     joy->naxes = 0;
239     joy->nbuttons = 0;
240     joy->nhats = 0;
241     joy->nballs = 0;
242 
243     while (hid_get_item(hdata, &hitem) > 0) {
244         char *sp;
245         const char *s;
246 
247         switch (hitem.kind) {
248         case hid_collection:
249             switch (HID_PAGE(hitem.usage)) {
250               case HUP_GENERIC_DESKTOP:
251                 switch (HID_USAGE(hitem.usage)) {
252                   case HUG_JOYSTICK:
253                   case HUG_GAME_PAD:
254                     s = hid_usage_in_page(hitem.usage);
255                     sp = malloc(strlen(s) + 5);
256                     mysnprintf(sp, strlen(s) + 5, "%s (%d)", s, joy->index);
257                     joydevnames[joy->index] = sp;
258                 }
259         }
260         break;
261         case hid_input:
262             switch (HID_PAGE(hitem.usage)) {
263               case HUP_GENERIC_DESKTOP:
264                 switch (HID_USAGE(hitem.usage)) {
265                   case HUG_X:
266                   case HUG_Y:
267                   case HUG_Z:
268                   case HUG_SLIDER:
269                   case HUG_WHEEL:
270 #if 0
271                     hw->axismin[joy->naxes] =
272                     hitem.logical_minimum;
273                     hw->axismax[joy->naxes] =
274                     hitem.logical_maximum;
275 #endif
276                     joy->naxes++;
277                     break;
278                 }
279                 break;
280               case HUP_BUTTON:
281                 joy->nbuttons++;
282                 break;
283               default:
284                 break;
285             }
286         break;
287       default:
288         break;
289         }
290     }
291     hid_end_parse(hdata);
292 
293     /* The poll blocks the event thread. */
294     fcntl(fd, F_SETFL, O_NONBLOCK);
295 
296     return (0);
297 usberr:
298     close(hw->fd);
299     free(hw->path);
300     free(hw);
301     return (-1);
302 }
303 
304 void
SDL_SYS_JoystickUpdate(SDL_Joystick * joy)305 SDL_SYS_JoystickUpdate(SDL_Joystick *joy)
306 {
307     struct hid_item hitem;
308     struct hid_data *hdata;
309     struct report *rep;
310     int nbutton, naxe = -1;
311     Sint32 v;
312 
313     rep = &joy->hwdata->inreport;
314 
315     if (read(joy->hwdata->fd, REP_BUF_DATA(rep), rep->size) != rep->size) {
316         return;
317     }
318 #ifdef USBHID_NEW
319     hdata = hid_start_parse(joy->hwdata->repdesc, 1 << hid_input, rep->rid);
320 #else
321     hdata = hid_start_parse(joy->hwdata->repdesc, 1 << hid_input);
322 #endif
323     if (hdata == NULL) {
324         fprintf(stderr, "%s: Cannot start HID parser\n", joy->hwdata->path);
325         return;
326     }
327 
328     for (nbutton = 0; hid_get_item(hdata, &hitem) > 0;) {
329         switch (hitem.kind) {
330           case hid_input:
331             switch (HID_PAGE(hitem.usage)) {
332               case HUP_GENERIC_DESKTOP:
333                 switch (HID_USAGE(hitem.usage)) {
334                   case HUG_X:
335                     naxe = JOYAXE_X;
336                     goto scaleaxe;
337                   case HUG_Y:
338                     naxe = JOYAXE_Y;
339                     goto scaleaxe;
340                   case HUG_Z:
341                     naxe = JOYAXE_Z;
342                     goto scaleaxe;
343                   case HUG_SLIDER:
344                     naxe = JOYAXE_SLIDER;
345                     goto scaleaxe;
346                   case HUG_WHEEL:
347                     naxe = JOYAXE_WHEEL;
348                     goto scaleaxe;
349                   default:
350                     continue;
351                 }
352 scaleaxe:
353                 v = (Sint32)hid_get_data(REP_BUF_DATA(rep),
354                     &hitem);
355                 v -= (hitem.logical_maximum + hitem.logical_minimum + 1)/2;
356                 v *= 32768/((hitem.logical_maximum - hitem.logical_minimum + 1)/2);
357                 if (v != joy->axes[naxe]) {
358                     SDL_PrivateJoystickAxis(joy, naxe, v);
359                 }
360                 break;
361               case HUP_BUTTON:
362                 v = (Sint32)hid_get_data(REP_BUF_DATA(rep),
363                     &hitem);
364                 if (joy->buttons[nbutton] != v) {
365                     SDL_PrivateJoystickButton(joy,
366                         nbutton, v);
367                 }
368                 nbutton++;
369                 break;
370               default:
371                 continue;
372             }
373             break;
374           default:
375             break;
376         }
377     }
378     hid_end_parse(hdata);
379 
380     return;
381 }
382 
383 /* Function to close a joystick after use */
384 void
SDL_SYS_JoystickClose(SDL_Joystick * joy)385 SDL_SYS_JoystickClose(SDL_Joystick *joy)
386 {
387     report_free(&joy->hwdata->inreport);
388     hid_dispose_report_desc(joy->hwdata->repdesc);
389     close(joy->hwdata->fd);
390     free(joy->hwdata->path);
391     free(joy->hwdata);
392 
393     return;
394 }
395 
396 void
SDL_SYS_JoystickQuit(void)397 SDL_SYS_JoystickQuit(void)
398 {
399     int i;
400 
401     for (i = 0; i < MAX_JOYS; i++) {
402         if (joynames[i] != NULL)
403             free(joynames[i]);
404         if (joydevnames[i] != NULL)
405             free(joydevnames[i]);
406     }
407 
408     return;
409 }
410 
411 static int
report_alloc(struct report * r,struct report_desc * rd,int repind)412 report_alloc(struct report *r, struct report_desc *rd, int repind)
413 {
414     int len;
415 
416 #ifdef USBHID_NEW
417     len = hid_report_size(rd, repinfo[repind].kind, &r->rid);
418 #else
419     len = hid_report_size(rd, repinfo[repind].kind, r->rid);
420 #endif
421     if (len < 0) {
422         SDL_SetError("Negative HID report size");
423         return (-1);
424     }
425     r->size = len;
426 
427     if (r->size > 0) {
428         r->buf = malloc(sizeof(*r->buf) - sizeof(REP_BUF_DATA(r)) +
429             r->size);
430         if (r->buf == NULL) {
431             SDL_OutOfMemory();
432             return (-1);
433         }
434     } else {
435         r->buf = NULL;
436     }
437 
438     r->status = SREPORT_CLEAN;
439     return (0);
440 }
441 
442 static void
report_free(struct report * r)443 report_free(struct report *r)
444 {
445     if (r->buf != NULL) {
446         free(r->buf);
447     }
448     r->status = SREPORT_UNINIT;
449 }
450 
451