/* See LICENSE file for copyright and license details. */ #include "common.h" #define MAX2(A, B) ((A) > (B) ? (A) : (B)) #define MAX4(A, B, C, D) (MAX2(MAX2(A, B), MAX2(C, D))) #define DEFER_EINTR(ERROR_CONDITION, GOTO_ON_FAILURE)\ DEFER_EINTR_(devicep, ERROR_CONDITION, GOTO_ON_FAILURE) static void store_uint(uint64_t value, char *buf) { buf[0] = (char)((value >> 0) & 255); buf[1] = (char)((value >> 8) & 255); buf[2] = (char)((value >> 16) & 255); buf[3] = (char)((value >> 24) & 255); buf[4] = (char)((value >> 32) & 255); buf[5] = (char)((value >> 40) & 255); buf[6] = (char)((value >> 48) & 255); buf[7] = (char)((value >> 56) & 255); } LIBGAMEPAD_CONST__ static size_t popcount(uint8_t value) { size_t n = 0; for (; value; value >>= 1) if (value & 1) n += 1; return n; } static int get_fingerprint(struct libgamepad_device *device, char non_unique[65], char unique[65]) { struct libsha2_state s, s2; char buf[128]; size_t i, n; if (libsha2_init(&s, LIBSHA2_256)) return -1; libsha2_update(&s, device->name, 8 * strlen(device->name) + 8); libsha2_update(&s, device->physical_location, 8 * strlen(device->physical_location) + 8); store_uint(device->vendor, &buf[0]); store_uint(device->product, &buf[8]); store_uint(device->version, &buf[16]); libsha2_update(&s, buf, 3 * 64); store_uint(device->nbuttons, buf); for (n = 8, i = 0; i < ELEMSOF(device->button_map); i++) { if (device->button_map[i] >= 0) { store_uint((uint64_t)i, &buf[n]); n += 8; if (n == sizeof(buf)) { libsha2_update(&s, buf, 8 * n); n = 0; } } } libsha2_update(&s, buf, 8 * n); n = 0; store_uint(device->nabsolute_axes, buf); for (n = 8, i = 0; i < ELEMSOF(device->absolute_axis_map); i++) { if (device->absolute_axis_map[i] >= 0) { store_uint((uint64_t)i, &buf[n]); n += 8; if (n == sizeof(buf)) { libsha2_update(&s, buf, 8 * n); n = 0; } } } libsha2_update(&s, buf, 8 * n); n = 0; store_uint(device->nrelative_axes, buf); for (n = 8, i = 0; i < ELEMSOF(device->relative_axis_map); i++) { if (device->relative_axis_map[i] >= 0) { store_uint((uint64_t)i, &buf[n]); n += 8; if (n == sizeof(buf)) { libsha2_update(&s, buf, 8 * n); n = 0; } } } libsha2_update(&s, buf, 8 * n); n = 0; n = 0; for (i = 0; i < ELEMSOF(device->force_feedback_support); i++) if (device->force_feedback_support[i]) n += popcount(device->force_feedback_support[i]); store_uint((uint64_t)n, buf); for (n = 8, i = 0; i < BITSOF(device->force_feedback_support); i++) { if (GETBIT(device->force_feedback_support, i)) { store_uint((uint64_t)i, &buf[n]); n += 8; if (n == sizeof(buf)) { libsha2_update(&s, buf, 8 * n); n = 0; } } } libsha2_update(&s, buf, 8 * n); n = 0; memcpy(&s2, &s, sizeof(s)); libsha2_update(&s2, device->unique_id, 8 * strlen(device->unique_id) + 8); libsha2_digest(&s, NULL, 0, buf); libsha2_behex_lower(non_unique, buf, 32); libsha2_digest(&s2, NULL, 0, buf); libsha2_behex_lower(unique, buf, 32); return 0; } int libgamepad_open_device(struct libgamepad_device *devicep, int dirfd, const char *path, int mode) { unsigned long int bits[ELEMS_REQUIRED(MAX4(ELEMSOF(devicep->button_map), ELEMSOF(devicep->absolute_axis_map), ELEMSOF(devicep->relative_axis_map), ELEMSOF(devicep->force_feedback_support)), long int)]; int r, saved_errno = errno; int16_t j; uint16_t n; unsigned int i, max; struct input_id id; char *buf = NULL; size_t bufsize = 0; void *new; memset(devicep, 0, sizeof(*devicep)); devicep->fd = dirfd; devicep->auto_sync = 1; devicep->internals = calloc(1, sizeof(*devicep->internals)); if (!devicep->internals) return -1; if (path && *path) { devicep->fd = openat(dirfd, path, mode); if (devicep->fd < 0) goto fail; devicep->internals->close_fd = 1; } DEFER_EINTR(ioctl(devicep->fd, EVIOCGID, &id) < 0, fail); devicep->bus_type = (unsigned int)id.bustype; devicep->vendor = (unsigned int)id.vendor; devicep->product = (unsigned int)id.product; devicep->version = (unsigned int)id.version; #define GET_STRING(EVIOCMACRO, OUTPUT)\ do {\ for (;;) {\ DEFER_EINTR((r = ioctl(devicep->fd, EVIOCMACRO(bufsize), buf)) < 0, fail_free_buf);\ if ((size_t)r < bufsize)\ break;\ new = realloc(buf, bufsize += 32);\ if (!new)\ goto fail_free_buf;\ buf = new;\ }\ buf[r] = '\0';\ *OUTPUT = strdup(buf);\ if (!*OUTPUT)\ goto fail_free_buf;\ } while (0) GET_STRING(EVIOCGNAME, &devicep->name); GET_STRING(EVIOCGUNIQ, &devicep->unique_id); GET_STRING(EVIOCGPHYS, &devicep->physical_location); free(buf); #define CREATE_MAPS(EVENTS, SINGULAR, PLURAL)\ do {\ /* Code-to-index map */\ DEFER_EINTR((r = ioctl(devicep->fd, EVIOCGBIT(EVENTS, sizeof(bits)), bits)) < 0, fail);\ max = (unsigned int)r * 8;\ devicep->n##PLURAL = 0;\ for (i = 0; i < ELEMSOF(devicep->SINGULAR##_map); i++) {\ devicep->SINGULAR##_map[i] = -1;\ if (i < max && ((bits[i / BITSOF(*bits)] >> (i % BITSOF(*bits))) & 1))\ devicep->SINGULAR##_map[i] = (int16_t)devicep->n##PLURAL++;\ }\ \ /* Index-to-code map */\ if (devicep->n##PLURAL) {\ devicep->PLURAL = calloc(devicep->n##PLURAL, sizeof(*devicep->PLURAL));\ if (!devicep->PLURAL)\ goto fail;\ for (i = 0, n = 0; i < ELEMSOF(devicep->SINGULAR##_map); i++)\ if (devicep->SINGULAR##_map[i] >= 0)\ devicep->PLURAL[n++] = (uint16_t)i;\ }\ } while (0) CREATE_MAPS(EV_KEY, button, buttons); CREATE_MAPS(EV_ABS, absolute_axis, absolute_axes); CREATE_MAPS(EV_REL, relative_axis, relative_axes); if (devicep->nabsolute_axes) { devicep->internals->absinfo = calloc(devicep->nabsolute_axes, sizeof(*devicep->internals->absinfo)); if (!devicep->internals->absinfo) goto fail; } for (i = 0; i < devicep->nabsolute_axes; i++) { DEFER_EINTR(ioctl(devicep->fd, EVIOCGABS((unsigned int)devicep->absolute_axes[i]), &devicep->internals->absinfo[i]) < 0, fail); if (devicep->internals->absinfo[i].minimum == devicep->internals->absinfo[i].maximum) { if (devicep->absolute_axes[i] == ABS_MT_TRACKING_ID) { devicep->internals->absinfo[i].minimum = -1; devicep->internals->absinfo[i].maximum = 0xFFFF; } } } if (devicep->nbuttons) { devicep->internals->buttons = calloc((devicep->nbuttons + 7) / 8, sizeof(*devicep->internals->buttons)); if (!devicep->internals->buttons) goto fail; DEFER_EINTR((r = ioctl(devicep->fd, EVIOCGKEY(sizeof(bits)), bits)) < 0, fail); max = (unsigned int)r * 8; for (i = 0; i < max && i < ELEMSOF(devicep->button_map); i++) { j = devicep->button_map[i]; if (j >= 0 && GETBIT(bits, i)) SETBIT(devicep->internals->buttons, (uint16_t)j); } } DEFER_EINTR((r = ioctl(devicep->fd, EVIOCGBIT(EV_FF, sizeof(bits)), bits)) < 0, fail); max = (unsigned int)r * 8; for (i = 0; i < BITSOF(devicep->force_feedback_support); i++) if (i < max && ((bits[i / BITSOF(*bits)] >> (i % BITSOF(*bits))) & 1)) SETBIT(devicep->force_feedback_support, i); if (get_fingerprint(devicep, devicep->fingerprint, devicep->fingerprint_unique)) goto fail; errno = saved_errno; return 0; fail_free_buf: free(buf); fail: libgamepad_close_device(devicep); /* sets `errno` to `EINTR` if it was deferred */ return -1; }