/*
* tty2keyb.c
* Copyright (c) 2009 LOR anonymous2
* Licensed under GPL 2 or newer
*/
#include <sys/types.h>
#include <sys/time.h>
#include <sys/stat.h>
#include <linux/input.h>
#include <linux/uinput.h>
#include <termios.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <signal.h>
#include <termios.h>
#define VERSION 20090506
speed_t int_speed(int baudrate);
int read_tty(const char* device, int baudrate, char* res, int max);
void key_event(int uinp_fd, struct input_event* event, int key);
void events_processing(struct input_event* event, const char* buf, int max);
void term_handler(int);
int debug = 0;
int uinp_fd = -1;
int ttys_fd = -1;
struct termios oldtio, newtio;
int main(int argc, char **argv)
{
const char* device = "/dev/ttyUSB0";
int speed = 9600;
int opt;
while((opt = getopt(argc, argv, "s:d:i:")) != -1)
switch(opt)
{
case 'd': debug = 1; break;
case 's': speed = optarg ? atoi(optarg) : 0; break;
case 'i': device = optarg; break;
default:
fprintf(stdout, "usage: %s [-d debug] [-i device] [-s speed]\n", argv[0]);
return 0;
}
uinp_fd = open("/dev/uinput", O_WRONLY|O_NDELAY);
if(0 > uinp_fd)
{
if(debug) fprintf(stderr, "unable to open /dev/uinput, check permissions or load uinput module\n");
return -1;
}
struct uinput_user_dev uinp;
memset(&uinp, 0, sizeof(uinp));
strncpy(uinp.name, argv[0], UINPUT_MAX_NAME_SIZE);
ioctl(uinp_fd, UI_SET_EVBIT, EV_KEY);
ioctl(uinp_fd, UI_SET_EVBIT, EV_REP);
int i;
for(i = 0; i < 256; i++) ioctl(uinp_fd, UI_SET_KEYBIT, i);
write(uinp_fd, &uinp, sizeof(uinp));
if(ioctl(uinp_fd, UI_DEV_CREATE))
{
if(debug) fprintf(stderr, "unable to create uinput device");
return -1;
}
struct input_event event;
memset(&event, 0, sizeof(event));
struct sigaction sa;
sigset_t newset;
sigemptyset(&newset);
sigaddset(&newset, SIGHUP);
sigprocmask(SIG_BLOCK, &newset, 0);
sa.sa_handler = term_handler;
sigaction(SIGTERM, &sa, 0);
int exit = 0;
char res[255];
while(1)
{
if(0 < read_tty(device, speed, res, 255)) events_processing(&event, res, 255);
sleep(1);
}
term_handler(0);
}
void term_handler(int r)
{
if(ttys_fd)
{
tcsetattr(ttys_fd, TCSANOW, &oldtio);
close(ttys_fd);
}
if(uinp_fd)
{
ioctl(uinp_fd, UI_DEV_DESTROY);
close(uinp_fd);
}
exit(EXIT_SUCCESS);
}
speed_t int_speed(int baudrate)
{
switch(baudrate)
{
case 1200: return B1200;
case 2400: return B2400;
case 4800: return B4800;
case 9600: return B9600;
case 19200: return B19200;
case 38400: return B38400;
case 57600: return B57600;
case 115200: return B115200;
default: break;
}
return B0;
}
void events_processing(struct input_event* event, const char* buf, int max)
{
if(NULL == event || NULL == buf || 0 == max) return;
const char* itr = buf;
for(; *itr && itr < buf + max; ++itr)
switch(*itr)
{
case '`': key_event(uinp_fd, event, KEY_GRAVE); break;
case '0': key_event(uinp_fd, event, KEY_0); break;
case '1': key_event(uinp_fd, event, KEY_1); break;
case '2': key_event(uinp_fd, event, KEY_2); break;
case '3': key_event(uinp_fd, event, KEY_3); break;
case '4': key_event(uinp_fd, event, KEY_4); break;
case '5': key_event(uinp_fd, event, KEY_5); break;
case '6': key_event(uinp_fd, event, KEY_6); break;
case '7': key_event(uinp_fd, event, KEY_7); break;
case '8': key_event(uinp_fd, event, KEY_8); break;
case '9': key_event(uinp_fd, event, KEY_9); break;
case '-': key_event(uinp_fd, event, KEY_MINUS); break;
case '=': key_event(uinp_fd, event, KEY_EQUAL); break;
case '\\': key_event(uinp_fd, event, KEY_BACKSLASH); break;
case '\b': key_event(uinp_fd, event, KEY_BACKSPACE); break;
case '\t': key_event(uinp_fd, event, KEY_TAB); break;
case 'a': key_event(uinp_fd, event, KEY_A); break;
case 'b': key_event(uinp_fd, event, KEY_B); break;
case 'c': key_event(uinp_fd, event, KEY_C); break;
case 'd': key_event(uinp_fd, event, KEY_D); break;
case 'e': key_event(uinp_fd, event, KEY_E); break;
case 'f': key_event(uinp_fd, event, KEY_F); break;
case 'g': key_event(uinp_fd, event, KEY_G); break;
case 'h': key_event(uinp_fd, event, KEY_H); break;
case 'i': key_event(uinp_fd, event, KEY_I); break;
case 'j': key_event(uinp_fd, event, KEY_J); break;
case 'k': key_event(uinp_fd, event, KEY_K); break;
case 'l': key_event(uinp_fd, event, KEY_L); break;
case 'm': key_event(uinp_fd, event, KEY_M); break;
case 'n': key_event(uinp_fd, event, KEY_N); break;
case 'o': key_event(uinp_fd, event, KEY_O); break;
case 'p': key_event(uinp_fd, event, KEY_P); break;
case 'q': key_event(uinp_fd, event, KEY_Q); break;
case 'r': key_event(uinp_fd, event, KEY_R); break;
case 's': key_event(uinp_fd, event, KEY_S); break;
case 't': key_event(uinp_fd, event, KEY_T); break;
case 'u': key_event(uinp_fd, event, KEY_U); break;
case 'v': key_event(uinp_fd, event, KEY_V); break;
case 'w': key_event(uinp_fd, event, KEY_W); break;
case 'x': key_event(uinp_fd, event, KEY_X); break;
case 'y': key_event(uinp_fd, event, KEY_Y); break;
case 'z': key_event(uinp_fd, event, KEY_Z); break;
case '[': key_event(uinp_fd, event, KEY_LEFTBRACE); break;
case ']': key_event(uinp_fd, event, KEY_RIGHTBRACE); break;
case '\r': key_event(uinp_fd, event, KEY_ENTER); break;
case ';': key_event(uinp_fd, event, KEY_SEMICOLON); break;
case '\'': key_event(uinp_fd, event, KEY_APOSTROPHE); break;
case ',': key_event(uinp_fd, event, KEY_COMMA); break;
case '.': key_event(uinp_fd, event, KEY_DOT); break;
case '/': key_event(uinp_fd, event, KEY_SLASH); break;
case 0x20: key_event(uinp_fd, event, KEY_SPACE); break;
case '~': key_event(uinp_fd, event, KEY_LEFTSHIFT); events_processing(event, "`", 1); break;
case '!': key_event(uinp_fd, event, KEY_LEFTSHIFT); events_processing(event, "1", 1); break;
case '@': key_event(uinp_fd, event, KEY_LEFTSHIFT); events_processing(event, "2", 1); break;
case '#': key_event(uinp_fd, event, KEY_LEFTSHIFT); events_processing(event, "3", 1); break;
case '$': key_event(uinp_fd, event, KEY_LEFTSHIFT); events_processing(event, "4", 1); break;
case '%': key_event(uinp_fd, event, KEY_LEFTSHIFT); events_processing(event, "5", 1); break;
case '^': key_event(uinp_fd, event, KEY_LEFTSHIFT); events_processing(event, "6", 1); break;
case '&': key_event(uinp_fd, event, KEY_LEFTSHIFT); events_processing(event, "7", 1); break;
case '*': key_event(uinp_fd, event, KEY_LEFTSHIFT); events_processing(event, "8", 1); break;
case '(': key_event(uinp_fd, event, KEY_LEFTSHIFT); events_processing(event, "9", 1); break;
case ')': key_event(uinp_fd, event, KEY_LEFTSHIFT); events_processing(event, "0", 1); break;
case '_': key_event(uinp_fd, event, KEY_LEFTSHIFT); events_processing(event, "-", 1); break;
case '+': key_event(uinp_fd, event, KEY_LEFTSHIFT); events_processing(event, "=", 1); break;
case '|': key_event(uinp_fd, event, KEY_LEFTSHIFT); events_processing(event, "\\", 1); break;
case '{': key_event(uinp_fd, event, KEY_LEFTSHIFT); events_processing(event, "[", 1); break;
case '}': key_event(uinp_fd, event, KEY_LEFTSHIFT); events_processing(event, "]", 1); break;
case '<': key_event(uinp_fd, event, KEY_LEFTSHIFT); events_processing(event, ",", 1); break;
case '>': key_event(uinp_fd, event, KEY_LEFTSHIFT); events_processing(event, ".", 1); break;
case '?': key_event(uinp_fd, event, KEY_LEFTSHIFT); events_processing(event, "/", 1); break;
case 'A': key_event(uinp_fd, event, KEY_LEFTSHIFT); key_event(uinp_fd, event, KEY_A); break;
case 'B': key_event(uinp_fd, event, KEY_LEFTSHIFT); key_event(uinp_fd, event, KEY_B); break;
case 'C': key_event(uinp_fd, event, KEY_LEFTSHIFT); key_event(uinp_fd, event, KEY_C); break;
case 'D': key_event(uinp_fd, event, KEY_LEFTSHIFT); key_event(uinp_fd, event, KEY_D); break;
case 'E': key_event(uinp_fd, event, KEY_LEFTSHIFT); key_event(uinp_fd, event, KEY_E); break;
case 'F': key_event(uinp_fd, event, KEY_LEFTSHIFT); key_event(uinp_fd, event, KEY_F); break;
case 'G': key_event(uinp_fd, event, KEY_LEFTSHIFT); key_event(uinp_fd, event, KEY_G); break;
case 'H': key_event(uinp_fd, event, KEY_LEFTSHIFT); key_event(uinp_fd, event, KEY_H); break;
case 'I': key_event(uinp_fd, event, KEY_LEFTSHIFT); key_event(uinp_fd, event, KEY_I); break;
case 'J': key_event(uinp_fd, event, KEY_LEFTSHIFT); key_event(uinp_fd, event, KEY_J); break;
case 'K': key_event(uinp_fd, event, KEY_LEFTSHIFT); key_event(uinp_fd, event, KEY_K); break;
case 'L': key_event(uinp_fd, event, KEY_LEFTSHIFT); key_event(uinp_fd, event, KEY_L); break;
case 'M': key_event(uinp_fd, event, KEY_LEFTSHIFT); key_event(uinp_fd, event, KEY_M); break;
case 'N': key_event(uinp_fd, event, KEY_LEFTSHIFT); key_event(uinp_fd, event, KEY_N); break;
case 'O': key_event(uinp_fd, event, KEY_LEFTSHIFT); key_event(uinp_fd, event, KEY_O); break;
case 'P': key_event(uinp_fd, event, KEY_LEFTSHIFT); key_event(uinp_fd, event, KEY_P); break;
case 'Q': key_event(uinp_fd, event, KEY_LEFTSHIFT); key_event(uinp_fd, event, KEY_Q); break;
case 'R': key_event(uinp_fd, event, KEY_LEFTSHIFT); key_event(uinp_fd, event, KEY_R); break;
case 'S': key_event(uinp_fd, event, KEY_LEFTSHIFT); key_event(uinp_fd, event, KEY_S); break;
case 'T': key_event(uinp_fd, event, KEY_LEFTSHIFT); key_event(uinp_fd, event, KEY_T); break;
case 'U': key_event(uinp_fd, event, KEY_LEFTSHIFT); key_event(uinp_fd, event, KEY_U); break;
case 'V': key_event(uinp_fd, event, KEY_LEFTSHIFT); key_event(uinp_fd, event, KEY_V); break;
case 'W': key_event(uinp_fd, event, KEY_LEFTSHIFT); key_event(uinp_fd, event, KEY_W); break;
case 'X': key_event(uinp_fd, event, KEY_LEFTSHIFT); key_event(uinp_fd, event, KEY_X); break;
case 'Y': key_event(uinp_fd, event, KEY_LEFTSHIFT); key_event(uinp_fd, event, KEY_Y); break;
case 'Z': key_event(uinp_fd, event, KEY_LEFTSHIFT); key_event(uinp_fd, event, KEY_Z); break;
default: break;
}
}
int read_tty(const char* device, int baudrate, char* res, int max)
{
if(NULL == device) return -1;
ttys_fd = open(device, O_RDONLY|O_NOCTTY);
if(0 > ttys_fd)
{
if(debug) fprintf(stderr, "unable open device: %s\n", device);
return -1;
}
tcgetattr(ttys_fd, &oldtio);
memset(&newtio, 0, sizeof(struct termios));
newtio.c_cflag = int_speed(baudrate);
newtio.c_cflag |= CS8|CLOCAL|ICANON;
tcflush(ttys_fd, TCIFLUSH);
tcsetattr(ttys_fd, TCSANOW, &newtio);
memset(res, 0, max);
int ret = read(ttys_fd, res, max);
tcsetattr(ttys_fd, TCSANOW, &oldtio);
close(ttys_fd);
ttys_fd = -1;
return ret;
}
void key_event(int uinp_fd, struct input_event* event, int key)
{
if(0 > uinp_fd || NULL == event) return;
gettimeofday(&event->time, NULL);
event->type = EV_KEY;
event->code = key;
event->value = 1;
write(uinp_fd, event, sizeof(struct input_event));
event->type = EV_SYN;
event->code = SYN_REPORT;
event->value = 0;
write(uinp_fd, event, sizeof(struct input_event));
gettimeofday(&event->time, NULL);
event->type = EV_KEY;
event->code = key;
event->value = 0;
write(uinp_fd, event, sizeof(struct input_event));
event->type = EV_SYN;
event->code = SYN_REPORT;
event->value = 0;
write(uinp_fd, event, sizeof(struct input_event));
}