Add functions to toggle LEDs on the device

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
Reviewed-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
This commit is contained in:
Peter Hutterer 2013-08-09 13:21:28 +10:00
parent bfb6c1c6c5
commit 4d4293a65b
3 changed files with 258 additions and 0 deletions

View file

@ -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;
}

View file

@ -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
*

View file

@ -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;
}