summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Herrmann <dh.herrmann@gmail.com>2013-12-17 15:09:33 +0100
committerDavid Herrmann <dh.herrmann@gmail.com>2013-12-17 17:32:55 +0100
commitd018ae6484024b0ade7f4721ddbd7d1d7646b680 (patch)
treed577f7b7958bd5be4ce21909908547ba26c934ef
parent39e6f32ad30ae5ab6cb22b5c74ec783f38b5dc49 (diff)
Add support for ABS2abs2
The current ABS API is limited in that it doesn't support any new ABS_* codes. Therefore, starting with linux-3.14-rc1 we will have a new ABS2 API. This patch updates linux/input.h to include the new definitions and makes libevdev use ABS2 if available. Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
-rw-r--r--include/linux/input.h49
-rw-r--r--libevdev/libevdev-int.h4
-rw-r--r--libevdev/libevdev-uinput.c4
-rw-r--r--libevdev/libevdev.c133
-rw-r--r--libevdev/libevdev.h31
-rwxr-xr-xlibevdev/make-event-names.py10
-rw-r--r--test/test-event-names.c4
-rw-r--r--test/test-libevdev-has-event.c28
8 files changed, 215 insertions, 48 deletions
diff --git a/include/linux/input.h b/include/linux/input.h
index e37293b..bccd829 100644
--- a/include/linux/input.h
+++ b/include/linux/input.h
@@ -30,7 +30,7 @@ struct input_event {
* Protocol version.
*/
-#define EV_VERSION 0x010001
+#define EV_VERSION 0x010002
/*
* IOCTLs (0x00 - 0x7f)
@@ -72,6 +72,30 @@ struct input_absinfo {
};
/**
+ * struct input_absinfo2 - used by EVIOC[G/S]ABS2 ioctls
+ * @code: First ABS code to query
+ * @cnt: Number of ABS codes to query starting at @code
+ * @info: #@cnt absinfo structures to get/set abs parameters for all codes
+ *
+ * This structure is used by the new EVIOC[G/S]ABS2 ioctls which
+ * do the same as the old EVIOC[G/S]ABS ioctls but avoid encoding
+ * the ABS code in the ioctl number. This allows a much wider
+ * range of ABS codes. Furthermore, it allows to query multiple codes with a
+ * single call.
+ *
+ * Note that this silently drops any requests to set ABS_MT_SLOT. Hence, it is
+ * allowed to call this with code=0 cnt=ABS_CNT2. Furthermore, retrieving
+ * invalid codes returns all 0, setting them does nothing. So you must check
+ * with EVIOCGBIT first if you want reliable results. This behavior is needed
+ * to allow forward compatibility to new ABS codes.
+ */
+struct input_absinfo2 {
+ __u32 code;
+ __u32 cnt;
+ struct input_absinfo info[1];
+};
+
+/**
* struct input_keymap_entry - used by EVIOCGKEYCODE/EVIOCSKEYCODE ioctls
* @scancode: scancode represented in machine-endian form.
* @len: length of the scancode that resides in @scancode buffer.
@@ -151,6 +175,8 @@ struct input_keymap_entry {
#define EVIOCGRAB _IOW('E', 0x90, int) /* Grab/Release device */
#define EVIOCREVOKE _IOW('E', 0x91, int) /* Revoke device access */
+#define EVIOCGABS2 _IOR('E', 0x92, struct input_absinfo2) /* get abs value/limits */
+#define EVIOCSABS2 _IOW('E', 0x93, struct input_absinfo2) /* set abs value/limits */
#define EVIOCSCLOCKID _IOW('E', 0xa0, int) /* Set clockid to be used for timestamps */
@@ -833,10 +859,29 @@ struct input_keymap_entry {
#define ABS_MT_TOOL_X 0x3c /* Center X tool position */
#define ABS_MT_TOOL_Y 0x3d /* Center Y tool position */
-
+/*
+ * ABS_MAX/CNT is limited to a maximum of 0x3f due to the design of EVIOCGABS
+ * and EVIOCSABS ioctls. Other kernel APIs like uinput also hardcoded it. Do
+ * not modify this value and instead use the extended ABS_MAX2/CNT2 API.
+ */
#define ABS_MAX 0x3f
#define ABS_CNT (ABS_MAX+1)
+#define ABS_ACCEL_X 0x40 /* Accelerometer X axis */
+#define ABS_ACCEL_Y 0x41 /* Accelerometer Y axis */
+#define ABS_ACCEL_Z 0x42 /* Accelerometer Z axis */
+#define ABS_GYRO_X 0x43 /* Gyroscope X axis */
+#define ABS_GYRO_Y 0x44 /* Gyroscope Y axis */
+#define ABS_GYRO_Z 0x45 /* Gyroscope Z axis */
+
+/*
+ * Due to API restrictions the legacy evdev API only supports ABS values up to
+ * ABS_MAX/CNT. Use the extended *ABS2 ioctls to operate on any ABS values in
+ * between ABS_MAX and ABS_MAX2.
+ */
+#define ABS_MAX2 0x4f
+#define ABS_CNT2 (ABS_MAX2+1)
+
/*
* Switch events
*/
diff --git a/libevdev/libevdev-int.h b/libevdev/libevdev-int.h
index 847fe56..17c84c1 100644
--- a/libevdev/libevdev-int.h
+++ b/libevdev/libevdev-int.h
@@ -84,7 +84,7 @@ struct libevdev {
unsigned long props[NLONGS(INPUT_PROP_CNT)];
unsigned long key_bits[NLONGS(KEY_CNT)];
unsigned long rel_bits[NLONGS(REL_CNT)];
- unsigned long abs_bits[NLONGS(ABS_CNT)];
+ unsigned long abs_bits[NLONGS(ABS_CNT2)];
unsigned long led_bits[NLONGS(LED_CNT)];
unsigned long msc_bits[NLONGS(MSC_CNT)];
unsigned long sw_bits[NLONGS(SW_CNT)];
@@ -94,7 +94,7 @@ struct libevdev {
unsigned long key_values[NLONGS(KEY_CNT)];
unsigned long led_values[NLONGS(LED_CNT)];
unsigned long sw_values[NLONGS(SW_CNT)];
- struct input_absinfo abs_info[ABS_CNT];
+ struct input_absinfo abs_info[ABS_CNT2];
int mt_slot_vals[MAX_SLOTS][ABS_MT_CNT];
int num_slots; /**< valid slots in mt_slot_vals */
int current_slot;
diff --git a/libevdev/libevdev-uinput.c b/libevdev/libevdev-uinput.c
index ea9cf78..d26b87a 100644
--- a/libevdev/libevdev-uinput.c
+++ b/libevdev/libevdev-uinput.c
@@ -108,6 +108,10 @@ set_evbits(const struct libevdev *dev, int fd, struct uinput_user_dev *uidev)
if (!libevdev_has_event_code(dev, type, code))
continue;
+ /* legacy uinput does not support ABS2 */
+ if (type == EV_ABS && code > ABS_MAX)
+ break;
+
rc = ioctl(fd, uinput_bit, code);
if (rc == -1)
goto out;
diff --git a/libevdev/libevdev.c b/libevdev/libevdev.c
index 8a37204..0623977 100644
--- a/libevdev/libevdev.c
+++ b/libevdev/libevdev.c
@@ -194,6 +194,66 @@ libevdev_change_fd(struct libevdev *dev, int fd)
return 0;
}
+static int
+fetch_abs_info(struct libevdev *dev, int fd)
+{
+ struct input_absinfo2 *abs_info2 = NULL;
+ int rc, i;
+
+ if (!bit_is_set(dev->bits, EV_ABS))
+ return 0;
+
+ if (libevdev_kernel_has_abs2(dev)) {
+ abs_info2 = malloc(sizeof(*abs_info2) +
+ ABS_CNT2 * sizeof(*abs_info2->info));
+ if (!abs_info2) {
+ rc = -1;
+ goto out;
+ }
+
+ abs_info2->code = 0;
+ abs_info2->cnt = ABS_CNT2;
+
+ rc = ioctl(fd, EVIOCGABS2, abs_info2);
+ if (rc < 0)
+ goto out;
+ }
+
+ for (i = 0; i <= ABS_MAX2; i++) {
+ if (bit_is_set(dev->abs_bits, i)) {
+ struct input_absinfo abs_info;
+
+ if (abs_info2)
+ abs_info = abs_info2->info[i];
+ else if (i > ABS_MAX)
+ break;
+ else {
+ rc = ioctl(fd, EVIOCGABS(i), &abs_info);
+ if (rc < 0)
+ goto out;
+ }
+
+ dev->abs_info[i] = abs_info;
+
+ /* devices with ABS_MT_SLOT - 1 aren't MT devices,
+ see the documentation for multitouch-related
+ functions for more details */
+ if (i == ABS_MT_SLOT &&
+ !libevdev_has_event_code(dev, EV_ABS, ABS_MT_SLOT - 1)) {
+ dev->num_slots = abs_info.maximum + 1;
+ dev->current_slot = abs_info.value;
+ }
+
+ }
+ }
+
+ rc = 0;
+
+out:
+ free(abs_info2);
+ return rc ? -errno : 0;
+}
+
LIBEVDEV_EXPORT int
libevdev_set_fd(struct libevdev* dev, int fd)
{
@@ -325,26 +385,9 @@ libevdev_set_fd(struct libevdev* dev, int fd)
goto out;
}
- for (i = ABS_X; i <= ABS_MAX; i++) {
- if (bit_is_set(dev->abs_bits, i)) {
- struct input_absinfo abs_info;
- rc = ioctl(fd, EVIOCGABS(i), &abs_info);
- if (rc < 0)
- goto out;
-
- dev->abs_info[i] = abs_info;
-
- /* devices with ABS_MT_SLOT - 1 aren't MT devices,
- see the documentation for multitouch-related
- functions for more details */
- if (i == ABS_MT_SLOT &&
- !libevdev_has_event_code(dev, EV_ABS, ABS_MT_SLOT - 1)) {
- dev->num_slots = abs_info.maximum + 1;
- dev->current_slot = abs_info.value;
- }
-
- }
- }
+ rc = fetch_abs_info(dev, fd);
+ if (rc < 0)
+ goto out;
dev->fd = fd;
sync_mt_state(dev, 0);
@@ -468,10 +511,27 @@ out:
static int
sync_abs_state(struct libevdev *dev)
{
+ struct input_absinfo2 *abs_info2 = NULL;
int rc;
int i;
- for (i = ABS_X; i < ABS_CNT; i++) {
+ if (libevdev_kernel_has_abs2(dev)) {
+ abs_info2 = malloc(sizeof(*abs_info2) +
+ ABS_CNT2 * sizeof(*abs_info2->info));
+ if (!abs_info2) {
+ rc = -1;
+ goto out;
+ }
+
+ abs_info2->code = 0;
+ abs_info2->cnt = ABS_CNT2;
+
+ rc = ioctl(dev->fd, EVIOCGABS2, abs_info2);
+ if (rc < 0)
+ goto out;
+ }
+
+ for (i = 0; i <= ABS_MAX2; i++) {
struct input_absinfo abs_info;
if (i >= ABS_MT_MIN && i <= ABS_MT_MAX)
@@ -480,9 +540,15 @@ sync_abs_state(struct libevdev *dev)
if (!bit_is_set(dev->abs_bits, i))
continue;
- rc = ioctl(dev->fd, EVIOCGABS(i), &abs_info);
- if (rc < 0)
- goto out;
+ if (abs_info2)
+ abs_info = abs_info2->info[i];
+ else if (i > ABS_MAX)
+ break;
+ else {
+ rc = ioctl(dev->fd, EVIOCGABS(i), &abs_info);
+ if (rc < 0)
+ goto out;
+ }
if (dev->abs_info[i].value != abs_info.value) {
struct input_event *ev = queue_push(dev);
@@ -494,6 +560,7 @@ sync_abs_state(struct libevdev *dev)
rc = 0;
out:
+ free(abs_info2);
return rc ? -errno : 0;
}
@@ -656,7 +723,7 @@ update_abs_state(struct libevdev *dev, const struct input_event *e)
if (!libevdev_has_event_type(dev, EV_ABS))
return 1;
- if (e->code > ABS_MAX)
+ if (e->code > ABS_MAX2)
return 1;
if (e->code >= ABS_MT_MIN && e->code <= ABS_MT_MAX)
@@ -1254,10 +1321,16 @@ libevdev_kernel_set_abs_info(struct libevdev *dev, unsigned int code, const stru
} else if (dev->fd < 0)
return -EBADF;
- if (code > ABS_MAX)
+ if (code > ABS_MAX2)
return -EINVAL;
- rc = ioctl(dev->fd, EVIOCSABS(code), abs);
+ if (code > ABS_MAX) {
+ struct input_absinfo2 info = { .code = code, .cnt = 1, .info = { *abs } };
+
+ rc = ioctl(dev->fd, EVIOCSABS2, &info);
+ } else
+ rc = ioctl(dev->fd, EVIOCSABS(code), abs);
+
if (rc < 0)
rc = -errno;
else
@@ -1367,6 +1440,12 @@ libevdev_get_repeat(const struct libevdev *dev, int *delay, int *period)
}
LIBEVDEV_EXPORT int
+libevdev_kernel_has_abs2(const struct libevdev *dev)
+{
+ return dev->driver_version >= 0x010002;
+}
+
+LIBEVDEV_EXPORT int
libevdev_kernel_set_led_value(struct libevdev *dev, unsigned int code, enum libevdev_led_value value)
{
return libevdev_kernel_set_led_values(dev, code, value, -1);
diff --git a/libevdev/libevdev.h b/libevdev/libevdev.h
index 44aa3a6..48132c7 100644
--- a/libevdev/libevdev.h
+++ b/libevdev/libevdev.h
@@ -245,6 +245,14 @@ extern "C" {
* A kernel without SYN_DROPPED, won't send the event. libevdev_next_event()
* will never require the switch to sync mode.
*
+ * ABS2 behavior
+ * =============
+ * The old ABS API was limited to a fixed number of ABS_* codes. The ABS2 API
+ * was introduced to fix this. If a running kernel does not support ABS2, any
+ * operation on ABS2 codes (ABS_* codes between ABS_MAX and ABS_MAX2) will
+ * simply fail with EINVAL. You can test for ABS2 support at runtime via
+ * libevdev_kernel_has_abs2().
+ *
*/
/**
@@ -295,6 +303,11 @@ extern "C" {
* libevdev_get_abs_info()</dd>
* <dt>EVIOCSABS:</dt>
* <dd>supported, see libevdev_kernel_set_abs_info()</dd>
+ * <dt>EVIOCGABS2:</dt>
+ * <dd>supported, see libevdev_has_event_code(), libevdev_get_event_value(),
+ * libevdev_get_abs_info()</dd>
+ * <dt>EVIOCSABS2:</dt>
+ * <dd>supported, see libevdev_kernel_set_abs_info()</dd>
* <dt>EVIOCSFF:</dt>
* <dd>currently not supported</dd>
* <dt>EVIOCRMFF:</dt>
@@ -1365,6 +1378,22 @@ int libevdev_disable_event_code(struct libevdev *dev, unsigned int type, unsigne
*/
int libevdev_kernel_set_abs_info(struct libevdev *dev, unsigned int code, const struct input_absinfo *abs);
+/**
+ * @ingroup kernel
+ *
+ * Test whether the running kernel supports ABS2. If it doesn't, you cannot make
+ * use of any ABS2 codes (all ABS_* codes between ABS_MAX and ABS_MAX2). Note
+ * that if it is supported, libevdev handles this transparently.
+ *
+ * In most situations, this call isn't needed. Simply try using your ABS codes
+ * and fail if you require them. However, if you have a proper fallback, you can
+ * use this call to make sure that ABS2 is supported.
+ *
+ * @param dev The evdev device, already initialized with libevdev_set_fd()
+ * @return 1 if ABS2 is supported, 0 if not or if the given evdev device
+ * wasn't initialized, yet.
+ */
+int libevdev_kernel_has_abs2(const struct libevdev *dev);
/**
* @ingroup kernel
@@ -1525,7 +1554,7 @@ const char* libevdev_property_get_name(unsigned int prop);
* @param type The event type to return the maximum for (EV_ABS, EV_REL, etc.). No max is defined for
* EV_SYN.
*
- * @return The max value defined for the given event type, e.g. ABS_MAX for a type of EV_ABS, or -1
+ * @return The max value defined for the given event type, e.g. REL_MAX for a type of EV_REL, or -1
* for an invalid type.
*
* @note The max value is compiled into libevdev. If the kernel changes the
diff --git a/libevdev/make-event-names.py b/libevdev/make-event-names.py
index 8f65ab8..4c601f3 100755
--- a/libevdev/make-event-names.py
+++ b/libevdev/make-event-names.py
@@ -63,7 +63,10 @@ names = [
def print_bits(bits, prefix):
if not hasattr(bits, prefix):
return
- print("static const char * const %s_map[%s_MAX + 1] = {" % (prefix, prefix.upper()))
+ if prefix == "abs":
+ print("static const char * const %s_map[%s_MAX2 + 1] = {" % (prefix, prefix.upper()))
+ else:
+ print("static const char * const %s_map[%s_MAX + 1] = {" % (prefix, prefix.upper()))
for val, name in list(getattr(bits, prefix).items()):
print(" [%s] = \"%s\"," % (name, name))
if prefix == "key":
@@ -105,7 +108,10 @@ def print_map(bits):
for prefix in prefixes:
if prefix == "BTN_" or prefix == "EV_" or prefix == "INPUT_PROP_":
continue
- print(" [EV_%s] = %s_MAX," % (prefix[:-1], prefix[:-1]))
+ if prefix == "ABS_":
+ print(" [EV_%s] = %s_MAX2," % (prefix[:-1], prefix[:-1]))
+ else:
+ print(" [EV_%s] = %s_MAX," % (prefix[:-1], prefix[:-1]))
print("};")
print("#pragma GCC diagnostic pop /* \"-Woverride-init\" */")
print("")
diff --git a/test/test-event-names.c b/test/test-event-names.c
index d469b15..696ae03 100644
--- a/test/test-event-names.c
+++ b/test/test-event-names.c
@@ -26,7 +26,7 @@
START_TEST(test_limits)
{
ck_assert(libevdev_event_type_get_name(EV_MAX + 1) == NULL);
- ck_assert(libevdev_event_code_get_name(EV_ABS, ABS_MAX + 1) == NULL);
+ ck_assert(libevdev_event_code_get_name(EV_ABS, ABS_MAX2 + 1) == NULL);
ck_assert(libevdev_event_code_get_name(EV_REL, REL_MAX + 1) == NULL);
ck_assert(libevdev_event_code_get_name(EV_KEY, KEY_MAX + 1) == NULL);
ck_assert(libevdev_event_code_get_name(EV_LED, LED_MAX + 1) == NULL);
@@ -210,7 +210,7 @@ END_TEST
START_TEST(test_event_type_max)
{
- ck_assert_int_eq(libevdev_event_type_get_max(EV_ABS), ABS_MAX);
+ ck_assert_int_eq(libevdev_event_type_get_max(EV_ABS), ABS_MAX2);
ck_assert_int_eq(libevdev_event_type_get_max(EV_REL), REL_MAX);
ck_assert_int_eq(libevdev_event_type_get_max(EV_KEY), KEY_MAX);
diff --git a/test/test-libevdev-has-event.c b/test/test-libevdev-has-event.c
index 8beb8c4..0b55241 100644
--- a/test/test-libevdev-has-event.c
+++ b/test/test-libevdev-has-event.c
@@ -128,6 +128,8 @@ START_TEST(test_event_codes)
for (code = 1; code < max; code += 10) {
if (*evbit == EV_ABS) {
struct input_absinfo abs = { code, 0, 2, 0, 0, 0};
+ if (code > ABS_MAX)
+ break;
rc = test_create_abs_device(&uidev, &dev,
1, &abs,
-1);
@@ -492,7 +494,9 @@ START_TEST(test_device_name)
ck_assert_int_eq(libevdev_get_id_vendor(dev), ids.vendor);
ck_assert_int_eq(libevdev_get_id_product(dev), ids.product);
ck_assert_int_eq(libevdev_get_id_version(dev), ids.version);
- ck_assert_int_eq(libevdev_get_driver_version(dev), EV_VERSION);
+ rc = libevdev_get_driver_version(dev);
+ ck_assert_int_ge(rc, 0x010001);
+ ck_assert_int_le(rc, 0x010002);
uinput_device_free(uidev);
libevdev_free(dev);
@@ -620,12 +624,12 @@ START_TEST(test_device_get_abs_info)
rc = libevdev_new_from_fd(uinput_device_get_fd(uidev), &dev);
ck_assert_msg(rc == 0, "Failed to init device: %s", strerror(-rc));;
- ck_assert_int_eq(libevdev_get_abs_minimum(dev, ABS_MAX + 1), 0);
- ck_assert_int_eq(libevdev_get_abs_maximum(dev, ABS_MAX + 1), 0);
- ck_assert_int_eq(libevdev_get_abs_fuzz(dev, ABS_MAX + 1), 0);
- ck_assert_int_eq(libevdev_get_abs_flat(dev, ABS_MAX + 1), 0);
- ck_assert_int_eq(libevdev_get_abs_resolution(dev, ABS_MAX + 1), 0);
- ck_assert(!libevdev_get_abs_info(dev, ABS_MAX + 1));
+ ck_assert_int_eq(libevdev_get_abs_minimum(dev, ABS_MAX2 + 1), 0);
+ ck_assert_int_eq(libevdev_get_abs_maximum(dev, ABS_MAX2 + 1), 0);
+ ck_assert_int_eq(libevdev_get_abs_fuzz(dev, ABS_MAX2 + 1), 0);
+ ck_assert_int_eq(libevdev_get_abs_flat(dev, ABS_MAX2 + 1), 0);
+ ck_assert_int_eq(libevdev_get_abs_resolution(dev, ABS_MAX2 + 1), 0);
+ ck_assert(!libevdev_get_abs_info(dev, ABS_MAX2 + 1));
ck_assert_int_eq(libevdev_get_abs_minimum(dev, ABS_X), 0);
ck_assert_int_eq(libevdev_get_abs_maximum(dev, ABS_X), 1000);
@@ -804,8 +808,8 @@ START_TEST(test_device_enable_bit_invalid)
-1);
ck_assert_msg(rc == 0, "Failed to create device: %s", strerror(-rc));
- ck_assert_int_eq(libevdev_enable_event_code(dev, EV_ABS, ABS_MAX + 1, &abs), -1);
- ck_assert_int_eq(libevdev_enable_event_code(dev, EV_MAX + 1, ABS_MAX + 1, &abs), -1);
+ ck_assert_int_eq(libevdev_enable_event_code(dev, EV_ABS, ABS_MAX2 + 1, &abs), -1);
+ ck_assert_int_eq(libevdev_enable_event_code(dev, EV_MAX + 1, ABS_MAX2 + 1, &abs), -1);
ck_assert_int_eq(libevdev_enable_event_type(dev, EV_MAX + 1), -1);
/* there's a gap between EV_SW and EV_LED */
ck_assert_int_eq(libevdev_enable_event_type(dev, EV_LED - 1), -1);
@@ -881,8 +885,8 @@ START_TEST(test_device_disable_bit_invalid)
/* there's a gap between EV_SW and EV_LED */
ck_assert_int_eq(libevdev_disable_event_type(dev, EV_LED - 1), -1);
ck_assert_int_eq(libevdev_disable_event_code(dev, EV_LED - 1, 0), -1);
- ck_assert_int_eq(libevdev_disable_event_code(dev, EV_ABS, ABS_MAX + 1), -1);
- ck_assert_int_eq(libevdev_disable_event_code(dev, EV_MAX + 1, ABS_MAX + 1), -1);
+ ck_assert_int_eq(libevdev_disable_event_code(dev, EV_ABS, ABS_MAX2 + 1), -1);
+ ck_assert_int_eq(libevdev_disable_event_code(dev, EV_MAX + 1, ABS_MAX2 + 1), -1);
ck_assert_int_eq(libevdev_disable_event_type(dev, EV_MAX + 1), -1);
ck_assert_int_eq(libevdev_disable_event_type(dev, EV_SYN), -1);
ck_assert_int_eq(libevdev_disable_event_code(dev, EV_SYN, SYN_REPORT), -1);
@@ -977,7 +981,7 @@ START_TEST(test_device_kernel_change_axis_invalid)
rc = libevdev_new_from_fd(uinput_device_get_fd(uidev), &dev);
ck_assert_msg(rc == 0, "Failed to init device: %s", strerror(-rc));;
- rc = libevdev_kernel_set_abs_info(dev, ABS_MAX + 1, &abs);
+ rc = libevdev_kernel_set_abs_info(dev, ABS_MAX2 + 1, &abs);
ck_assert_int_eq(rc, -EINVAL);
libevdev_free(dev);