diff options
author | David Herrmann <dh.herrmann@gmail.com> | 2013-12-17 15:09:33 +0100 |
---|---|---|
committer | David Herrmann <dh.herrmann@gmail.com> | 2013-12-17 17:32:55 +0100 |
commit | d018ae6484024b0ade7f4721ddbd7d1d7646b680 (patch) | |
tree | d577f7b7958bd5be4ce21909908547ba26c934ef | |
parent | 39e6f32ad30ae5ab6cb22b5c74ec783f38b5dc49 (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.h | 49 | ||||
-rw-r--r-- | libevdev/libevdev-int.h | 4 | ||||
-rw-r--r-- | libevdev/libevdev-uinput.c | 4 | ||||
-rw-r--r-- | libevdev/libevdev.c | 133 | ||||
-rw-r--r-- | libevdev/libevdev.h | 31 | ||||
-rwxr-xr-x | libevdev/make-event-names.py | 10 | ||||
-rw-r--r-- | test/test-event-names.c | 4 | ||||
-rw-r--r-- | test/test-libevdev-has-event.c | 28 |
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); |