SDL_sysjoystick.c (17906B)
1/* 2 Simple DirectMedia Layer 3 Copyright (C) 1997-2014 Sam Lantinga <slouken@libsdl.org> 4 5 This software is provided 'as-is', without any express or implied 6 warranty. In no event will the authors be held liable for any damages 7 arising from the use of this software. 8 9 Permission is granted to anyone to use this software for any purpose, 10 including commercial applications, and to alter it and redistribute it 11 freely, subject to the following restrictions: 12 13 1. The origin of this software must not be misrepresented; you must not 14 claim that you wrote the original software. If you use this software 15 in a product, an acknowledgment in the product documentation would be 16 appreciated but is not required. 17 2. Altered source versions must be plainly marked as such, and must not be 18 misrepresented as being the original software. 19 3. This notice may not be removed or altered from any source distribution. 20*/ 21#include "../../SDL_internal.h" 22 23#ifdef SDL_JOYSTICK_USBHID 24 25/* 26 * Joystick driver for the uhid(4) interface found in OpenBSD, 27 * NetBSD and FreeBSD. 28 * 29 * Maintainer: <vedge at csoft.org> 30 */ 31 32#include <sys/param.h> 33 34#include <unistd.h> 35#include <fcntl.h> 36#include <errno.h> 37 38#ifndef __FreeBSD_kernel_version 39#define __FreeBSD_kernel_version __FreeBSD_version 40#endif 41 42#if defined(HAVE_USB_H) 43#include <usb.h> 44#endif 45#ifdef __DragonFly__ 46#include <bus/usb/usb.h> 47#include <bus/usb/usbhid.h> 48#else 49#include <dev/usb/usb.h> 50#include <dev/usb/usbhid.h> 51#endif 52 53#if defined(HAVE_USBHID_H) 54#include <usbhid.h> 55#elif defined(HAVE_LIBUSB_H) 56#include <libusb.h> 57#elif defined(HAVE_LIBUSBHID_H) 58#include <libusbhid.h> 59#endif 60 61#if defined(__FREEBSD__) || defined(__FreeBSD_kernel__) 62#ifndef __DragonFly__ 63#include <osreldate.h> 64#endif 65#if __FreeBSD_kernel_version > 800063 66#include <dev/usb/usb_ioctl.h> 67#endif 68#include <sys/joystick.h> 69#endif 70 71#if SDL_JOYSTICK_USBHID_MACHINE_JOYSTICK_H 72#include <machine/joystick.h> 73#endif 74 75#include "SDL_joystick.h" 76#include "../SDL_sysjoystick.h" 77#include "../SDL_joystick_c.h" 78 79#define MAX_UHID_JOYS 16 80#define MAX_JOY_JOYS 2 81#define MAX_JOYS (MAX_UHID_JOYS + MAX_JOY_JOYS) 82 83 84struct report 85{ 86#if defined(__FREEBSD__) && (__FreeBSD_kernel_version > 900000) 87 void *buf; /* Buffer */ 88#elif defined(__FREEBSD__) && (__FreeBSD_kernel_version > 800063) 89 struct usb_gen_descriptor *buf; /* Buffer */ 90#else 91 struct usb_ctl_report *buf; /* Buffer */ 92#endif 93 size_t size; /* Buffer size */ 94 int rid; /* Report ID */ 95 enum 96 { 97 SREPORT_UNINIT, 98 SREPORT_CLEAN, 99 SREPORT_DIRTY 100 } status; 101}; 102 103static struct 104{ 105 int uhid_report; 106 hid_kind_t kind; 107 const char *name; 108} const repinfo[] = { 109 {UHID_INPUT_REPORT, hid_input, "input"}, 110 {UHID_OUTPUT_REPORT, hid_output, "output"}, 111 {UHID_FEATURE_REPORT, hid_feature, "feature"} 112}; 113 114enum 115{ 116 REPORT_INPUT = 0, 117 REPORT_OUTPUT = 1, 118 REPORT_FEATURE = 2 119}; 120 121enum 122{ 123 JOYAXE_X, 124 JOYAXE_Y, 125 JOYAXE_Z, 126 JOYAXE_SLIDER, 127 JOYAXE_WHEEL, 128 JOYAXE_RX, 129 JOYAXE_RY, 130 JOYAXE_RZ, 131 JOYAXE_count 132}; 133 134struct joystick_hwdata 135{ 136 int fd; 137 char *path; 138 enum 139 { 140 BSDJOY_UHID, /* uhid(4) */ 141 BSDJOY_JOY /* joy(4) */ 142 } type; 143 struct report_desc *repdesc; 144 struct report inreport; 145 int axis_map[JOYAXE_count]; /* map present JOYAXE_* to 0,1,.. */ 146}; 147 148static char *joynames[MAX_JOYS]; 149static char *joydevnames[MAX_JOYS]; 150 151static int report_alloc(struct report *, struct report_desc *, int); 152static void report_free(struct report *); 153 154#if defined(USBHID_UCR_DATA) || (defined(__FreeBSD_kernel__) && __FreeBSD_kernel_version <= 800063) 155#define REP_BUF_DATA(rep) ((rep)->buf->ucr_data) 156#elif (defined(__FREEBSD__) && (__FreeBSD_kernel_version > 900000)) 157#define REP_BUF_DATA(rep) ((rep)->buf) 158#elif (defined(__FREEBSD__) && (__FreeBSD_kernel_version > 800063)) 159#define REP_BUF_DATA(rep) ((rep)->buf->ugd_data) 160#else 161#define REP_BUF_DATA(rep) ((rep)->buf->data) 162#endif 163 164static int SDL_SYS_numjoysticks = 0; 165 166int 167SDL_SYS_JoystickInit(void) 168{ 169 char s[16]; 170 int i, fd; 171 172 SDL_SYS_numjoysticks = 0; 173 174 SDL_memset(joynames, 0, sizeof(joynames)); 175 SDL_memset(joydevnames, 0, sizeof(joydevnames)); 176 177 for (i = 0; i < MAX_UHID_JOYS; i++) { 178 SDL_Joystick nj; 179 180 SDL_snprintf(s, SDL_arraysize(s), "/dev/uhid%d", i); 181 182 joynames[SDL_SYS_numjoysticks] = strdup(s); 183 184 if (SDL_SYS_JoystickOpen(&nj, SDL_SYS_numjoysticks) == 0) { 185 SDL_SYS_JoystickClose(&nj); 186 SDL_SYS_numjoysticks++; 187 } else { 188 SDL_free(joynames[SDL_SYS_numjoysticks]); 189 joynames[SDL_SYS_numjoysticks] = NULL; 190 } 191 } 192 for (i = 0; i < MAX_JOY_JOYS; i++) { 193 SDL_snprintf(s, SDL_arraysize(s), "/dev/joy%d", i); 194 fd = open(s, O_RDONLY); 195 if (fd != -1) { 196 joynames[SDL_SYS_numjoysticks++] = strdup(s); 197 close(fd); 198 } 199 } 200 201 /* Read the default USB HID usage table. */ 202 hid_init(NULL); 203 204 return (SDL_SYS_numjoysticks); 205} 206 207int SDL_SYS_NumJoysticks() 208{ 209 return SDL_SYS_numjoysticks; 210} 211 212void SDL_SYS_JoystickDetect() 213{ 214} 215 216const char * 217SDL_SYS_JoystickNameForDeviceIndex(int device_index) 218{ 219 if (joydevnames[device_index] != NULL) { 220 return (joydevnames[device_index]); 221 } 222 return (joynames[device_index]); 223} 224 225/* Function to perform the mapping from device index to the instance id for this index */ 226SDL_JoystickID SDL_SYS_GetInstanceIdOfDeviceIndex(int device_index) 227{ 228 return device_index; 229} 230 231static int 232usage_to_joyaxe(unsigned usage) 233{ 234 int joyaxe; 235 switch (usage) { 236 case HUG_X: 237 joyaxe = JOYAXE_X; 238 break; 239 case HUG_Y: 240 joyaxe = JOYAXE_Y; 241 break; 242 case HUG_Z: 243 joyaxe = JOYAXE_Z; 244 break; 245 case HUG_SLIDER: 246 joyaxe = JOYAXE_SLIDER; 247 break; 248 case HUG_WHEEL: 249 joyaxe = JOYAXE_WHEEL; 250 break; 251 case HUG_RX: 252 joyaxe = JOYAXE_RX; 253 break; 254 case HUG_RY: 255 joyaxe = JOYAXE_RY; 256 break; 257 case HUG_RZ: 258 joyaxe = JOYAXE_RZ; 259 break; 260 default: 261 joyaxe = -1; 262 } 263 return joyaxe; 264} 265 266static unsigned 267hatval_to_sdl(Sint32 hatval) 268{ 269 static const unsigned hat_dir_map[8] = { 270 SDL_HAT_UP, SDL_HAT_RIGHTUP, SDL_HAT_RIGHT, SDL_HAT_RIGHTDOWN, 271 SDL_HAT_DOWN, SDL_HAT_LEFTDOWN, SDL_HAT_LEFT, SDL_HAT_LEFTUP 272 }; 273 unsigned result; 274 if ((hatval & 7) == hatval) 275 result = hat_dir_map[hatval]; 276 else 277 result = SDL_HAT_CENTERED; 278 return result; 279} 280 281 282int 283SDL_SYS_JoystickOpen(SDL_Joystick * joy, int device_index) 284{ 285 char *path = joynames[device_index]; 286 struct joystick_hwdata *hw; 287 struct hid_item hitem; 288 struct hid_data *hdata; 289 struct report *rep; 290 int fd; 291 int i; 292 293 fd = open(path, O_RDONLY); 294 if (fd == -1) { 295 return SDL_SetError("%s: %s", path, strerror(errno)); 296 } 297 298 joy->instance_id = device_index; 299 hw = (struct joystick_hwdata *) 300 SDL_malloc(sizeof(struct joystick_hwdata)); 301 if (hw == NULL) { 302 close(fd); 303 return SDL_OutOfMemory(); 304 } 305 joy->hwdata = hw; 306 hw->fd = fd; 307 hw->path = strdup(path); 308 if (!SDL_strncmp(path, "/dev/joy", 8)) { 309 hw->type = BSDJOY_JOY; 310 joy->naxes = 2; 311 joy->nbuttons = 2; 312 joy->nhats = 0; 313 joy->nballs = 0; 314 joydevnames[device_index] = strdup("Gameport joystick"); 315 goto usbend; 316 } else { 317 hw->type = BSDJOY_UHID; 318 } 319 320 { 321 int ax; 322 for (ax = 0; ax < JOYAXE_count; ax++) 323 hw->axis_map[ax] = -1; 324 } 325 hw->repdesc = hid_get_report_desc(fd); 326 if (hw->repdesc == NULL) { 327 SDL_SetError("%s: USB_GET_REPORT_DESC: %s", hw->path, 328 strerror(errno)); 329 goto usberr; 330 } 331 rep = &hw->inreport; 332#if defined(__FREEBSD__) && (__FreeBSD_kernel_version > 800063) || defined(__FreeBSD_kernel__) 333 rep->rid = hid_get_report_id(fd); 334 if (rep->rid < 0) { 335#else 336 if (ioctl(fd, USB_GET_REPORT_ID, &rep->rid) < 0) { 337#endif 338 rep->rid = -1; /* XXX */ 339 } 340 if (report_alloc(rep, hw->repdesc, REPORT_INPUT) < 0) { 341 goto usberr; 342 } 343 if (rep->size <= 0) { 344 SDL_SetError("%s: Input report descriptor has invalid length", 345 hw->path); 346 goto usberr; 347 } 348#if defined(USBHID_NEW) || (defined(__FREEBSD__) && __FreeBSD_kernel_version >= 500111) || defined(__FreeBSD_kernel__) 349 hdata = hid_start_parse(hw->repdesc, 1 << hid_input, rep->rid); 350#else 351 hdata = hid_start_parse(hw->repdesc, 1 << hid_input); 352#endif 353 if (hdata == NULL) { 354 SDL_SetError("%s: Cannot start HID parser", hw->path); 355 goto usberr; 356 } 357 joy->naxes = 0; 358 joy->nbuttons = 0; 359 joy->nhats = 0; 360 joy->nballs = 0; 361 for (i = 0; i < JOYAXE_count; i++) 362 hw->axis_map[i] = -1; 363 364 while (hid_get_item(hdata, &hitem) > 0) { 365 char *sp; 366 const char *s; 367 368 switch (hitem.kind) { 369 case hid_collection: 370 switch (HID_PAGE(hitem.usage)) { 371 case HUP_GENERIC_DESKTOP: 372 switch (HID_USAGE(hitem.usage)) { 373 case HUG_JOYSTICK: 374 case HUG_GAME_PAD: 375 s = hid_usage_in_page(hitem.usage); 376 sp = SDL_malloc(SDL_strlen(s) + 5); 377 SDL_snprintf(sp, SDL_strlen(s) + 5, "%s (%d)", 378 s, device_index); 379 joydevnames[device_index] = sp; 380 } 381 } 382 break; 383 case hid_input: 384 switch (HID_PAGE(hitem.usage)) { 385 case HUP_GENERIC_DESKTOP: 386 { 387 unsigned usage = HID_USAGE(hitem.usage); 388 int joyaxe = usage_to_joyaxe(usage); 389 if (joyaxe >= 0) { 390 hw->axis_map[joyaxe] = 1; 391 } else if (usage == HUG_HAT_SWITCH) { 392 joy->nhats++; 393 } 394 break; 395 } 396 case HUP_BUTTON: 397 joy->nbuttons++; 398 break; 399 default: 400 break; 401 } 402 break; 403 default: 404 break; 405 } 406 } 407 hid_end_parse(hdata); 408 for (i = 0; i < JOYAXE_count; i++) 409 if (hw->axis_map[i] > 0) 410 hw->axis_map[i] = joy->naxes++; 411 412 usbend: 413 /* The poll blocks the event thread. */ 414 fcntl(fd, F_SETFL, O_NONBLOCK); 415 416 return (0); 417 usberr: 418 close(hw->fd); 419 SDL_free(hw->path); 420 SDL_free(hw); 421 return (-1); 422} 423 424/* Function to determine is this joystick is attached to the system right now */ 425SDL_bool SDL_SYS_JoystickAttached(SDL_Joystick *joystick) 426{ 427 return SDL_TRUE; 428} 429 430void 431SDL_SYS_JoystickUpdate(SDL_Joystick * joy) 432{ 433 struct hid_item hitem; 434 struct hid_data *hdata; 435 struct report *rep; 436 int nbutton, naxe = -1; 437 Sint32 v; 438 439#if defined(__FREEBSD__) || SDL_JOYSTICK_USBHID_MACHINE_JOYSTICK_H || defined(__FreeBSD_kernel__) 440 struct joystick gameport; 441 static int x, y, xmin = 0xffff, ymin = 0xffff, xmax = 0, ymax = 0; 442 443 if (joy->hwdata->type == BSDJOY_JOY) { 444 while (read(joy->hwdata->fd, &gameport, sizeof gameport) == sizeof gameport) { 445 if (abs(x - gameport.x) > 8) { 446 x = gameport.x; 447 if (x < xmin) { 448 xmin = x; 449 } 450 if (x > xmax) { 451 xmax = x; 452 } 453 if (xmin == xmax) { 454 xmin--; 455 xmax++; 456 } 457 v = (Sint32) x; 458 v -= (xmax + xmin + 1) / 2; 459 v *= 32768 / ((xmax - xmin + 1) / 2); 460 SDL_PrivateJoystickAxis(joy, 0, v); 461 } 462 if (abs(y - gameport.y) > 8) { 463 y = gameport.y; 464 if (y < ymin) { 465 ymin = y; 466 } 467 if (y > ymax) { 468 ymax = y; 469 } 470 if (ymin == ymax) { 471 ymin--; 472 ymax++; 473 } 474 v = (Sint32) y; 475 v -= (ymax + ymin + 1) / 2; 476 v *= 32768 / ((ymax - ymin + 1) / 2); 477 SDL_PrivateJoystickAxis(joy, 1, v); 478 } 479 if (gameport.b1 != joy->buttons[0]) { 480 SDL_PrivateJoystickButton(joy, 0, gameport.b1); 481 } 482 if (gameport.b2 != joy->buttons[1]) { 483 SDL_PrivateJoystickButton(joy, 1, gameport.b2); 484 } 485 } 486 return; 487 } 488#endif /* defined(__FREEBSD__) || SDL_JOYSTICK_USBHID_MACHINE_JOYSTICK_H */ 489 490 rep = &joy->hwdata->inreport; 491 492 while (read(joy->hwdata->fd, REP_BUF_DATA(rep), rep->size) == rep->size) { 493#if defined(USBHID_NEW) || (defined(__FREEBSD__) && __FreeBSD_kernel_version >= 500111) || defined(__FreeBSD_kernel__) 494 hdata = hid_start_parse(joy->hwdata->repdesc, 1 << hid_input, rep->rid); 495#else 496 hdata = hid_start_parse(joy->hwdata->repdesc, 1 << hid_input); 497#endif 498 if (hdata == NULL) { 499 /*fprintf(stderr, "%s: Cannot start HID parser\n", joy->hwdata->path);*/ 500 continue; 501 } 502 503 for (nbutton = 0; hid_get_item(hdata, &hitem) > 0;) { 504 switch (hitem.kind) { 505 case hid_input: 506 switch (HID_PAGE(hitem.usage)) { 507 case HUP_GENERIC_DESKTOP: 508 { 509 unsigned usage = HID_USAGE(hitem.usage); 510 int joyaxe = usage_to_joyaxe(usage); 511 if (joyaxe >= 0) { 512 naxe = joy->hwdata->axis_map[joyaxe]; 513 /* scaleaxe */ 514 v = (Sint32) hid_get_data(REP_BUF_DATA(rep), &hitem); 515 v -= (hitem.logical_maximum + 516 hitem.logical_minimum + 1) / 2; 517 v *= 32768 / 518 ((hitem.logical_maximum - 519 hitem.logical_minimum + 1) / 2); 520 if (v != joy->axes[naxe]) { 521 SDL_PrivateJoystickAxis(joy, naxe, v); 522 } 523 } else if (usage == HUG_HAT_SWITCH) { 524 v = (Sint32) hid_get_data(REP_BUF_DATA(rep), &hitem); 525 SDL_PrivateJoystickHat(joy, 0, 526 hatval_to_sdl(v) - 527 hitem.logical_minimum); 528 } 529 break; 530 } 531 case HUP_BUTTON: 532 v = (Sint32) hid_get_data(REP_BUF_DATA(rep), &hitem); 533 if (joy->buttons[nbutton] != v) { 534 SDL_PrivateJoystickButton(joy, nbutton, v); 535 } 536 nbutton++; 537 break; 538 default: 539 continue; 540 } 541 break; 542 default: 543 break; 544 } 545 } 546 hid_end_parse(hdata); 547 } 548} 549 550/* Function to close a joystick after use */ 551void 552SDL_SYS_JoystickClose(SDL_Joystick * joy) 553{ 554 if (SDL_strncmp(joy->hwdata->path, "/dev/joy", 8)) { 555 report_free(&joy->hwdata->inreport); 556 hid_dispose_report_desc(joy->hwdata->repdesc); 557 } 558 close(joy->hwdata->fd); 559 SDL_free(joy->hwdata->path); 560 SDL_free(joy->hwdata); 561 562 return; 563} 564 565void 566SDL_SYS_JoystickQuit(void) 567{ 568 int i; 569 570 for (i = 0; i < MAX_JOYS; i++) { 571 SDL_free(joynames[i]); 572 SDL_free(joydevnames[i]); 573 } 574 575 return; 576} 577 578SDL_JoystickGUID SDL_SYS_JoystickGetDeviceGUID( int device_index ) 579{ 580 SDL_JoystickGUID guid; 581 /* the GUID is just the first 16 chars of the name for now */ 582 const char *name = SDL_SYS_JoystickNameForDeviceIndex( device_index ); 583 SDL_zero( guid ); 584 SDL_memcpy( &guid, name, SDL_min( sizeof(guid), SDL_strlen( name ) ) ); 585 return guid; 586} 587 588SDL_JoystickGUID SDL_SYS_JoystickGetGUID(SDL_Joystick * joystick) 589{ 590 SDL_JoystickGUID guid; 591 /* the GUID is just the first 16 chars of the name for now */ 592 const char *name = joystick->name; 593 SDL_zero( guid ); 594 SDL_memcpy( &guid, name, SDL_min( sizeof(guid), SDL_strlen( name ) ) ); 595 return guid; 596} 597 598static int 599report_alloc(struct report *r, struct report_desc *rd, int repind) 600{ 601 int len; 602 603#ifdef __DragonFly__ 604 len = hid_report_size(rd, r->rid, repinfo[repind].kind); 605#elif __FREEBSD__ 606# if (__FreeBSD_kernel_version >= 460000) || defined(__FreeBSD_kernel__) 607# if (__FreeBSD_kernel_version <= 500111) 608 len = hid_report_size(rd, r->rid, repinfo[repind].kind); 609# else 610 len = hid_report_size(rd, repinfo[repind].kind, r->rid); 611# endif 612# else 613 len = hid_report_size(rd, repinfo[repind].kind, &r->rid); 614# endif 615#else 616# ifdef USBHID_NEW 617 len = hid_report_size(rd, repinfo[repind].kind, r->rid); 618# else 619 len = hid_report_size(rd, repinfo[repind].kind, &r->rid); 620# endif 621#endif 622 623 if (len < 0) { 624 return SDL_SetError("Negative HID report size"); 625 } 626 r->size = len; 627 628 if (r->size > 0) { 629#if defined(__FREEBSD__) && (__FreeBSD_kernel_version > 900000) 630 r->buf = SDL_malloc(r->size); 631#else 632 r->buf = SDL_malloc(sizeof(*r->buf) - sizeof(REP_BUF_DATA(r)) + 633 r->size); 634#endif 635 if (r->buf == NULL) { 636 return SDL_OutOfMemory(); 637 } 638 } else { 639 r->buf = NULL; 640 } 641 642 r->status = SREPORT_CLEAN; 643 return 0; 644} 645 646static void 647report_free(struct report *r) 648{ 649 SDL_free(r->buf); 650 r->status = SREPORT_UNINIT; 651} 652 653#endif /* SDL_JOYSTICK_USBHID */ 654 655/* vi: set ts=4 sw=4 expandtab: */