cscg22-gearboy

CSCG 2022 Challenge 'Gearboy'
git clone https://git.sinitax.com/sinitax/cscg22-gearboy
Log | Files | Refs | sfeed.txt

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: */