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 4 81#define MAX_JOY_JOYS 2 82#define MAX_JOYS (MAX_UHID_JOYS + MAX_JOY_JOYS) 83 84struct 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 99static 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 109enum { 110 REPORT_INPUT = 0, 111 REPORT_OUTPUT = 1, 112 REPORT_FEATURE = 2 113}; 114 115enum { 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 127struct 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 145static char *joynames[MAX_JOYS]; 146static char *joydevnames[MAX_JOYS]; 147 148static int report_alloc(struct report *, struct report_desc *, int); 149static 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 159int 160SDL_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 201const char * 202SDL_SYS_JoystickName(int index) 203{ 204 if (joydevnames[index] != NULL) { 205 return (joydevnames[index]); 206 } 207 return (joynames[index]); 208} 209 210static int 211usage_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 237static unsigned 238hatval_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 253int 254SDL_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 389usbend: 390 /* The poll blocks the event thread. */ 391 fcntl(fd, F_SETFL, O_NONBLOCK); 392 393 return (0); 394usberr: 395 close(hw->fd); 396 SDL_free(hw->path); 397 SDL_free(hw); 398 return (-1); 399} 400 401void 402SDL_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 */ 524void 525SDL_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 538void 539SDL_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 553static int 554report_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 599static void 600report_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