From 4d4293a65b604e0bf8d02dc729803f142fc665aa Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Fri, 9 Aug 2013 13:21:28 +1000 Subject: [PATCH] Add functions to toggle LEDs on the device Signed-off-by: Peter Hutterer Reviewed-by: Benjamin Tissoires --- libevdev/libevdev.c | 59 +++++++++++++ libevdev/libevdev.h | 46 ++++++++++ test/test-libevdev-has-event.c | 153 +++++++++++++++++++++++++++++++++ 3 files changed, 258 insertions(+) diff --git a/libevdev/libevdev.c b/libevdev/libevdev.c index bd0c58c..25d33e7 100644 --- a/libevdev/libevdev.c +++ b/libevdev/libevdev.c @@ -1179,3 +1179,62 @@ libevdev_get_repeat(struct libevdev *dev, int *delay, int *period) return 0; } + +int +libevdev_kernel_set_led_value(struct libevdev *dev, unsigned int code, enum EvdevLEDValues value) +{ + return libevdev_kernel_set_led_values(dev, code, value, -1); +} + +int +libevdev_kernel_set_led_values(struct libevdev *dev, ...) +{ + struct input_event ev[LED_MAX]; + enum EvdevLEDValues val; + va_list args; + int code; + int rc = 0; + size_t nleds = 0; + + memset(ev, 0, sizeof(ev)); + + va_start(args, dev); + code = va_arg(args, unsigned int); + while (code != -1) { + if (code > LED_MAX) { + rc = -EINVAL; + break; + } + val = va_arg(args, enum EvdevLEDValues); + if (val != LIBEVDEV_LED_ON && val != LIBEVDEV_LED_OFF) { + rc = -EINVAL; + break; + } + + if (libevdev_has_event_code(dev, EV_LED, code)) { + struct input_event *e = ev; + + while (e->type > 0 && e->code != code) + e++; + + if (e->type == 0) + nleds++; + e->type = EV_LED; + e->code = code; + e->value = (val == LIBEVDEV_LED_ON); + } + code = va_arg(args, unsigned int); + } + va_end(args); + + if (rc == 0 && nleds > 0) { + rc = write(libevdev_get_fd(dev), ev, nleds * sizeof(ev[0])); + if (rc > 0) { + while (nleds--) + update_led_state(dev, &ev[nleds]); + } + rc = (rc != -1) ? 0 : -errno; + } + + return rc; +} diff --git a/libevdev/libevdev.h b/libevdev/libevdev.h index 4c01847..db6a2ec 100644 --- a/libevdev/libevdev.h +++ b/libevdev/libevdev.h @@ -1146,6 +1146,52 @@ 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); + +enum EvdevLEDValues { + LIBEVDEV_LED_ON = 3, + LIBEVDEV_LED_OFF = 4, +}; + +/** + * @ingroup kernel + * + * Turn an LED on or off. Convenience function, if you need to modify multiple + * LEDs simultaneously, use libevdev_kernel_set_led_values() instead. + * + * @note enabling an LED requires write permissions on the device's file descriptor. + * + * @param dev The evdev device, already initialized with libevdev_set_fd() + * @param code The EV_LED event code to modify, one of LED_NUML, LED_CAPSL, ... + * @param value Specifies whether to turn the LED on or off + * @return zero on success, or a negative errno on failure + */ +int libevdev_kernel_set_led_value(struct libevdev *dev, unsigned int code, enum EvdevLEDValues value); + +/** + * @ingroup kernel + * + * Turn multiple LEDs on or off simultaneously. This function expects a pair + * of LED codes and values to set them to, terminated by a -1. For example, to + * switch the NumLock LED on but the CapsLock LED off, use: + * + * @code + * libevdev_kernel_set_led_values(dev, LED_NUML, LIBEVDEV_LED_ON, + * LED_CAPSL, LIBEVDEV_LED_OFF, + * -1); + * @endcode + * + * If any LED code or value is invalid, this function returns -EINVAL and no + * LEDs are modified. + * + * @note enabling an LED requires write permissions on the device's file descriptor. + * + * @param dev The evdev device, already initialized with libevdev_set_fd() + * @param ... A pair of LED_* event codes and enum EvdevLEDValues, followed by + * -1 to terminate the list. + * @return zero on success, or a negative errno on failure + */ +int libevdev_kernel_set_led_values(struct libevdev *dev, ...); + /** * @ingroup misc * diff --git a/test/test-libevdev-has-event.c b/test/test-libevdev-has-event.c index 307ea58..31a46ab 100644 --- a/test/test-libevdev-has-event.c +++ b/test/test-libevdev-has-event.c @@ -923,6 +923,153 @@ START_TEST(test_device_kernel_change_axis_invalid) } END_TEST +START_TEST(test_led_valid) +{ + struct uinput_device* uidev; + struct libevdev *dev; + int rc; + + rc = test_create_device(&uidev, &dev, + EV_LED, LED_NUML, + EV_LED, LED_CAPSL, + EV_LED, LED_COMPOSE, + -1); + ck_assert_msg(rc == 0, "Failed to create device: %s", strerror(-rc)); + + rc = libevdev_kernel_set_led_value(dev, LED_NUML, LIBEVDEV_LED_ON); + ck_assert_int_eq(rc, 0); + rc = libevdev_kernel_set_led_value(dev, LED_NUML, LIBEVDEV_LED_OFF); + ck_assert_int_eq(rc, 0); + + rc = libevdev_kernel_set_led_values(dev, + LED_NUML, LIBEVDEV_LED_OFF, + LED_CAPSL, LIBEVDEV_LED_ON, + LED_COMPOSE, LIBEVDEV_LED_OFF, + -1); + ck_assert_int_eq(rc, 0); + ck_assert_int_eq(0, libevdev_get_event_value(dev, EV_LED, LED_NUML)); + ck_assert_int_eq(1, libevdev_get_event_value(dev, EV_LED, LED_CAPSL)); + ck_assert_int_eq(0, libevdev_get_event_value(dev, EV_LED, LED_COMPOSE)); + + rc = libevdev_kernel_set_led_values(dev, + LED_NUML, LIBEVDEV_LED_ON, + LED_CAPSL, LIBEVDEV_LED_OFF, + LED_COMPOSE, LIBEVDEV_LED_ON, + -1); + ck_assert_int_eq(rc, 0); + ck_assert_int_eq(1, libevdev_get_event_value(dev, EV_LED, LED_NUML)); + ck_assert_int_eq(0, libevdev_get_event_value(dev, EV_LED, LED_CAPSL)); + ck_assert_int_eq(1, libevdev_get_event_value(dev, EV_LED, LED_COMPOSE)); + + /* make sure we ignore unset leds */ + rc = libevdev_kernel_set_led_values(dev, + LED_NUML, LIBEVDEV_LED_ON, + LED_CAPSL, LIBEVDEV_LED_OFF, + LED_SCROLLL, LIBEVDEV_LED_OFF, + LED_COMPOSE, LIBEVDEV_LED_ON, + -1); + ck_assert_int_eq(rc, 0); + ck_assert_int_eq(1, libevdev_get_event_value(dev, EV_LED, LED_NUML)); + ck_assert_int_eq(0, libevdev_get_event_value(dev, EV_LED, LED_CAPSL)); + ck_assert_int_eq(1, libevdev_get_event_value(dev, EV_LED, LED_COMPOSE)); + + libevdev_free(dev); + uinput_device_free(uidev); +} +END_TEST + +START_TEST(test_led_invalid) +{ + struct uinput_device* uidev; + struct libevdev *dev; + int rc; + + rc = test_create_device(&uidev, &dev, + EV_LED, LED_NUML, + EV_LED, LED_CAPSL, + EV_LED, LED_COMPOSE, + -1); + ck_assert_msg(rc == 0, "Failed to create device: %s", strerror(-rc)); + + rc = libevdev_kernel_set_led_value(dev, LED_MAX + 1, LIBEVDEV_LED_ON); + ck_assert_int_eq(rc, -EINVAL); + + rc = libevdev_kernel_set_led_value(dev, LED_NUML, LIBEVDEV_LED_OFF + 1); + ck_assert_int_eq(rc, -EINVAL); + + rc = libevdev_kernel_set_led_value(dev, LED_SCROLLL, LIBEVDEV_LED_ON); + ck_assert_int_eq(rc, 0); + + rc = libevdev_kernel_set_led_values(dev, + LED_NUML, LIBEVDEV_LED_OFF + 1, + -1); + ck_assert_int_eq(rc, -EINVAL); + + rc = libevdev_kernel_set_led_values(dev, + LED_MAX + 1, LIBEVDEV_LED_ON, + LED_NUML, LIBEVDEV_LED_OFF + 1, + -1); + ck_assert_int_eq(rc, -EINVAL); + + rc = libevdev_kernel_set_led_values(dev, + LED_SCROLLL, LIBEVDEV_LED_OFF, + -1); + ck_assert_int_eq(rc, 0); + + libevdev_free(dev); + uinput_device_free(uidev); +} +END_TEST + +START_TEST(test_led_same) +{ + struct uinput_device* uidev; + struct libevdev *dev; + int rc; + + rc = test_create_device(&uidev, &dev, + EV_LED, LED_NUML, + EV_LED, LED_CAPSL, + EV_LED, LED_COMPOSE, + -1); + ck_assert_msg(rc == 0, "Failed to create device: %s", strerror(-rc)); + + rc = libevdev_kernel_set_led_values(dev, + LED_NUML, LIBEVDEV_LED_OFF, + LED_NUML, LIBEVDEV_LED_ON, + LED_NUML, LIBEVDEV_LED_OFF, + LED_NUML, LIBEVDEV_LED_ON, + LED_NUML, LIBEVDEV_LED_OFF, + LED_NUML, LIBEVDEV_LED_ON, + LED_NUML, LIBEVDEV_LED_OFF, + LED_NUML, LIBEVDEV_LED_ON, + LED_NUML, LIBEVDEV_LED_OFF, + LED_NUML, LIBEVDEV_LED_ON, + LED_NUML, LIBEVDEV_LED_OFF, + LED_NUML, LIBEVDEV_LED_ON, + LED_NUML, LIBEVDEV_LED_OFF, + LED_NUML, LIBEVDEV_LED_ON, + LED_NUML, LIBEVDEV_LED_OFF, + LED_NUML, LIBEVDEV_LED_ON, + LED_NUML, LIBEVDEV_LED_OFF, + LED_NUML, LIBEVDEV_LED_ON, + LED_NUML, LIBEVDEV_LED_OFF, + LED_NUML, LIBEVDEV_LED_ON, + LED_NUML, LIBEVDEV_LED_OFF, + LED_NUML, LIBEVDEV_LED_ON, + LED_NUML, LIBEVDEV_LED_OFF, + LED_NUML, LIBEVDEV_LED_ON, + /* more than LED_CNT */ + -1); + ck_assert_int_eq(rc, 0); + ck_assert_int_eq(1, libevdev_get_event_value(dev, EV_LED, LED_NUML)); + ck_assert_int_eq(0, libevdev_get_event_value(dev, EV_LED, LED_CAPSL)); + ck_assert_int_eq(0, libevdev_get_event_value(dev, EV_LED, LED_COMPOSE)); + + libevdev_free(dev); + uinput_device_free(uidev); +} +END_TEST Suite * libevdev_has_event_test(void) { @@ -971,6 +1118,12 @@ libevdev_has_event_test(void) tcase_add_test(tc, test_device_kernel_change_axis_invalid); suite_add_tcase(s, tc); + tc = tcase_create("led manipulation"); + tcase_add_test(tc, test_led_valid); + tcase_add_test(tc, test_led_invalid); + tcase_add_test(tc, test_led_same); + suite_add_tcase(s, tc); + return s; } -- 2.34.1