diff options
author | cbdev <cb@cbcdn.com> | 2018-02-18 19:12:47 +0100 |
---|---|---|
committer | cbdev <cb@cbcdn.com> | 2018-02-18 19:12:47 +0100 |
commit | eb943d3547520e7ee0c511a599e69355f516b9ab (patch) | |
tree | 12d0e3608accf5f11c5cbb65eb01da348149ce53 | |
parent | 65ae3ef13b91e625ec75108f2c354943b624b341 (diff) | |
download | midimonster-eb943d3547520e7ee0c511a599e69355f516b9ab.tar.gz midimonster-eb943d3547520e7ee0c511a599e69355f516b9ab.tar.bz2 midimonster-eb943d3547520e7ee0c511a599e69355f516b9ab.zip |
Implement evdev input via libevdev
-rw-r--r-- | evdev.c | 496 | ||||
-rw-r--r-- | evdev.h | 19 | ||||
-rw-r--r-- | makefile | 2 | ||||
-rw-r--r-- | monster.cfg | 24 |
4 files changed, 190 insertions, 351 deletions
@@ -1,17 +1,25 @@ -#include <linux/input.h> #include <fcntl.h> #include <stdio.h> #include <errno.h> #include <string.h> #include <unistd.h> -#include <linux/uinput.h> #include <sys/ioctl.h> +#include <libevdev/libevdev.h> +#include <libevdev/libevdev-uinput.h> #include "midimonster.h" #include "evdev.h" #define BACKEND_NAME "evdev" -#define UINPUT_PATH "/dev/uinput" + +typedef union { + struct { + uint32_t pad; + uint16_t type; + uint16_t code; + } fields; + uint64_t label; +} evdev_channel_ident; int init(){ backend evdev = { @@ -35,383 +43,232 @@ int init(){ } static int evdev_configure(char* option, char* value) { - //intentionally ignored - return 0; -} - -static int evdev_configure_instance(instance* inst, char* option, char* value) { - evdev_instance_data* data = (evdev_instance_data*) inst->impl; - - if (!strcmp(option, "device")) { - if (data->device_path) { - free(data->device_path); - } - data->device_path = strdup(value); - - if (!data->device_path) { - fprintf(stderr, "Failed to allocate memory for device path: %s\n", strerror(errno)); - return 1; - } - } else if (!strcmp(option, "exclusive")) { - data->exclusive = strtoul(value, NULL, 10); - } else if (!strcmp(option, "name")) { - if (data->name) { - free(data->name); - } - - data->name = strdup(value); - - if (!data->name) { - fprintf(stderr, "Failed to allocate memory for name: %s\n", strerror(errno)); - return 1; - } - } else { - fprintf(stderr, "Unkown configuration parameter %s for evdev backend\n", option); - return 1; - } - return 0; + fprintf(stderr, "The evdev backend does not take any global configuration\n"); + return 1; } -static channel* evdev_channel(instance* inst, char* spec) { - evdev_instance_data* data = (evdev_instance_data*) inst->impl; - char* next = spec; - // type - unsigned long type = strtoul(spec, &next, 10); - - if (spec == next) { - fprintf(stderr, "Cannot parse type\n"); - return NULL; - } - - if (type >= EV_MAX) { - fprintf(stderr, "Type is out of range\n"); +static instance* evdev_instance(){ + instance* inst = mm_instance(); + if(!inst){ return NULL; } - if (next[0] != '.') { - fprintf(stderr, "Cannot parse code. Unknown character %c\n", next[0]); + evdev_instance_data* data = calloc(1, sizeof(evdev_instance_data)); + if(!data){ + fprintf(stderr, "Failed to allocate memory\n"); return NULL; } - spec = next + 1; - - unsigned long code = strtoul(spec, &next, 10); + data->input_fd = -1; + data->output_fd = -1; - if (spec == next) { - fprintf(stderr, "Cannot parse code\n"); - return NULL; - } + inst->impl = data; + return inst; +} - if (type == EV_SYN && code >= SYN_MAX) { - fprintf(stderr, "Code is out of range. Limit for SYN is %d\n", SYN_MAX); - } else if (type == EV_KEY && code >= KEY_MAX) { - fprintf(stderr, "Code is out of range. Limit for KEY is %d\n", KEY_MAX); - return NULL; - } else if (type == EV_REL && code >= REL_MAX) { - fprintf(stderr, "Code is out of range. Limit for REL is %d\n", REL_MAX); - return NULL; - } else if (type == EV_ABS && code >= ABS_MAX) { - fprintf(stderr, "Code is out of range. Limit for ABS is %d\n", ABS_MAX); - return NULL; - } else if (type == EV_SW && code >= SW_MAX) { - fprintf(stderr, "Code is out of range. Limit for SW is %d\n", SW_MAX); - return NULL; - } else if (type == EV_MSC && code >= MSC_MAX) { - fprintf(stderr, "Code is out of range. Limit for MSC is %d\n", MSC_MAX); - return NULL; - } else if (type == EV_LED && code >= LED_MAX) { - fprintf(stderr, "Code is out of range. Limit for LED is %d\n", LED_MAX); - return NULL; - } else if (type == EV_REP && code >= REP_MAX) { - fprintf(stderr, "Code is out of range. Limit for REP is %d\n", REP_MAX); - return NULL; - } else if (type == EV_SND && code >= SND_MAX) { - fprintf(stderr, "Code is out of range. Limit for SND is %d\n", SND_MAX); - } +static int evdev_configure_instance(instance* inst, char* option, char* value) { + evdev_instance_data* data = (evdev_instance_data*) inst->impl; - uint64_t u; - if (next[0] == '.') { - spec = next + 1; - long value = strtol(spec, &next, 10); - if (spec == next) { - fprintf(stderr, "Cannot parse value\n"); - return NULL; - } - if (type == EV_KEY && (value != 0 && value != 1)) { - fprintf(stderr, "Value of KEY %ld is out of range. Only values 0 and 1 are supported for KEY.\n", value); - return NULL; - } - // find event with value - for (u = 0; u < data->size_events; u++) { - if (data->events[u].type == type - && data->events[u].code == code - && data->events[u].value == value) { - break; - } - } - } else if (next[0] != '\0') { - fprintf(stderr, "Unkown characters: %s\n", next); - return NULL; - } else { - // find event - for (u = 0; u < data->size_events; u++) { - if (data->events[u].type == type - && data->events[u].code == code) { - break; - } + if(!strcmp(option, "input")){ + if(data->input_fd >= 0){ + fprintf(stderr, "Instance %s already was assigned an input device\n", inst->name); + return 1; } - } - - // check if no event was found - if (u == data->size_events) { - fprintf(stderr, "Alloc dev %ld: %ld, %ld\n", u, type, code); - data->events = realloc(data->events, (u + 1) * sizeof(struct input_event)); - if (!data->events) { - fprintf(stderr, "Failed to allocate memory\n"); - return NULL; + data->input_fd = open(value, O_RDONLY | O_NONBLOCK); + if(data->input_fd < 0){ + fprintf(stderr, "Failed to open evdev input device node %s: %s\n", value, strerror(errno)); + return 1; } - data->events[u].type = (uint16_t) type; - data->events[u].code = (uint16_t) code; - data->size_events++; - } - return mm_channel(inst, u, 1); -} - -static instance* evdev_instance() { - instance* inst = mm_instance(); - if (!inst) { - return NULL; - } - - inst->impl = calloc(1, sizeof(evdev_instance_data)); - if (!inst->impl) { - fprintf(stderr, "Failed to allocate memory for instance\n"); - return NULL; - } - return inst; -} + if(libevdev_new_from_fd(data->input_fd, &data->input_ev)){ + fprintf(stderr, "Failed to initialize libevdev for %s\n", value); + close(data->input_fd); + data->input_fd = -1; + return 1; + } -static channel_value evdev_normalize(evdev_instance_data* data, uint64_t ident, struct input_event* event) { - channel_value value = {}; - - switch (event->type) { - case EV_KEY: - value.normalised = event->value > 0; - break; - case EV_REL: - if (event->value > 0) { - value.normalised = 1.0; - } else { - value.normalised = 0.0; - } - break; + if(data->exclusive && libevdev_grab(data->input_ev, LIBEVDEV_GRAB)){ + fprintf(stderr, "Failed to obtain exclusive device access on %s\n", value); + } } - - return value; -} - -static uint32_t evdev_convert_normalised(struct input_event event, channel_value* value) { - switch (event.type) { - case EV_KEY: - return value->normalised < 0.5; - case EV_REL: - return (value->normalised < 0.5) - 1; - default: - return value->normalised < 0.5; + else if (!strcmp(option, "exclusive")){ + if(data->input_fd >= 0 && libevdev_grab(data->input_ev, LIBEVDEV_GRAB)){ + fprintf(stderr, "Failed to obtain exclusive device access on %s\n", inst->name); + } + data->exclusive = 1; } -} - - -static int evdev_handle(size_t num, managed_fd* fds) { - struct input_event event; - ssize_t bytes = 0; - uint64_t ident; - - evdev_instance_data* data; - - channel* channel; - for (int i = 0; i < num; i++) { - bytes = read(fds[i].fd, &event, sizeof(struct input_event)); - - if (bytes < sizeof(struct input_event)) { - fprintf(stderr, "Failed to read an complete event\n"); + else if (!strcmp(option, "name")){ + if(data->output_name){ + fprintf(stderr, "Instance %s evdev device name already assigned\n", inst->name); return 1; } - data = (evdev_instance_data*) fds[0].impl; - for (ident = 0; ident < data->size_events; ident++) { - if (data->events[ident].type == event.type - && data->events[ident].code == event.code) { - break; - } - } - fprintf(stderr, "Found event: %d, %d, %d (%ld/%ld)\n", event.type, event.code, event.value, ident, data->size_events); - if (ident >= data->size_events) { - fprintf(stderr, "Event not registered.\n"); - continue; - } - channel = mm_channel(mm_instance_find(BACKEND_NAME, data->ident), ident, 0); - - if (channel) { - fprintf(stderr, "Channel found\n"); - if (mm_channel_event(channel, evdev_normalize(data, ident, &event))) { - return 1; - } + data->output_name = strdup(value); + if (!data->output_name) { + fprintf(stderr, "Failed to allocate memory\n"); + return 1; } } - + else{ + fprintf(stderr, "Unknown configuration parameter %s for evdev backend\n", option); + return 1; + } return 0; } -static int evdev_open_input_device(evdev_instance_data* data) { - if (!data->device_path) { - return 0; +static channel* evdev_channel(instance* inst, char* spec) { + char* separator = strchr(spec, '.'); + evdev_instance_data* data = (evdev_instance_data*) inst->impl; + evdev_channel_ident ident = { + .label = 0 + }; + + if(!separator){ + fprintf(stderr, "Invalid evdev channel specification %s\n", spec); + return NULL; } - data->fd_in = open(data->device_path, O_RDONLY | O_NONBLOCK); + *(separator++) = 0; - if (data->fd_in < 0) { - fprintf(stderr, "Failed to open device %s: %s\n", data->device_path, strerror(errno)); - return 1; - } - int grab = data->exclusive; - if (ioctl(data->fd_in, EVIOCGRAB, &grab) > 0) { - fprintf(stderr, "Cannot set exclusive lock on device %s\n", data->device_path); - close(data->fd_in); - data->fd_in = -1; - return 1; + if(libevdev_event_type_from_name(spec) < 0){ + fprintf(stderr, "Invalid evdev type specification: %s", spec); + return NULL; } + ident.fields.type = libevdev_event_type_from_name(spec); - if (!mm_manage_fd(data->fd_in, BACKEND_NAME, 1, data)) { - return 1; + if(libevdev_event_code_from_name(ident.fields.type, separator) >= 0){ + ident.fields.code = libevdev_event_code_from_name(ident.fields.type, separator); + } + else{ + fprintf(stderr, "evdev Code name not recognized, using as number: %s\n", separator); + ident.fields.code = strtoul(separator, NULL, 10); } - return 0; + //TODO If allowing output, push to enable list + return mm_channel(inst, ident.label, 1); } -static int enable_device_keys(evdev_instance_data* data, int uinput_fd, struct uinput_user_dev* dev) { - unsigned int u; - int ret; - int action; - uint8_t first_bits[EV_CNT]; - memset(first_bits, 0, EV_CNT * sizeof(uint8_t)); - for (u = 0; u < data->size_events; u++) { - if (data->events[u].type < EV_MAX && !first_bits[data->events[u].type]) { - ret = ioctl(uinput_fd, UI_SET_EVBIT, data->events[u].type); - - if (ret < 0) { - fprintf(stderr, "Cannot enable type: %d\n", data->events[u].type); - return 1; - } - } - switch (data->events[u].type) { - case EV_KEY: - action = UI_SET_KEYBIT; - break; - case EV_ABS: - action = UI_SET_ABSBIT; - break; +static int evdev_push_event(instance* inst, evdev_instance_data* data, struct input_event event){ + uint64_t range = 0; + channel_value val; + evdev_channel_ident ident = { + .fields.type = event.type, + .fields.code = event.code + }; + channel* chan = mm_channel(inst, ident.label, 0); + + if(chan){ + val.raw.u64 = event.value; + switch(event.type){ case EV_REL: - action = UI_SET_RELBIT; + val.normalised = 0.5 + ((event.value < 0) ? 0.5 : -0.5); break; - case EV_MSC: - action = UI_SET_MSCBIT; + case EV_ABS: + range = libevdev_get_abs_maximum(data->input_ev, event.code) - libevdev_get_abs_minimum(data->input_ev, event.code); + val.normalised = (event.value - libevdev_get_abs_minimum(data->input_ev, event.code)) / (double) range; break; + case EV_KEY: + case EV_SW: default: - fprintf(stderr, "Event code not supported: %d\n", data->events[u].type); - return 1; + val.normalised = 1.0 * event.value; + break; } - ret = ioctl(uinput_fd, action, data->events[u].code); - if (ret < 0) { - fprintf(stderr, "Cannot enable code: %d\n", data->events[u].code); + if(mm_channel_event(chan, val)){ + fprintf(stderr, "Failed to push evdev channel event to core\n"); return 1; } } + return 0; } -static int uinput_create_output_device(evdev_instance_data* data) { - - int uinput_fd = open(UINPUT_PATH, O_WRONLY | O_NONBLOCK); +static int evdev_handle(size_t num, managed_fd* fds){ + instance* inst = NULL; + evdev_instance_data* data = NULL; + size_t fd; + unsigned int read_flags = LIBEVDEV_READ_FLAG_NORMAL; + int read_status; + struct input_event ev; - if (uinput_fd < 0) { - fprintf(stderr, "Cannot open uinput device: %s\n", strerror(errno)); - return 1; + if(!num){ + return 0; } - struct uinput_user_dev dev = {}; - memset(&dev, 0, sizeof(dev)); - strncpy(dev.name, data->name, UINPUT_MAX_NAME_SIZE - 1); - dev.id.bustype = 0; - dev.id.vendor = 0; - dev.id.product = 0; - dev.id.version = 0; - - if (enable_device_keys(data, uinput_fd, &dev)) { - close(uinput_fd); - return 1; - } - // write config to uinput - int ret = write(uinput_fd, &dev, sizeof(dev)); + for(fd = 0; fd < num; fd++){ + inst = (instance*) fds[fd].impl; + if(!inst){ + fprintf(stderr, "evdev backend signaled for unknown fd\n"); + continue; + } - if (ret < 0) { - fprintf(stderr, "Cannot write to uinput device: %s\n", strerror(errno)); - close(uinput_fd); - return 1; - } + data = (evdev_instance_data*) inst->impl; - ret = ioctl(uinput_fd, UI_DEV_CREATE); + for(read_status = libevdev_next_event(data->input_ev, read_flags, &ev); read_status >= 0; read_status = libevdev_next_event(data->input_ev, read_flags, &ev)){ + read_flags = LIBEVDEV_READ_FLAG_NORMAL; + if(read_status == LIBEVDEV_READ_STATUS_SYNC){ + read_flags = LIBEVDEV_READ_FLAG_SYNC; + } - if (ret < 0) { - fprintf(stderr, "Cannot create device: %s\n", strerror(errno)); - close(uinput_fd); - return 1; + //handle event + if(evdev_push_event(inst, data, ev)){ + return 1; + } + } } - data->fd_out = uinput_fd; - return 0; } static int evdev_start() { - size_t n; + size_t n, u, fds = 0; instance** inst = NULL; - evdev_instance_data* data; - if (mm_backend_instances(BACKEND_NAME, &n, &inst)) { + evdev_instance_data* data = NULL; + + if(mm_backend_instances(BACKEND_NAME, &n, &inst)){ fprintf(stderr, "Failed to fetch instance list\n"); return 1; } - if (!n) { + if(!n){ free(inst); return 0; } - for (unsigned p = 0; p < n; p++) { - data = (evdev_instance_data*) inst[p]->impl; + for(u = 0; u < n; u++){ + data = (evdev_instance_data*) inst[u]->impl; - if (data->name) { - uinput_create_output_device(data); + if(data->output_name) { + //TODO + //if(evdev_create_output(data)){ + // return 1; + //} } - if (data->device_path) { - evdev_open_input_device(data); + inst[u]->ident = data->input_fd; + if(data->input_fd >= 0){ + if(mm_manage_fd(data->input_fd, BACKEND_NAME, 1, inst[u])){ + fprintf(stderr, "Failed to register event input descriptor for instance %s\n", inst[u]->name); + free(inst); + return 1; + } + fds++; } - data->ident = p; - inst[p]->ident = data->ident; + } + fprintf(stderr, "evdev backend registered %zu descriptors to core\n", fds); free(inst); return 0; } static int evdev_set(instance* inst, size_t num, channel** c, channel_value* v) { - size_t u; + if(!num){ + return 0; + } +/* size_t u; evdev_instance_data* data; - uint64_t ident; int ret; struct input_event event = {}; @@ -438,47 +295,38 @@ static int evdev_set(instance* inst, size_t num, channel** c, channel_value* v) } } + return 0;*/ + fprintf(stderr, "Awaiting rework, %zu channels signaled\n", num); return 0; } static int evdev_shutdown() { evdev_instance_data* data = NULL; instance** instances = NULL; - size_t n = 0; + size_t n, u; - if (mm_backend_instances(BACKEND_NAME, &n, &instances)) { + if(mm_backend_instances(BACKEND_NAME, &n, &instances)){ fprintf(stderr, "Failed to fetch instance list\n"); return 1; } - if (!n) { - free(instances); - return 0; - } + for(u = 0; u < n; u++){ + data = (evdev_instance_data*) instances[u]->impl; - for (unsigned p = 0; p < n; p++) { - data = (evdev_instance_data*) instances[p]->impl; - if (data->fd_in < 0) { - close(data->fd_in); - data->fd_in = -1; + if(data->input_fd >= 0){ + libevdev_free(data->input_ev); + close(data->input_fd); } - if (data->fd_out < 0) { - int ret = ioctl(data->fd_out, UI_DEV_DESTROY); - - if (ret < 0) { - fprintf(stderr, "Could not destroy device: %s\n", strerror(errno)); - return 1; - } - close(data->fd_out); - data->fd_out = -1; + if(data->output_fd >= 0){ + libevdev_uinput_destroy(data->output_ev); + close(data->output_fd); } - free(data->events); - free(data->name); - free(data->device_path); - free(data); + free(data->output_name); + free(data->enabled_events); } + free(instances); return 0; } @@ -13,14 +13,15 @@ static int evdev_handle(size_t num, managed_fd* fds); static int evdev_start(); static int evdev_shutdown(); -/* uinput_instance */ -typedef struct { - int ident; - char* device_path; - char* name; - int fd_in; - int fd_out; +typedef struct /*_evdev_instance_model*/ { + int input_fd; + struct libevdev* input_ev; int exclusive; - size_t size_events; - struct input_event* events; + + int output_fd; + char* output_name; + struct libevdev_uinput* output_ev; + + size_t nenabled_events; + struct input_event* enabled_events; } evdev_instance_data; @@ -11,6 +11,8 @@ CFLAGS ?= -g -Wall midimonster: LDLIBS = -ldl midimonster: CFLAGS += -rdynamic -DPLUGINS=$(PLUGINDIR) midi.so: LDLIBS = -lasound +evdev.so: CFLAGS += $(shell pkg-config --cflags libevdev) +evdev.so: LDLIBS = $(shell pkg-config --libs libevdev) %.so :: %.c %.h diff --git a/monster.cfg b/monster.cfg index 558478e..ba0e94a 100644 --- a/monster.cfg +++ b/monster.cfg @@ -1,25 +1,13 @@ [backend midi] name = MIDIMonster -[backend artnet] -bind = * -net = 0 +[evdev test] +input = /dev/input/event14 -[artnet net1] -iface = 0 -uni = 0 -dest = 255.255.255.255 +[midi foo] -[osc osc1] -bind = * 8000 -dest = learn@8001 -/1/fader1 = f 0.0 1.0 -/1/fader2 = f 0.0 1.0 -/1/fader3 = f 0.0 1.0 -/1/fader4 = f 0.0 1.0 -/1/xy = ff 0.0 1.0 0.0 1.0 [map] -osc1./1/xy:0 <> net1.1+2 -osc1./1/xy:1 <> net1.3+4 -osc1./1/fader1 <> net1.20+21 +test.EV_ABS.ABS_X > foo.cc0.81 +test.EV_ABS.ABS_GAS > foo.cc0.82 +test.EV_KEY.BTN_SOUTH > foo.cc0.83 |