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 (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 #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 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 * 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 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 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 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 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 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 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 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 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