Skip to content

Instantly share code, notes, and snippets.

@dvdhrm
Created March 28, 2013 11:51
Show Gist options
  • Select an option

  • Save dvdhrm/5262589 to your computer and use it in GitHub Desktop.

Select an option

Save dvdhrm/5262589 to your computer and use it in GitHub Desktop.
linux-input self-tests
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <linux/input.h>
#include <linux/uinput.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
/* name of the uinput device to create */
static const char *conf_devname = "kernel_selftest_device";
/* max number of /dev/input/eventX devices to test for $conf_devname */
static unsigned int conf_maxdev = 100;
/* event bits for uinput device */
static __u16 *conf_bits[EV_CNT] = {
[EV_KEY] = (__u16[]) { KEY_A, KEY_B, KEY_C, KEY_D, ~0 },
[EV_ABS] = (__u16[]) { ABS_X, ABS_Y, ABS_Z, ~0 },
};
/* ABS parameters for uinput device */
static const __s32 conf_absmax[ABS_CNT] = { };
static const __s32 conf_absmin[ABS_CNT] = { };
static const __s32 conf_absfuzz[ABS_CNT] = { };
static const __s32 conf_absflat[ABS_CNT] = { };
static void die(const char *format, ...)
{
va_list list;
va_start(list, format);
vfprintf(stderr, format, list);
va_end(list);
exit(1);
}
static long type2ioc(__u16 type)
{
switch (type) {
case EV_KEY: return UI_SET_KEYBIT;
case EV_REL: return UI_SET_RELBIT;
case EV_ABS: return UI_SET_ABSBIT;
case EV_MSC: return UI_SET_MSCBIT;
case EV_LED: return UI_SET_LEDBIT;
case EV_SND: return UI_SET_SNDBIT;
case EV_FF: return UI_SET_FFBIT;
case EV_SW: return UI_SET_SWBIT;
default: return 0;
}
}
static int uinput_create(__u16 **bits, const __s32 *absmax,
const __s32 *absmin, const __s32 *absfuzz,
const __s32 *absflat)
{
int ret, j, i, fd;
struct uinput_user_dev udev;
long ioc;
fd = open("/dev/uinput", O_RDWR | O_CLOEXEC | O_NONBLOCK);
if (fd < 0)
die("cannot open uinput device (%d:%d): %m\n", fd, errno);
memset(&udev, 0, sizeof(udev));
strncpy(udev.name, conf_devname, UINPUT_MAX_NAME_SIZE);
udev.id.bustype = BUS_VIRTUAL;
memcpy(udev.absmax, absmax, sizeof(udev.absmax));
memcpy(udev.absmin, absmin, sizeof(udev.absmin));
memcpy(udev.absfuzz, absfuzz, sizeof(udev.absfuzz));
memcpy(udev.absflat, absflat, sizeof(udev.absflat));
ret = write(fd, &udev, sizeof(udev));
if (ret != sizeof(udev))
die("cannot register uinput device (%d:%d): %m\n", ret, errno);
for (j = 0; j <= EV_MAX; ++j) {
if (!bits[j])
continue;
ioc = type2ioc(j);
if (!ioc)
die("invalid uinput EV flag %d\n", j);
ret = ioctl(fd, UI_SET_EVBIT, j);
if (ret)
die("cannot set uinput EV flag %d (%d:%d): %m\n",
j, ret, errno);
for (i = 0; bits[j][i] != (__u16)~0; ++i) {
ret = ioctl(fd, ioc, bits[j][i]);
if (ret)
die("cannot set uinput bit %d:%d (%d:%d): %m\n",
j, (int)bits[j][i], ret, errno);
}
}
ret = ioctl(fd, UI_DEV_CREATE);
if (ret)
die("cannot create uinput device (%d:%d): %m\n", ret, errno);
return fd;
}
static int uinput_find()
{
char buf[128];
unsigned int i;
int fd, ret;
for (i = 0; i < conf_maxdev; ++i) {
sprintf(buf, "/dev/input/event%u", i);
fd = open(buf, O_RDWR | O_CLOEXEC | O_NONBLOCK);
if (fd < 0)
continue;
ret = ioctl(fd, EVIOCGNAME(sizeof(buf)), buf);
if (ret > 0 && !strcmp(buf, conf_devname))
return fd;
close(fd);
}
die("cannot find own uinput device in /etc/input/\n");
return -1;
}
static void test_send_one(int ufd, const struct input_event *ev)
{
int ret;
ret = write(ufd, (void*)ev, sizeof(*ev));
if (ret != sizeof(*ev))
die("cannot send event %d:%d:%d (%d:%d): %m\n",
ev->type, ev->code, ev->value, ret, errno);
}
static void test_send(int ufd, const struct input_event *buf, unsigned int num)
{
for ( ; num--; ++buf)
test_send_one(ufd, buf);
}
static void test_recv_one(int fd, const struct input_event *ev)
{
int ret;
struct input_event buf;
ret = read(fd, (void*)&buf, sizeof(buf));
if (ret != sizeof(buf))
die("cannot recv event %d:%d:%d (%d:%d): %m\n",
ev->type, ev->code, ev->value, ret, errno);
if (ev->type != buf.type ||
ev->code != buf.code ||
ev->value != buf.value)
die("received unexpected event %d:%d:%d != %d:%d:%d\n",
buf.type, buf.code, buf.value,
ev->type, ev->code, ev->value);
}
static void test_recv(int fd, const struct input_event *buf, unsigned int num)
{
for ( ; num--; ++buf)
test_recv_one(fd, buf);
}
static void test_EVIOCGKEY(int fd, bool invalid)
{
unsigned char buf[(KEY_CNT - 1) / CHAR_BIT + 1];
int ret;
ret = ioctl(fd, EVIOCGKEY(sizeof(buf)), invalid ? NULL : (void*)buf);
if (!invalid && ret != sizeof(buf))
die("unexpected EVIOCGKEY return value %d != %d (%d): %m\n",
ret, sizeof(buf), errno);
}
static const struct input_event ev_test1[] = {
{ .type = EV_KEY, .code = KEY_A, .value = 1 },
{ .type = EV_KEY, .code = KEY_B, .value = 1 },
{ .type = EV_KEY, .code = KEY_C, .value = 1 },
{ .type = EV_KEY, .code = KEY_A, .value = 0 },
{ .type = EV_KEY, .code = KEY_B, .value = 0 },
{ .type = EV_KEY, .code = KEY_C, .value = 0 },
{ .type = EV_SYN, .code = SYN_REPORT },
};
static const unsigned int num_test1 = sizeof(ev_test1) / sizeof(*ev_test1);
static const struct input_event ev_test2[] = {
{ .type = EV_KEY, .code = KEY_A, .value = 1 },
{ .type = EV_KEY, .code = KEY_B, .value = 1 },
{ .type = EV_KEY, .code = KEY_C, .value = 1 },
{ .type = EV_KEY, .code = KEY_D, .value = 1 },
{ .type = EV_KEY, .code = KEY_D, .value = 0 },
{ .type = EV_KEY, .code = KEY_C, .value = 0 },
{ .type = EV_KEY, .code = KEY_B, .value = 0 },
{ .type = EV_KEY, .code = KEY_A, .value = 0 },
{ .type = EV_SYN, .code = SYN_REPORT },
{ .type = EV_KEY, .code = KEY_A, .value = 1 },
{ .type = EV_KEY, .code = KEY_B, .value = 1 },
{ .type = EV_KEY, .code = KEY_C, .value = 1 },
{ .type = EV_KEY, .code = KEY_D, .value = 1 },
{ .type = EV_KEY, .code = KEY_D, .value = 0 },
{ .type = EV_KEY, .code = KEY_C, .value = 0 },
{ .type = EV_KEY, .code = KEY_B, .value = 0 },
{ .type = EV_KEY, .code = KEY_A, .value = 0 },
{ .type = EV_SYN, .code = SYN_REPORT },
{ .type = EV_KEY, .code = KEY_A, .value = 1 },
{ .type = EV_KEY, .code = KEY_B, .value = 1 },
{ .type = EV_KEY, .code = KEY_C, .value = 1 },
{ .type = EV_KEY, .code = KEY_D, .value = 1 },
{ .type = EV_KEY, .code = KEY_D, .value = 0 },
{ .type = EV_KEY, .code = KEY_C, .value = 0 },
{ .type = EV_KEY, .code = KEY_B, .value = 0 },
{ .type = EV_KEY, .code = KEY_A, .value = 0 },
{ .type = EV_SYN, .code = SYN_REPORT },
{ .type = EV_KEY, .code = KEY_A, .value = 1 },
{ .type = EV_KEY, .code = KEY_B, .value = 1 },
{ .type = EV_KEY, .code = KEY_C, .value = 1 },
{ .type = EV_KEY, .code = KEY_D, .value = 1 },
{ .type = EV_KEY, .code = KEY_D, .value = 0 },
{ .type = EV_KEY, .code = KEY_C, .value = 0 },
{ .type = EV_KEY, .code = KEY_B, .value = 0 },
{ .type = EV_KEY, .code = KEY_A, .value = 0 },
{ .type = EV_SYN, .code = SYN_REPORT },
{ .type = EV_KEY, .code = KEY_A, .value = 1 },
{ .type = EV_KEY, .code = KEY_B, .value = 1 },
{ .type = EV_KEY, .code = KEY_C, .value = 1 },
{ .type = EV_KEY, .code = KEY_D, .value = 1 },
{ .type = EV_KEY, .code = KEY_D, .value = 0 },
{ .type = EV_KEY, .code = KEY_C, .value = 0 },
{ .type = EV_KEY, .code = KEY_B, .value = 0 },
{ .type = EV_KEY, .code = KEY_A, .value = 0 },
{ .type = EV_SYN, .code = SYN_REPORT },
{ .type = EV_KEY, .code = KEY_A, .value = 1 },
{ .type = EV_KEY, .code = KEY_B, .value = 1 },
{ .type = EV_KEY, .code = KEY_C, .value = 1 },
{ .type = EV_KEY, .code = KEY_D, .value = 1 },
{ .type = EV_KEY, .code = KEY_D, .value = 0 },
{ .type = EV_KEY, .code = KEY_C, .value = 0 },
{ .type = EV_KEY, .code = KEY_B, .value = 0 },
{ .type = EV_KEY, .code = KEY_A, .value = 0 },
{ .type = EV_SYN, .code = SYN_REPORT },
{ .type = EV_KEY, .code = KEY_A, .value = 1 },
{ .type = EV_KEY, .code = KEY_B, .value = 1 },
{ .type = EV_KEY, .code = KEY_C, .value = 1 },
{ .type = EV_KEY, .code = KEY_D, .value = 1 },
{ .type = EV_KEY, .code = KEY_D, .value = 0 },
{ .type = EV_KEY, .code = KEY_C, .value = 0 },
{ .type = EV_KEY, .code = KEY_B, .value = 0 },
{ .type = EV_KEY, .code = KEY_A, .value = 0 },
{ .type = EV_SYN, .code = SYN_REPORT },
/* 63 events up to here */
{ .type = EV_KEY, .code = KEY_A, .value = 1 },
{ .type = EV_KEY, .code = KEY_B, .value = 1 },
{ .type = EV_KEY, .code = KEY_C, .value = 1 },
{ .type = EV_KEY, .code = KEY_D, .value = 1 },
{ .type = EV_KEY, .code = KEY_D, .value = 0 },
{ .type = EV_KEY, .code = KEY_C, .value = 0 },
{ .type = EV_KEY, .code = KEY_B, .value = 0 },
{ .type = EV_KEY, .code = KEY_A, .value = 0 },
{ .type = EV_SYN, .code = SYN_REPORT },
{ .type = EV_KEY, .code = KEY_A, .value = 1 },
{ .type = EV_KEY, .code = KEY_B, .value = 1 },
{ .type = EV_KEY, .code = KEY_C, .value = 1 },
{ .type = EV_KEY, .code = KEY_D, .value = 1 },
{ .type = EV_KEY, .code = KEY_D, .value = 0 },
{ .type = EV_KEY, .code = KEY_C, .value = 0 },
{ .type = EV_KEY, .code = KEY_B, .value = 0 },
{ .type = EV_KEY, .code = KEY_A, .value = 0 },
{ .type = EV_SYN, .code = SYN_REPORT },
{ .type = EV_KEY, .code = KEY_A, .value = 1 },
{ .type = EV_KEY, .code = KEY_B, .value = 1 },
{ .type = EV_KEY, .code = KEY_C, .value = 1 },
{ .type = EV_KEY, .code = KEY_D, .value = 1 },
{ .type = EV_KEY, .code = KEY_D, .value = 0 },
{ .type = EV_KEY, .code = KEY_C, .value = 0 },
{ .type = EV_KEY, .code = KEY_B, .value = 0 },
{ .type = EV_KEY, .code = KEY_A, .value = 0 },
{ .type = EV_SYN, .code = SYN_REPORT },
{ .type = EV_KEY, .code = KEY_A, .value = 1 },
{ .type = EV_KEY, .code = KEY_B, .value = 1 },
{ .type = EV_KEY, .code = KEY_C, .value = 1 },
{ .type = EV_KEY, .code = KEY_D, .value = 1 },
{ .type = EV_KEY, .code = KEY_D, .value = 0 },
{ .type = EV_KEY, .code = KEY_C, .value = 0 },
{ .type = EV_KEY, .code = KEY_B, .value = 0 },
{ .type = EV_KEY, .code = KEY_A, .value = 0 },
{ .type = EV_SYN, .code = SYN_REPORT },
{ .type = EV_KEY, .code = KEY_A, .value = 1 },
{ .type = EV_KEY, .code = KEY_B, .value = 1 },
{ .type = EV_KEY, .code = KEY_C, .value = 1 },
{ .type = EV_KEY, .code = KEY_D, .value = 1 },
{ .type = EV_KEY, .code = KEY_D, .value = 0 },
{ .type = EV_KEY, .code = KEY_C, .value = 0 },
{ .type = EV_KEY, .code = KEY_B, .value = 0 },
{ .type = EV_KEY, .code = KEY_A, .value = 0 },
{ .type = EV_SYN, .code = SYN_REPORT },
{ .type = EV_KEY, .code = KEY_A, .value = 1 },
{ .type = EV_KEY, .code = KEY_B, .value = 1 },
{ .type = EV_KEY, .code = KEY_C, .value = 1 },
{ .type = EV_KEY, .code = KEY_D, .value = 1 },
{ .type = EV_KEY, .code = KEY_D, .value = 0 },
{ .type = EV_KEY, .code = KEY_C, .value = 0 },
{ .type = EV_KEY, .code = KEY_B, .value = 0 },
{ .type = EV_KEY, .code = KEY_A, .value = 0 },
{ .type = EV_SYN, .code = SYN_REPORT },
{ .type = EV_KEY, .code = KEY_A, .value = 1 },
{ .type = EV_KEY, .code = KEY_B, .value = 1 },
{ .type = EV_KEY, .code = KEY_C, .value = 1 },
{ .type = EV_KEY, .code = KEY_D, .value = 1 },
{ .type = EV_KEY, .code = KEY_D, .value = 0 },
{ .type = EV_KEY, .code = KEY_C, .value = 0 },
{ .type = EV_KEY, .code = KEY_B, .value = 0 },
{ .type = EV_KEY, .code = KEY_A, .value = 0 },
{ .type = EV_SYN, .code = SYN_REPORT },
/* 126 events up to here */
{ .type = EV_KEY, .code = KEY_A, .value = 1 },
{ .type = EV_SYN, .code = SYN_REPORT },
};
static const unsigned int num_test2 = sizeof(ev_test2) / sizeof(*ev_test2);
static const struct input_event ev_test3[] = {
{ .type = EV_SYN, .code = SYN_DROPPED },
{ .type = EV_SYN, .code = SYN_REPORT },
};
static const unsigned int num_test3 = sizeof(ev_test3) / sizeof(*ev_test3);
static const struct input_event ev_test4[] = {
{ .type = EV_SYN, .code = SYN_REPORT },
};
static const unsigned int num_test4 = sizeof(ev_test4) / sizeof(*ev_test4);
static const struct input_event ev_test5[] = {
{ .type = EV_KEY, .code = KEY_A, .value = 1 },
{ .type = EV_KEY, .code = KEY_B, .value = 1 },
{ .type = EV_KEY, .code = KEY_C, .value = 1 },
{ .type = EV_KEY, .code = KEY_A, .value = 0 },
{ .type = EV_KEY, .code = KEY_B, .value = 0 },
{ .type = EV_KEY, .code = KEY_C, .value = 0 },
{ .type = EV_SYN, .code = SYN_REPORT },
{ .type = EV_KEY, .code = KEY_A, .value = 1 },
{ .type = EV_KEY, .code = KEY_A, .value = 0 },
{ .type = EV_SYN, .code = SYN_REPORT },
};
static const unsigned int num_test5 = sizeof(ev_test5) / sizeof(*ev_test5);
static const struct input_event ev_test6[] = {
{ .type = EV_KEY, .code = KEY_A, .value = 1 },
{ .type = EV_KEY, .code = KEY_B, .value = 1 },
{ .type = EV_KEY, .code = KEY_C, .value = 1 },
{ .type = EV_KEY, .code = KEY_A, .value = 0 },
{ .type = EV_KEY, .code = KEY_B, .value = 0 },
{ .type = EV_KEY, .code = KEY_C, .value = 0 },
{ .type = EV_ABS, .code = ABS_X, .value = 100 },
{ .type = EV_SYN, .code = SYN_REPORT },
{ .type = EV_KEY, .code = KEY_A, .value = 1 },
{ .type = EV_KEY, .code = KEY_A, .value = 0 },
{ .type = EV_SYN, .code = SYN_REPORT },
};
static const unsigned int num_test6 = sizeof(ev_test6) / sizeof(*ev_test6);
static const struct input_event ev_test7[] = {
{ .type = EV_ABS, .code = ABS_X, .value = 100 },
{ .type = EV_SYN, .code = SYN_REPORT },
};
static const unsigned int num_test7 = sizeof(ev_test7) / sizeof(*ev_test7);
static const struct input_event ev_test8[] = {
{ .type = EV_ABS, .code = ABS_X, .value = 1000 },
{ .type = EV_ABS, .code = ABS_Y, .value = 1000 },
{ .type = EV_ABS, .code = ABS_Z, .value = 1000 },
{ .type = EV_SYN, .code = SYN_REPORT },
{ .type = EV_ABS, .code = ABS_X, .value = 0 },
{ .type = EV_ABS, .code = ABS_Y, .value = 0 },
{ .type = EV_ABS, .code = ABS_Z, .value = 0 },
{ .type = EV_SYN, .code = SYN_REPORT },
};
static const unsigned int num_test8 = sizeof(ev_test8) / sizeof(*ev_test8);
static const struct input_event ev_test9[] = {
{ .type = EV_ABS, .code = ABS_X, .value = 1000 },
{ .type = EV_ABS, .code = ABS_Y, .value = 1000 },
{ .type = EV_ABS, .code = ABS_Z, .value = 1000 },
{ .type = EV_SYN, .code = SYN_REPORT },
{ .type = EV_ABS, .code = ABS_X, .value = 0 },
{ .type = EV_ABS, .code = ABS_Y, .value = 0 },
{ .type = EV_ABS, .code = ABS_Z, .value = 0 },
{ .type = EV_SYN, .code = SYN_REPORT },
};
static const unsigned int num_test9 = sizeof(ev_test9) / sizeof(*ev_test9);
static const struct input_event ev_test10[] = {
{ .type = EV_SYN, .code = SYN_REPORT },
{ .type = EV_SYN, .code = SYN_DROPPED },
{ .type = EV_KEY, .code = KEY_A, .value = 1 },
{ .type = EV_KEY, .code = KEY_B, .value = 1 },
{ .type = EV_KEY, .code = KEY_C, .value = 1 },
{ .type = EV_KEY, .code = KEY_A, .value = 0 },
{ .type = EV_KEY, .code = KEY_B, .value = 0 },
{ .type = EV_KEY, .code = KEY_C, .value = 0 },
{ .type = EV_SYN, .code = SYN_REPORT },
};
static const unsigned int num_test10 = sizeof(ev_test10) / sizeof(*ev_test10);
int main(int argc, char **argv)
{
int ufd, fd, i;
fprintf(stderr, "creating uinput device...\n");
ufd = uinput_create(conf_bits, conf_absmax, conf_absmin,
conf_absfuzz, conf_absflat);
fprintf(stderr, "opening own uinput device...\n");
fd = uinput_find();
fprintf(stderr, "testing device...\n");
for (i = 0; i < 10; ++i) {
test_send(ufd, ev_test1, num_test1);
test_recv(fd, ev_test1, num_test1);
test_send(ufd, ev_test2, num_test2);
test_recv(fd, ev_test3, num_test3);
test_send(ufd, ev_test1, num_test1);
test_EVIOCGKEY(fd, false);
test_recv(fd, ev_test4, num_test4);
test_send(ufd, ev_test5, num_test5);
test_EVIOCGKEY(fd, false);
test_recv(fd, ev_test4, num_test4);
test_send(ufd, ev_test6, num_test6);
test_EVIOCGKEY(fd, false);
test_recv(fd, ev_test7, num_test7);
test_send(ufd, ev_test8, num_test8);
test_EVIOCGKEY(fd, false);
test_recv(fd, ev_test9, num_test9);
test_send(ufd, ev_test1, num_test1);
test_send(ufd, ev_test1, num_test1);
test_recv(fd, ev_test1, num_test1);
test_recv(fd, ev_test1, num_test1);
test_send(ufd, ev_test1, num_test1);
test_EVIOCGKEY(fd, true);
test_send(ufd, ev_test1, num_test1);
test_recv(fd, ev_test10, num_test10);
}
fprintf(stderr, "exit...\n");
close(fd);
close(ufd);
fprintf(stderr, "tests passed\n");
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment