Home | History | Annotate | Download | only in bsd
      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 (at) 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
    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 *
    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
    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
    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
    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