commit d232d7636c570604d364963845cfb8d9a93c6a80
parent 63ae472a6386f37dc85d442c57d701816fd40a77
Author: lostd <lostd@2f30.org>
Date: Sat, 28 Mar 2015 16:40:02 +0200
Minimal working libcec code and key press logic
Diffstat:
M | Makefile | | | 1 | + |
M | 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;
}