/* 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 (;;) {\
while ((r = ioctl(devicep->fd, EVIOCMACRO(bufsize), buf)) < 0) {\
if (errno == EINTR) {\
devicep->internals->deferred_error = EINTR;\
continue;\
} else if (errno == ENOENT) {\
if (bufsize)\
r = 0;\
break;\
} else {\
goto 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;
}