ceckb

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

ceckb.c (4689B)


      1 #include <sys/stat.h>
      2 
      3 #include <linux/input.h>
      4 #include <linux/uinput.h>
      5 
      6 #include <err.h>
      7 #include <signal.h>
      8 #include <string.h>
      9 #include <stdio.h>
     10 #include <fcntl.h>
     11 #include <unistd.h>
     12 
     13 #include <libcec/cecc.h>
     14 
     15 #define LEN(x) (sizeof(x) / sizeof(*(x)))
     16 
     17 #ifdef DEBUG
     18 #define DPRINTF_X(t) printf(#t"=0x%x\n", t)
     19 #define DPRINTF_D(t) printf(#t"=%d\n", t)
     20 #define DPRINTF_U(t) printf(#t"=%u\n", t)
     21 #define DPRINTF_S(t) printf(#t"=%s\n", t)
     22 #define DPRINTF printf
     23 #else
     24 #define DPRINTF_X(t)
     25 #define DPRINTF_D(t)
     26 #define DPRINTF_U(t)
     27 #define DPRINTF_S(t)
     28 #define DPRINTF(t)
     29 #endif
     30 
     31 struct map {
     32 	unsigned cec;  /* HDMI-CEC code */
     33 	unsigned code; /* uinput code */
     34 };
     35 
     36 #include "config.h"
     37 
     38 int fd;
     39 int die;
     40 
     41 void
     42 setupuinput(void)
     43 {
     44 	struct uinput_user_dev uidev;
     45 	unsigned int i;
     46 	int ret;
     47 
     48 	fd = open(upath, O_WRONLY | O_NONBLOCK);
     49 	if (fd == -1)
     50 		err(1, "open %s", upath);
     51 
     52 	ret = ioctl(fd, UI_SET_EVBIT, EV_KEY);
     53 	if (ret == -1)
     54 		err(1, "key events");
     55 	ret = ioctl(fd, UI_SET_EVBIT, EV_SYN);
     56 	if (ret == -1)
     57 		err(1, "sync events");
     58 
     59 	/* Set available keys */
     60 	for (i = 0; i < LEN(bindings); i++) {
     61 		ret = ioctl(fd, UI_SET_KEYBIT, bindings[i].code);
     62 		if (ret == -1)
     63 			err(1, "set key 0x%x", bindings[i].code);
     64 	}
     65 
     66 	memset(&uidev, 0, sizeof(uidev));
     67 	snprintf(uidev.name, UINPUT_MAX_NAME_SIZE, "ceckb");
     68 	uidev.id.bustype = BUS_USB;
     69 	uidev.id.vendor  = 0xdead;
     70 	uidev.id.product = 0xbabe;
     71 	uidev.id.version = 1;
     72 
     73 	ret = write(fd, &uidev, sizeof(uidev));
     74 	if (ret == -1)
     75 		err(1, "write uidev");
     76 	ret = ioctl(fd, UI_DEV_CREATE);
     77 	if (ret == -1)
     78 		err(1, "create dev");
     79 }
     80 
     81 void
     82 cleanuinput(void)
     83 {
     84 	int ret;
     85 
     86 	ret = ioctl(fd, UI_DEV_DESTROY);
     87 	if (ret == -1)
     88 		err(1, "destroy dev");
     89 
     90 	close(fd);
     91 }
     92 
     93 void
     94 sendevent(unsigned type, unsigned code, unsigned val)
     95 {
     96 	struct input_event ev;
     97 	int ret;
     98 
     99 	memset(&ev, 0, sizeof(ev));
    100 	ev.type = type;
    101 	ev.code = code;
    102 	ev.value = val;
    103 	ret = write(fd, &ev, sizeof(ev));
    104 	if (ret == -1)
    105 		err(1, "send event type 0x%x code 0x%x", type, code);
    106 }
    107 
    108 void
    109 sendkeypress(unsigned ceccode)
    110 {
    111 	int i;
    112 
    113 	for (i = 0; i < LEN(bindings); i++) {
    114 		if (ceccode == bindings[i].cec) {
    115 			sendevent(EV_KEY, bindings[i].code, 1);
    116 			sendevent(EV_KEY, bindings[i].code, 0);
    117 			sendevent(EV_SYN, SYN_REPORT, 0);
    118 			return;
    119 		}
    120 	}
    121 	warnx("unhandled code 0x%x", ceccode);
    122 }
    123 
    124 void
    125 onkeypress(void *cbparam, const cec_keypress *key)
    126 {
    127 	static unsigned prevcode = CEC_USER_CONTROL_CODE_UNKNOWN;
    128 
    129 	DPRINTF_X(key->keycode);
    130 	DPRINTF_X(key->duration);
    131 
    132 	/* No duration means this is a press event.  Duration is set on
    133 	 * release events.  Repeated press events are emulated by always
    134 	 * sending a release event to uinput.  Some key presses only
    135 	 * generate a single release event (the select button for example),
    136 	 * so we inject a key press there by checking with the previous key
    137 	 * press. */
    138 	if (key->duration == 0) {
    139 		sendkeypress(key->keycode);
    140 		prevcode = key->keycode;
    141 	} else if (key->keycode != prevcode) {
    142 		sendkeypress(key->keycode);
    143 	}
    144 }
    145 
    146 void
    147 onlogmsg(void *cbparam, const cec_log_message *msg)
    148 {
    149 	DPRINTF_S(msg->message);
    150 }
    151 
    152 void
    153 oncommand(void *cbparam, const cec_command *cmd)
    154 {
    155 }
    156 
    157 void
    158 onalert(void *cbparam, const libcec_alert type, const libcec_parameter param)
    159 {
    160 }
    161 
    162 ICECCallbacks callbacks = {
    163 	.keyPress = onkeypress,
    164 	.logMessage = onlogmsg,
    165 	.commandReceived = oncommand,
    166 	.alert = onalert,
    167 };
    168 
    169 libcec_configuration config = {
    170 	.clientVersion = LIBCEC_VERSION_CURRENT,
    171 	.serverVersion = LIBCEC_VERSION_CURRENT,
    172 	.strDeviceName = "ceckb",
    173 	.deviceTypes = {
    174 		.types = {
    175 			CEC_DEVICE_TYPE_RECORDING_DEVICE,
    176 			CEC_DEVICE_TYPE_RESERVED,
    177 			CEC_DEVICE_TYPE_RESERVED,
    178 			CEC_DEVICE_TYPE_RESERVED,
    179 			CEC_DEVICE_TYPE_RESERVED,
    180 		},
    181 	},
    182 	.cecVersion = CEC_DEFAULT_SETTING_CEC_VERSION,
    183 	.callbacks = &callbacks,
    184 };
    185 
    186 /* global cec connection handle */
    187 libcec_connection_t conn;
    188 
    189 void
    190 setupcec(void)
    191 {
    192 	cec_adapter devs[10], *dev;
    193 	int ret;
    194 	int n;
    195 
    196 	conn = libcec_initialise(&config);
    197 	if (!conn)
    198 		errx(1, "cec init");
    199 
    200 	/* Initialize host stack */
    201 	libcec_init_video_standalone(conn);
    202 
    203 	/* Auto detect adapters */
    204 	n = libcec_find_adapters(conn, devs, LEN(devs), NULL);
    205 	if (n == -1)
    206 		errx(1, "find adapters");
    207 	if (n == 0)
    208 		errx(1, "no adapters found");
    209 	DPRINTF_D(n);
    210 
    211 	/* Take the first */
    212 	dev = &devs[0];
    213 	DPRINTF_S(dev->path);
    214 	DPRINTF_S(dev->comm);
    215 
    216 	ret = libcec_open(conn, dev->comm, CEC_DEFAULT_CONNECT_TIMEOUT);
    217 	if (!ret)
    218 		errx(1, "open port %s", dev->comm);
    219 }
    220 
    221 void
    222 cleancec(void)
    223 {
    224 	libcec_close(conn);
    225 	libcec_destroy(conn);
    226 }
    227 
    228 void
    229 death(int sig)
    230 {
    231 	die = 1;
    232 }
    233 
    234 int
    235 main(void)
    236 {
    237 	setupuinput();
    238 	setupcec();
    239 
    240 	signal(SIGINT, death);
    241 	signal(SIGTERM, death);
    242 
    243 	while (!die)
    244 		pause();
    245 
    246 	cleancec();
    247 	cleanuinput();
    248 
    249 	return 0;
    250 }