[PATCH libevdev 5/7] Add functions to toggle LEDs on the device

Peter Hutterer peter.hutterer at who-t.net
Tue Aug 13 17:50:52 PDT 2013


Signed-off-by: Peter Hutterer <peter.hutterer at who-t.net>
---
 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;
 }
 
-- 
1.8.2.1



More information about the Input-tools mailing list