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 }