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