aboutsummaryrefslogtreecommitdiffstats
path: root/libgamepad_open_device.c
diff options
context:
space:
mode:
authorMattias Andrée <maandree@kth.se>2022-07-27 15:32:10 +0200
committerMattias Andrée <maandree@kth.se>2022-07-27 15:32:10 +0200
commit586ddd6fad5061ea1de4b0fbe903c1c9ea582f72 (patch)
treee34b10acf215a2140df2fd3728300c34825f0734 /libgamepad_open_device.c
parentnames.sh: improve readability (diff)
downloadlibgamepad-586ddd6fad5061ea1de4b0fbe903c1c9ea582f72.tar.gz
libgamepad-586ddd6fad5061ea1de4b0fbe903c1c9ea582f72.tar.bz2
libgamepad-586ddd6fad5061ea1de4b0fbe903c1c9ea582f72.tar.xz
m + add controller fingerprint + relay less on libevdev
Signed-off-by: Mattias Andrée <maandree@kth.se>
Diffstat (limited to 'libgamepad_open_device.c')
-rw-r--r--libgamepad_open_device.c289
1 files changed, 226 insertions, 63 deletions
diff --git a/libgamepad_open_device.c b/libgamepad_open_device.c
index 3c2a751..22163a4 100644
--- a/libgamepad_open_device.c
+++ b/libgamepad_open_device.c
@@ -1,39 +1,165 @@
/* See LICENSE file for copyright and license details. */
#include "common.h"
+#define ELEMS_REQUIRED(BITS, TYPE) ((BITS + BITSOF(TYPE) - 1) / BITSOF(TYPE))
+#define MAX2(A, B) ((A) > (B) ? (A) : (B))
+#define MAX4(A, B, C, D) (MAX2(MAX2(A, B), MAX2(C, D)))
+
+
+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)
{
- int err;
- unsigned int i;
+ 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 err, r;
+ 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->internals = NULL;
- devicep->buttons = NULL;
- devicep->absolute_axes = NULL;
- devicep->relative_axes = NULL;
- memset(devicep->force_feedback_support, 0, sizeof(devicep->force_feedback_support));
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) {
- libgamepad_close_device(devicep);
- return -1;
- }
+ if (devicep->fd < 0)
+ goto fail;
devicep->internals->close_fd = 1;
}
- if (ioctl(devicep->fd, EVIOCGID, &id)) {
- libgamepad_close_device(devicep);
- return -1;
- }
+
+ if (ioctl(devicep->fd, EVIOCGID, &id) < 0)
+ goto 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;
+
err = libevdev_new_from_fd(devicep->fd, &devicep->internals->dev);
if (err < 0) {
@@ -42,68 +168,105 @@ libgamepad_open_device(struct libgamepad_device *devicep, int dirfd, const char
return -1;
}
- devicep->name = libevdev_get_name(devicep->internals->dev);
- devicep->unique_id = libevdev_get_uniq(devicep->internals->dev);
- devicep->physical_location = libevdev_get_phys(devicep->internals->dev);
- 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;
-
- devicep->nbuttons = 0;
- for (i = 0; i < KEY_CNT; i++) {
- devicep->button_map[i] = -1;
- if (libevdev_has_event_code(devicep->internals->dev, EV_KEY, i))
- devicep->button_map[i] = (int16_t)devicep->nbuttons++;
- }
- devicep->nabsolute_axes = 0;
- for (i = 0; i < ABS_CNT; i++) {
- devicep->absolute_axis_map[i] = -1;
- if (libevdev_has_event_code(devicep->internals->dev, EV_ABS, i))
- devicep->absolute_axis_map[i] = (int16_t)devicep->nabsolute_axes++;
- }
+#define GET_STRING(EVIOCMACRO, OUTPUT)\
+ do {\
+ for (;;) {\
+ r = ioctl(devicep->fd, EVIOCMACRO(bufsize), buf);\
+ if (r < 0)\
+ 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);
- devicep->nrelative_axes = 0;
- for (i = 0; i < REL_CNT; i++) {
- devicep->relative_axis_map[i] = -1;
- if (libevdev_has_event_code(devicep->internals->dev, EV_REL, i))
- devicep->relative_axis_map[i] = (int16_t)devicep->nrelative_axes++;
- }
- for (i = 0; i < FF_CNT; i++)
- if (libevdev_has_event_code(devicep->internals->dev, EV_FF, i))
- devicep->force_feedback_support[i / 8] |= (uint8_t)(1 << (i & 7));
+#define CREATE_MAPS(EVENTS, SINGULAR, PLURAL)\
+ do {\
+ /* Code-to-index map */\
+ r = ioctl(devicep->fd, EVIOCGBIT(EVENTS, sizeof(bits)), bits);\
+ if (r < 0)\
+ goto 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->nbuttons) {
- devicep->buttons = calloc(devicep->nbuttons, sizeof(*devicep->buttons));
- if (!devicep->buttons)
- goto fail;
- for (i = 0, n = 0; i < KEY_CNT; i++)
- if (devicep->button_map[i] >= 0)
- devicep->buttons[n++] = (uint16_t)i;
- }
if (devicep->nabsolute_axes) {
- devicep->absolute_axes = calloc(devicep->nabsolute_axes, sizeof(*devicep->absolute_axes));
- if (!devicep->absolute_axes)
+ devicep->internals->absinfo = calloc(devicep->nabsolute_axes, sizeof(*devicep->internals->absinfo));
+ if (!devicep->internals->absinfo)
goto fail;
- for (i = 0, n = 0; i < ABS_CNT; i++)
- if (devicep->absolute_axis_map[i] >= 0)
- devicep->absolute_axes[n++] = (uint16_t)i;
}
+ for (i = 0; i < devicep->nabsolute_axes; i++)
+ if (ioctl(devicep->fd, EVIOCGABS((unsigned int)devicep->absolute_axes[i]), &devicep->internals->absinfo[i]) < 0)
+ goto fail;
+
- if (devicep->nrelative_axes) {
- devicep->relative_axes = calloc(devicep->nrelative_axes, sizeof(*devicep->relative_axes));
- if (!devicep->relative_axes)
+ if (devicep->nbuttons) {
+ devicep->internals->buttons = calloc((devicep->nbuttons + 7) / 8, sizeof(*devicep->internals->buttons));
+ if (!devicep->internals->buttons)
goto fail;
- for (i = 0, n = 0; i < REL_CNT; i++)
- if (devicep->relative_axis_map[i] >= 0)
- devicep->relative_axes[n++] = (uint16_t)i;
+ r = ioctl(devicep->fd, EVIOCGKEY(sizeof(bits)), bits);
+ if (r < 0)
+ goto 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);
+ }
}
+
+ r = ioctl(devicep->fd, EVIOCGBIT(EV_FF, sizeof(bits)), bits);
+ if (r < 0)
+ goto 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;
+
+ /* TODO get actual KEY/ABS state */
+
return 0;
+fail_free_buf:
+ free(buf);
fail:
libgamepad_close_device(devicep);
return -1;