commit e7656962d2bd1e5d2290b261d3bf273270f8d22a Author: Carl Philipp Klemm Date: Fri Feb 6 09:28:04 2026 +0100 Inial commit diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..d3f6b05 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,8 @@ +cmake_minimum_required(VERSION 3.21) + +project(onkioriclient LANGUAGES C) + +set(CMAKE_C_STANDARD 17) + +add_executable(${PROJECT_NAME} main.c) +install(FILES onkiori.service DESTINATION /etc/systemd/system) diff --git a/main.c b/main.c new file mode 100644 index 0000000..2b2ce46 --- /dev/null +++ b/main.c @@ -0,0 +1,151 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct key +{ + int linux_code; + int onkio_code; +}; + +struct key keys[] = +{ + {KEY_PLAY, 3471}, + {KEY_V, 3983}, + {KEY_Q, 911}, + {KEY_A, 943}, + {KEY_FASTFORWARD, 2959}, + {KEY_REWIND, 1935}, + {-1, -1} +}; + +static int register_uinput_device(const char* uinputdev, const char* devname, uint16_t vid, uint16_t pid) +{ + int fd = open(uinputdev, O_RDWR); + if(fd < 0) + return fd; + + struct uinput_setup dev = {}; + size_t len = strlen(devname); + if(len > UINPUT_MAX_NAME_SIZE) + len = UINPUT_MAX_NAME_SIZE; + strncpy(dev.name, devname, len); + + dev.id.bustype = BUS_VIRTUAL; + dev.id.vendor = vid; + dev.id.product = pid; + + ioctl(fd, UI_SET_EVBIT, EV_KEY); + + for(size_t i = 0; keys[i].linux_code > 0; ++i) + ioctl(fd, UI_SET_KEYBIT, keys[i].linux_code); + + int ret = ioctl(fd, UI_DEV_SETUP, &dev); + if(ret < 0) + { + close(fd); + return ret; + } + ret = ioctl(fd, UI_DEV_CREATE); + if(ret < 0) + { + close(fd); + return ret; + } + + return fd; +} + +int main(int argc, char** argv) +{ + if(argc < 2) + { + printf("usage: %s [BIND ADDR]\n", argv[0]); + return 1; + } + + char* addrstr = argv[1]; + + int uinputfd = register_uinput_device("/dev/uinput", "ONKIO RI", 0xfefe, 0xfefe); + + if(uinputfd < 0) + { + fprintf(stderr, "Could not create uinput device: %s\n", strerror(errno)); + return 1; + } + + int socketfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + if(socketfd < 0) + { + fprintf(stderr, "Could not create socket device: %s\n", strerror(errno)); + return 1; + } + in_addr_t addr = inet_addr(addrstr); + struct sockaddr_in sockaddr = {.sin_family = AF_INET, .sin_port = htons(40586), .sin_addr = {.s_addr = INADDR_ANY}}; + int ret = bind(socketfd, (const struct sockaddr*)&sockaddr, sizeof(sockaddr)); + if(ret < 0) + { + fprintf(stderr, "Could not bind to socket: %s\n", strerror(errno)); + return 1; + } + + char buffer[65] = {}; + + while(true) + { + ret = read(socketfd, buffer, 64); + if(ret < 0) + { + fprintf(stderr, "Could not read from to socket: %s\n", strerror(errno)); + return 1; + } + const char* header = "RI_MESSAGE: "; + if(strncmp(header, buffer, strlen(header)) == 0 && ret > strlen(header)+1) + { + int code = atoi(buffer+strlen(header)); + + bool found = false; + for(size_t i = 0; keys[i].linux_code > 0; ++i) + { + if(keys[i].onkio_code == code) + { + struct input_event ev = {}; + ev.type = EV_KEY; + ev.code = keys[i].linux_code; + ev.value = 1; + if(write(uinputfd, &ev, sizeof(ev)) != sizeof(ev)) + return -1; + ev.value = 0; + if(write(uinputfd, &ev, sizeof(ev)) != sizeof(ev)) + return -1; + gettimeofday(&ev.time, NULL); + ev.type = EV_SYN; + ev.code = SYN_REPORT; + ev.value = 0; + if(write(uinputfd, &ev, sizeof(ev)) != sizeof(ev)) + return -2; + found = true; + } + } + printf("Got %s code %i\n", found ? "known" : "unkown", code); + } + } + + close(uinputfd); + close(socketfd); +} diff --git a/onkiori.service b/onkiori.service new file mode 100644 index 0000000..3afa128 --- /dev/null +++ b/onkiori.service @@ -0,0 +1,10 @@ +[Unit] +Description=Emulates a keyboard based on the codes sent by the OnkioRI device +After=network.target + +[Service] +Type=simple +ExecStart=/usr/bin/onkioriclient 10.0.0.1 + +[Install] +WantedBy=multi-user.target