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 77struct 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 88static 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 98enum { 99 REPORT_INPUT = 0, 100 REPORT_OUTPUT = 1, 101 REPORT_FEATURE = 2 102}; 103 104enum { 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 116struct 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 134static char *joynames[MAX_JOYS]; 135static char *joydevnames[MAX_JOYS]; 136 137static int report_alloc(struct report *, struct report_desc *, int); 138static 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 146int 147SDL_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 188const char * 189SDL_SYS_JoystickName(int index) 190{ 191 if (joydevnames[index] != NULL) { 192 return (joydevnames[index]); 193 } 194 return (joynames[index]); 195} 196 197static int 198usage_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 224static unsigned 225hatval_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 240int 241SDL_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 372usbend: 373 /* The poll blocks the event thread. */ 374 fcntl(fd, F_SETFL, O_NONBLOCK); 375 376 return (0); 377usberr: 378 close(hw->fd); 379 SDL_free(hw->path); 380 SDL_free(hw); 381 return (-1); 382} 383 384void 385SDL_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 */ 507void 508SDL_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 521void 522SDL_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 536static int 537report_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 582static void 583report_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