aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorcbdev <cb@cbcdn.com>2018-02-18 19:12:47 +0100
committercbdev <cb@cbcdn.com>2018-02-18 19:12:47 +0100
commiteb943d3547520e7ee0c511a599e69355f516b9ab (patch)
tree12d0e3608accf5f11c5cbb65eb01da348149ce53
parent65ae3ef13b91e625ec75108f2c354943b624b341 (diff)
downloadmidimonster-eb943d3547520e7ee0c511a599e69355f516b9ab.tar.gz
midimonster-eb943d3547520e7ee0c511a599e69355f516b9ab.tar.bz2
midimonster-eb943d3547520e7ee0c511a599e69355f516b9ab.zip
Implement evdev input via libevdev
-rw-r--r--evdev.c496
-rw-r--r--evdev.h19
-rw-r--r--makefile2
-rw-r--r--monster.cfg24
4 files changed, 190 insertions, 351 deletions
diff --git a/evdev.c b/evdev.c
index 1355c06..070d5bb 100644
--- a/evdev.c
+++ b/evdev.c
@@ -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;
}
diff --git a/evdev.h b/evdev.h
index dc3e669..f1e3b7a 100644
--- a/evdev.h
+++ b/evdev.h
@@ -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;
diff --git a/makefile b/makefile
index 493c690..3ab55a1 100644
--- a/makefile
+++ b/makefile
@@ -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