src/joystick/bsd/SDL_sysjoystick.c
author Sam Lantinga <slouken@libsdl.org>
Fri, 24 Aug 2012 10:04:07 -0700
branchSDL-1.2
changeset 6402 e786a4241cb5
parent 6289 62ff1c0a103f
permissions -rw-r--r--
Fixed bug 1561 - BSD joystick: Increase the number of uhid devices to scan

Brad Smith 2012-08-01 20:10:19 PDT

The attached patch from the OpenBSD ports tree is to increase the number of
uhid devices to scan for joysticks. It's somewhat easy to exhaust the default
number of devices which are scanned.
     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	16
    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)
   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 */