Atlas - SDL_sysjoystick.c

Home / ext / SDL2 / src / joystick / bsd Lines: 2 | Size: 19070 bytes [Download] [Show on GitHub] [Search similar files] [Raw] [Raw (proxy)]
[FILE BEGIN]
1/* 2 Simple DirectMedia Layer 3 Copyright (C) 1997-2018 Sam Lantinga <[email protected]> 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 64 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 numjoysticks = 0; 165 166static int BSD_JoystickOpen(SDL_Joystick * joy, int device_index); 167static void BSD_JoystickClose(SDL_Joystick * joy); 168 169static int 170BSD_JoystickInit(void) 171{ 172 char s[16]; 173 int i, fd; 174 175 numjoysticks = 0; 176 177 SDL_memset(joynames, 0, sizeof(joynames)); 178 SDL_memset(joydevnames, 0, sizeof(joydevnames)); 179 180 for (i = 0; i < MAX_UHID_JOYS; i++) { 181 SDL_Joystick nj; 182 183 SDL_snprintf(s, SDL_arraysize(s), "/dev/uhid%d", i); 184 185 joynames[numjoysticks] = SDL_strdup(s); 186 187 if (BSD_JoystickOpen(&nj, numjoysticks) == 0) { 188 BSD_JoystickClose(&nj); 189 numjoysticks++; 190 } else { 191 SDL_free(joynames[numjoysticks]); 192 joynames[numjoysticks] = NULL; 193 } 194 } 195 for (i = 0; i < MAX_JOY_JOYS; i++) { 196 SDL_snprintf(s, SDL_arraysize(s), "/dev/joy%d", i); 197 fd = open(s, O_RDONLY); 198 if (fd != -1) { 199 joynames[numjoysticks++] = SDL_strdup(s); 200 close(fd); 201 } 202 } 203 204 /* Read the default USB HID usage table. */ 205 hid_init(NULL); 206 207 return (numjoysticks); 208} 209 210static int 211BSD_JoystickGetCount(void) 212{ 213 return numjoysticks; 214} 215 216static void 217BSD_JoystickDetect(void) 218{ 219} 220 221static const char * 222BSD_JoystickGetDeviceName(int device_index) 223{ 224 if (joydevnames[device_index] != NULL) { 225 return (joydevnames[device_index]); 226 } 227 return (joynames[device_index]); 228} 229 230/* Function to perform the mapping from device index to the instance id for this index */ 231static SDL_JoystickID 232BSD_JoystickGetDeviceInstanceID(int device_index) 233{ 234 return device_index; 235} 236 237static int 238usage_to_joyaxe(unsigned usage) 239{ 240 int joyaxe; 241 switch (usage) { 242 case HUG_X: 243 joyaxe = JOYAXE_X; 244 break; 245 case HUG_Y: 246 joyaxe = JOYAXE_Y; 247 break; 248 case HUG_Z: 249 joyaxe = JOYAXE_Z; 250 break; 251 case HUG_SLIDER: 252 joyaxe = JOYAXE_SLIDER; 253 break; 254 case HUG_WHEEL: 255 joyaxe = JOYAXE_WHEEL; 256 break; 257 case HUG_RX: 258 joyaxe = JOYAXE_RX; 259 break; 260 case HUG_RY: 261 joyaxe = JOYAXE_RY; 262 break; 263 case HUG_RZ: 264 joyaxe = JOYAXE_RZ; 265 break; 266 default: 267 joyaxe = -1; 268 } 269 return joyaxe; 270} 271 272static unsigned 273hatval_to_sdl(Sint32 hatval) 274{ 275 static const unsigned hat_dir_map[8] = { 276 SDL_HAT_UP, SDL_HAT_RIGHTUP, SDL_HAT_RIGHT, SDL_HAT_RIGHTDOWN, 277 SDL_HAT_DOWN, SDL_HAT_LEFTDOWN, SDL_HAT_LEFT, SDL_HAT_LEFTUP 278 }; 279 unsigned result; 280 if ((hatval & 7) == hatval) 281 result = hat_dir_map[hatval]; 282 else 283 result = SDL_HAT_CENTERED; 284 return result; 285} 286 287 288static int 289BSD_JoystickOpen(SDL_Joystick * joy, int device_index) 290{ 291 char *path = joynames[device_index]; 292 struct joystick_hwdata *hw; 293 struct hid_item hitem; 294 struct hid_data *hdata; 295 struct report *rep = NULL; 296 int fd; 297 int i; 298 299 fd = open(path, O_RDONLY); 300 if (fd == -1) { 301 return SDL_SetError("%s: %s", path, strerror(errno)); 302 } 303 304 joy->instance_id = device_index; 305 hw = (struct joystick_hwdata *) 306 SDL_malloc(sizeof(struct joystick_hwdata)); 307 if (hw == NULL) { 308 close(fd); 309 return SDL_OutOfMemory(); 310 } 311 joy->hwdata = hw; 312 hw->fd = fd; 313 hw->path = SDL_strdup(path); 314 if (!SDL_strncmp(path, "/dev/joy", 8)) { 315 hw->type = BSDJOY_JOY; 316 joy->naxes = 2; 317 joy->nbuttons = 2; 318 joy->nhats = 0; 319 joy->nballs = 0; 320 joydevnames[device_index] = SDL_strdup("Gameport joystick"); 321 goto usbend; 322 } else { 323 hw->type = BSDJOY_UHID; 324 } 325 326 { 327 int ax; 328 for (ax = 0; ax < JOYAXE_count; ax++) 329 hw->axis_map[ax] = -1; 330 } 331 hw->repdesc = hid_get_report_desc(fd); 332 if (hw->repdesc == NULL) { 333 SDL_SetError("%s: USB_GET_REPORT_DESC: %s", hw->path, 334 strerror(errno)); 335 goto usberr; 336 } 337 rep = &hw->inreport; 338#if defined(__FREEBSD__) && (__FreeBSD_kernel_version > 800063) || defined(__FreeBSD_kernel__) 339 rep->rid = hid_get_report_id(fd); 340 if (rep->rid < 0) { 341#else 342 if (ioctl(fd, USB_GET_REPORT_ID, &rep->rid) < 0) { 343#endif 344 rep->rid = -1; /* XXX */ 345 } 346#if defined(__NetBSD__) 347 usb_device_descriptor_t udd; 348 struct usb_string_desc usd; 349 if (ioctl(fd, USB_GET_DEVICE_DESC, &udd) == -1) 350 goto desc_failed; 351 352 /* Get default language */ 353 usd.usd_string_index = USB_LANGUAGE_TABLE; 354 usd.usd_language_id = 0; 355 if (ioctl(fd, USB_GET_STRING_DESC, &usd) == -1 || usd.usd_desc.bLength < 4) { 356 usd.usd_language_id = 0; 357 } else { 358 usd.usd_language_id = UGETW(usd.usd_desc.bString[0]); 359 } 360 361 usd.usd_string_index = udd.iProduct; 362 if (ioctl(fd, USB_GET_STRING_DESC, &usd) == 0) { 363 char str[128]; 364 char *new_name = NULL; 365 int i; 366 for (i = 0; i < (usd.usd_desc.bLength >> 1) - 1 && i < sizeof(str) - 1; i++) { 367 str[i] = UGETW(usd.usd_desc.bString[i]); 368 } 369 str[i] = '\0'; 370 asprintf(&new_name, "%s @ %s", str, path); 371 if (new_name != NULL) { 372 SDL_free(joydevnames[numjoysticks]); 373 joydevnames[numjoysticks] = new_name; 374 } 375 } 376desc_failed: 377#endif 378 if (report_alloc(rep, hw->repdesc, REPORT_INPUT) < 0) { 379 goto usberr; 380 } 381 if (rep->size <= 0) { 382 SDL_SetError("%s: Input report descriptor has invalid length", 383 hw->path); 384 goto usberr; 385 } 386#if defined(USBHID_NEW) || (defined(__FREEBSD__) && __FreeBSD_kernel_version >= 500111) || defined(__FreeBSD_kernel__) 387 hdata = hid_start_parse(hw->repdesc, 1 << hid_input, rep->rid); 388#else 389 hdata = hid_start_parse(hw->repdesc, 1 << hid_input); 390#endif 391 if (hdata == NULL) { 392 SDL_SetError("%s: Cannot start HID parser", hw->path); 393 goto usberr; 394 } 395 joy->naxes = 0; 396 joy->nbuttons = 0; 397 joy->nhats = 0; 398 joy->nballs = 0; 399 for (i = 0; i < JOYAXE_count; i++) 400 hw->axis_map[i] = -1; 401 402 while (hid_get_item(hdata, &hitem) > 0) { 403 char *sp; 404 const char *s; 405 406 switch (hitem.kind) { 407 case hid_collection: 408 switch (HID_PAGE(hitem.usage)) { 409 case HUP_GENERIC_DESKTOP: 410 switch (HID_USAGE(hitem.usage)) { 411 case HUG_JOYSTICK: 412 case HUG_GAME_PAD: 413 s = hid_usage_in_page(hitem.usage); 414 sp = SDL_malloc(SDL_strlen(s) + 5); 415 SDL_snprintf(sp, SDL_strlen(s) + 5, "%s (%d)", 416 s, device_index); 417 joydevnames[device_index] = sp; 418 } 419 } 420 break; 421 case hid_input: 422 switch (HID_PAGE(hitem.usage)) { 423 case HUP_GENERIC_DESKTOP: 424 { 425 unsigned usage = HID_USAGE(hitem.usage); 426 int joyaxe = usage_to_joyaxe(usage); 427 if (joyaxe >= 0) { 428 hw->axis_map[joyaxe] = 1; 429 } else if (usage == HUG_HAT_SWITCH) { 430 joy->nhats++; 431 } 432 break; 433 } 434 case HUP_BUTTON: 435 joy->nbuttons++; 436 break; 437 default: 438 break; 439 } 440 break; 441 default: 442 break; 443 } 444 } 445 hid_end_parse(hdata); 446 for (i = 0; i < JOYAXE_count; i++) 447 if (hw->axis_map[i] > 0) 448 hw->axis_map[i] = joy->naxes++; 449 450 if (joy->naxes == 0 && joy->nbuttons == 0 && joy->nhats == 0 && joy->nballs == 0) { 451 SDL_SetError("%s: Not a joystick, ignoring", hw->path); 452 goto usberr; 453 } 454 455 usbend: 456 /* The poll blocks the event thread. */ 457 fcntl(fd, F_SETFL, O_NONBLOCK); 458#ifdef __NetBSD__ 459 /* Flush pending events */ 460 if (rep) { 461 while (read(joy->hwdata->fd, REP_BUF_DATA(rep), rep->size) == rep->size) 462 ; 463 } 464#endif 465 466 return (0); 467 usberr: 468 close(hw->fd); 469 SDL_free(hw->path); 470 SDL_free(hw); 471 return (-1); 472} 473 474static void 475BSD_JoystickUpdate(SDL_Joystick * joy) 476{ 477 struct hid_item hitem; 478 struct hid_data *hdata; 479 struct report *rep; 480 int nbutton, naxe = -1; 481 Sint32 v; 482 483#if defined(__FREEBSD__) || SDL_JOYSTICK_USBHID_MACHINE_JOYSTICK_H || defined(__FreeBSD_kernel__) 484 struct joystick gameport; 485 static int x, y, xmin = 0xffff, ymin = 0xffff, xmax = 0, ymax = 0; 486 487 if (joy->hwdata->type == BSDJOY_JOY) { 488 while (read(joy->hwdata->fd, &gameport, sizeof gameport) == sizeof gameport) { 489 if (abs(x - gameport.x) > 8) { 490 x = gameport.x; 491 if (x < xmin) { 492 xmin = x; 493 } 494 if (x > xmax) { 495 xmax = x; 496 } 497 if (xmin == xmax) { 498 xmin--; 499 xmax++; 500 } 501 v = (Sint32) x; 502 v -= (xmax + xmin + 1) / 2; 503 v *= 32768 / ((xmax - xmin + 1) / 2); 504 SDL_PrivateJoystickAxis(joy, 0, v); 505 } 506 if (abs(y - gameport.y) > 8) { 507 y = gameport.y; 508 if (y < ymin) { 509 ymin = y; 510 } 511 if (y > ymax) { 512 ymax = y; 513 } 514 if (ymin == ymax) { 515 ymin--; 516 ymax++; 517 } 518 v = (Sint32) y; 519 v -= (ymax + ymin + 1) / 2; 520 v *= 32768 / ((ymax - ymin + 1) / 2); 521 SDL_PrivateJoystickAxis(joy, 1, v); 522 } 523 SDL_PrivateJoystickButton(joy, 0, gameport.b1); 524 SDL_PrivateJoystickButton(joy, 1, gameport.b2); 525 } 526 return; 527 } 528#endif /* defined(__FREEBSD__) || SDL_JOYSTICK_USBHID_MACHINE_JOYSTICK_H */ 529 530 rep = &joy->hwdata->inreport; 531 532 while (read(joy->hwdata->fd, REP_BUF_DATA(rep), rep->size) == rep->size) { 533#if defined(USBHID_NEW) || (defined(__FREEBSD__) && __FreeBSD_kernel_version >= 500111) || defined(__FreeBSD_kernel__) 534 hdata = hid_start_parse(joy->hwdata->repdesc, 1 << hid_input, rep->rid); 535#else 536 hdata = hid_start_parse(joy->hwdata->repdesc, 1 << hid_input); 537#endif 538 if (hdata == NULL) { 539 /*fprintf(stderr, "%s: Cannot start HID parser\n", joy->hwdata->path);*/ 540 continue; 541 } 542 543 for (nbutton = 0; hid_get_item(hdata, &hitem) > 0;) { 544 switch (hitem.kind) { 545 case hid_input: 546 switch (HID_PAGE(hitem.usage)) { 547 case HUP_GENERIC_DESKTOP: 548 { 549 unsigned usage = HID_USAGE(hitem.usage); 550 int joyaxe = usage_to_joyaxe(usage); 551 if (joyaxe >= 0) { 552 naxe = joy->hwdata->axis_map[joyaxe]; 553 /* scaleaxe */ 554 v = (Sint32) hid_get_data(REP_BUF_DATA(rep), &hitem); 555 v -= (hitem.logical_maximum + 556 hitem.logical_minimum + 1) / 2; 557 v *= 32768 / 558 ((hitem.logical_maximum - 559 hitem.logical_minimum + 1) / 2); 560 SDL_PrivateJoystickAxis(joy, naxe, v); 561 } else if (usage == HUG_HAT_SWITCH) { 562 v = (Sint32) hid_get_data(REP_BUF_DATA(rep), &hitem); 563 SDL_PrivateJoystickHat(joy, 0, 564 hatval_to_sdl(v) - 565 hitem.logical_minimum); 566 } 567 break; 568 } 569 case HUP_BUTTON: 570 v = (Sint32) hid_get_data(REP_BUF_DATA(rep), &hitem); 571 SDL_PrivateJoystickButton(joy, nbutton, v); 572 nbutton++; 573 break; 574 default: 575 continue; 576 } 577 break; 578 default: 579 break; 580 } 581 } 582 hid_end_parse(hdata); 583 } 584} 585 586/* Function to close a joystick after use */ 587static void 588BSD_JoystickClose(SDL_Joystick * joy) 589{ 590 if (SDL_strncmp(joy->hwdata->path, "/dev/joy", 8)) { 591 report_free(&joy->hwdata->inreport); 592 hid_dispose_report_desc(joy->hwdata->repdesc); 593 } 594 close(joy->hwdata->fd); 595 SDL_free(joy->hwdata->path); 596 SDL_free(joy->hwdata); 597} 598 599static void 600BSD_JoystickQuit(void) 601{ 602 int i; 603 604 for (i = 0; i < MAX_JOYS; i++) { 605 SDL_free(joynames[i]); 606 SDL_free(joydevnames[i]); 607 } 608 609 return; 610} 611 612static SDL_JoystickGUID 613BSD_JoystickGetDeviceGUID( int device_index ) 614{ 615 SDL_JoystickGUID guid; 616 /* the GUID is just the first 16 chars of the name for now */ 617 const char *name = BSD_JoystickGetDeviceName( device_index ); 618 SDL_zero( guid ); 619 SDL_memcpy( &guid, name, SDL_min( sizeof(guid), SDL_strlen( name ) ) ); 620 return guid; 621} 622 623static int 624report_alloc(struct report *r, struct report_desc *rd, int repind) 625{ 626 int len; 627 628#ifdef __DragonFly__ 629 len = hid_report_size(rd, r->rid, repinfo[repind].kind); 630#elif __FREEBSD__ 631# if (__FreeBSD_kernel_version >= 460000) || defined(__FreeBSD_kernel__) 632# if (__FreeBSD_kernel_version <= 500111) 633 len = hid_report_size(rd, r->rid, repinfo[repind].kind); 634# else 635 len = hid_report_size(rd, repinfo[repind].kind, r->rid); 636# endif 637# else 638 len = hid_report_size(rd, repinfo[repind].kind, &r->rid); 639# endif 640#else 641# ifdef USBHID_NEW 642 len = hid_report_size(rd, repinfo[repind].kind, r->rid); 643# else 644 len = hid_report_size(rd, repinfo[repind].kind, &r->rid); 645# endif 646#endif 647 648 if (len < 0) { 649 return SDL_SetError("Negative HID report size"); 650 } 651 r->size = len; 652 653 if (r->size > 0) { 654#if defined(__FREEBSD__) && (__FreeBSD_kernel_version > 900000) 655 r->buf = SDL_malloc(r->size); 656#else 657 r->buf = SDL_malloc(sizeof(*r->buf) - sizeof(REP_BUF_DATA(r)) + 658 r->size); 659#endif 660 if (r->buf == NULL) { 661 return SDL_OutOfMemory(); 662 } 663 } else { 664 r->buf = NULL; 665 } 666 667 r->status = SREPORT_CLEAN; 668 return 0; 669} 670 671static void 672report_free(struct report *r) 673{ 674 SDL_free(r->buf); 675 r->status = SREPORT_UNINIT; 676} 677 678static int 679BSD_JoystickRumble(SDL_Joystick * joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble, Uint32 duration_ms) 680{ 681 return SDL_Unsupported(); 682} 683 684SDL_JoystickDriver SDL_BSD_JoystickDriver = 685{ 686 BSD_JoystickInit, 687 BSD_JoystickGetCount, 688 BSD_JoystickDetect, 689 BSD_JoystickGetDeviceName, 690 BSD_JoystickGetDeviceGUID, 691 BSD_JoystickGetDeviceInstanceID, 692 BSD_JoystickOpen, 693 BSD_JoystickRumble, 694 BSD_JoystickUpdate, 695 BSD_JoystickClose, 696 BSD_JoystickQuit, 697}; 698 699#endif /* SDL_JOYSTICK_USBHID */ 700 701/* vi: set ts=4 sw=4 expandtab: */ 702
[FILE END]
(C) 2025 0x4248 (C) 2025 4248 Media and 4248 Systems, All part of 0x4248 See LICENCE files for more information. Not all files are by 0x4248 always check Licencing.