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