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