ceckb

libcec to uinput key event mapper
git clone git://git.2f30.org/ceckb.git
Log | Files | Refs | README | LICENSE

commit d232d7636c570604d364963845cfb8d9a93c6a80
parent 63ae472a6386f37dc85d442c57d701816fd40a77
Author: lostd <lostd@2f30.org>
Date:   Sat Mar 28 16:40:02 +0200

Minimal working libcec code and key press logic

Diffstat:
Makefile | 1+
ceckb.c | 181+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------
2 files changed, 165 insertions(+), 17 deletions(-)
diff --git a/Makefile b/Makefile @@ -1,3 +1,4 @@ +#CPPFLAGS = -DDEBUG LDLIBS = -lcec OBJS = ceckb.o BIN = ceckb diff --git a/ceckb.c b/ceckb.c @@ -4,28 +4,51 @@ #include <linux/uinput.h> #include <err.h> +#include <signal.h> #include <string.h> #include <stdio.h> #include <fcntl.h> +#include <unistd.h> -#include <libcec/cectypes.h> +#include <libcec/cecc.h> #define LEN(x) (sizeof(x) / sizeof(*(x))) +#ifdef DEBUG +#define DPRINTF_X(t) printf(#t"=0x%x\n", t) +#define DPRINTF_D(t) printf(#t"=%d\n", t) +#define DPRINTF_U(t) printf(#t"=%u\n", t) +#define DPRINTF_S(t) printf(#t"=%s\n", t) +#define DPRINTF printf +#else +#define DPRINTF_X(t) +#define DPRINTF_D(t) +#define DPRINTF_U(t) +#define DPRINTF_S(t) +#define DPRINTF(t) +#endif + struct map { - unsigned cec; - unsigned code; + unsigned cec; /* HDMI-CEC code */ + unsigned code; /* uinput code */ }; #include "config.h" +int fd; +int die; + void -setupuinput(int fd) +setupuinput(void) { struct uinput_user_dev uidev; unsigned int i; int ret; + fd = open(upath, O_WRONLY | O_NONBLOCK); + if (fd == -1) + err(1, "open %s", upath); + ret = ioctl(fd, UI_SET_EVBIT, EV_KEY); if (ret == -1) err(1, "key events"); @@ -56,17 +79,19 @@ setupuinput(int fd) } void -cleanuinput(int fd) +cleanuinput(void) { int ret; ret = ioctl(fd, UI_DEV_DESTROY); if (ret == -1) err(1, "destroy dev"); + + close(fd); } void -sendevent(int fd, unsigned type, unsigned code) +sendevent(unsigned type, unsigned code, unsigned val) { struct input_event ev; int ret; @@ -74,29 +99,151 @@ sendevent(int fd, unsigned type, unsigned code) memset(&ev, 0, sizeof(ev)); ev.type = type; ev.code = code; - ev.value = 1; + ev.value = val; ret = write(fd, &ev, sizeof(ev)); if (ret == -1) err(1, "send event type 0x%x code 0x%x", type, code); } +void +sendkeypress(unsigned ceccode) +{ + int i; + + for (i = 0; i < LEN(bindings); i++) { + if (ceccode == bindings[i].cec) { + sendevent(EV_KEY, bindings[i].code, 1); + sendevent(EV_KEY, bindings[i].code, 0); + sendevent(EV_SYN, SYN_REPORT, 0); + return; + } + } + warnx("unhandled code 0x%x", ceccode); +} + int -main(void) +onkeypress(void *cbparam, const cec_keypress key) { - int fd; + static unsigned prevcode; - fd = open(upath, O_WRONLY | O_NONBLOCK); - if (fd == -1) - err(1, "open %s", upath); + DPRINTF_X(key.keycode); + DPRINTF_X(key.duration); - setupuinput(fd); + /* No duration means this is a press event. Duration is set on + * release events. Repeated press events are emulated by always + * sending a release event to uinput. Some key presses only + * generate a single release event (the select button for example), + * so we inject a key press there by checking with the previous key + * press. */ + if (key.duration == 0 || key.keycode != prevcode) + sendkeypress(key.keycode); - sendevent(fd, EV_KEY, KEY_DOWN); - sendevent(fd, EV_SYN, SYN_REPORT); + prevcode = key.keycode; - cleanuinput(fd); + return 0; +} - close(fd); +int +onlogmsg(void *cbparam, const cec_log_message msg) +{ + DPRINTF_S(msg.message); + + return 0; +} + +int +oncommand(void *cbparam, const cec_command cmd) +{ + return 0; +} + +int +onalert(void *cbparam, const libcec_alert type, const libcec_parameter param) +{ + return 0; +} + +ICECCallbacks callbacks = { + .CBCecKeyPress = onkeypress, + .CBCecLogMessage = onlogmsg, + .CBCecCommand = oncommand, + .CBCecAlert = onalert, +}; + +libcec_configuration config = { + .clientVersion = CEC_CLIENT_VERSION_CURRENT, + .serverVersion = CEC_SERVER_VERSION_CURRENT, + .strDeviceName = "ceckb", + .deviceTypes = { + CEC_DEVICE_TYPE_RECORDING_DEVICE, + CEC_DEVICE_TYPE_RESERVED, + CEC_DEVICE_TYPE_RESERVED, + CEC_DEVICE_TYPE_RESERVED, + CEC_DEVICE_TYPE_RESERVED, + }, + .cecVersion = CEC_DEFAULT_SETTING_CEC_VERSION, + .callbacks = &callbacks, +}; + +void +setupcec(void) +{ + cec_adapter devs[10], *dev; + int ret; + int n; + + ret = cec_initialise(&config); + if (!ret) + errx(1, "cec init"); + + /* Initialize host stack */ + cec_init_video_standalone(); + + /* Auto detect adapters */ + n = cec_find_adapters(devs, LEN(devs), NULL); + if (n == -1) + err(1, "find adapters"); + if (n == 0) + err(1, "no adapters found"); + DPRINTF_D(n); + + /* Take the first */ + dev = &devs[0]; + DPRINTF_S(dev->path); + DPRINTF_S(dev->comm); + + ret = cec_open(dev->comm, CEC_DEFAULT_CONNECT_TIMEOUT); + if (!ret) + errx(1, "open port %s", dev->comm); +} + +void +cleancec(void) +{ + cec_close(); + cec_destroy(); +} + +void +death(int sig) +{ + die = 1; +} + +int +main(void) +{ + setupuinput(); + setupcec(); + + signal(SIGINT, death); + signal(SIGTERM, death); + + while (!die) + pause(); + + cleancec(); + cleanuinput(); return 0; }