├── server ├── .gitignore ├── Makefile └── src │ └── server.c ├── .gitignore ├── clients ├── C_Cpp │ ├── Makefile │ ├── mouse │ │ ├── Makefile │ │ └── mouse_client.c │ └── client.c └── Python │ └── client.py ├── test ├── units │ ├── list_hid.c │ ├── event_hid.c │ ├── dev_debug.c │ └── uhidlib_test.c └── ideas │ ├── udp_client.c │ └── udp_server.c └── README.md /server/.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore the build and lib dirs 2 | build/* 3 | lib/* 4 | 5 | # Ignore any executables 6 | bin/* -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore the build and lib dirs 2 | build/* 3 | lib/* 4 | 5 | # Ignore any executables 6 | bin/* 7 | 8 | # Ignore ide folders 9 | .vscode -------------------------------------------------------------------------------- /clients/C_Cpp/Makefile: -------------------------------------------------------------------------------- 1 | CC = gcc 2 | CFLAGS = -Wall 3 | LIBS = -lm 4 | 5 | SRC_DIR = 6 | OBJ_DIR = 7 | BIN_DIR = 8 | 9 | APP_NAME = client 10 | 11 | $(BIN_DIR)$(APP_NAME): $(OBJ_DIR)$(APP_NAME).o 12 | $(CC) $(CFLAGS) $(LIBS) -o $(BIN_DIR)$(APP_NAME) $(OBJ_DIR)$(APP_NAME).o 13 | 14 | $(OBJ_DIR)$(APP_NAME).o: $(SRC_DIR)$(APP_NAME).c 15 | $(CC) $(CFLAGS) -c $(SRC_DIR)$(APP_NAME).c -o $(OBJ_DIR)$(APP_NAME).o 16 | 17 | clean: 18 | rm -f $(OBJ_DIR)*.o 19 | 20 | install: 21 | cp $(BIN_DIR)$(APP_NAME) /usr/bin 22 | 23 | -------------------------------------------------------------------------------- /clients/C_Cpp/mouse/Makefile: -------------------------------------------------------------------------------- 1 | CC = gcc 2 | CFLAGS = -Wall 3 | LIBS = -lm 4 | 5 | SRC_DIR = 6 | OBJ_DIR = 7 | BIN_DIR = 8 | 9 | APP_NAME = mouse_client 10 | 11 | $(BIN_DIR)$(APP_NAME): $(OBJ_DIR)$(APP_NAME).o 12 | $(CC) $(CFLAGS) $(LIBS) -o $(BIN_DIR)$(APP_NAME) $(OBJ_DIR)$(APP_NAME).o 13 | 14 | $(OBJ_DIR)$(APP_NAME).o: $(SRC_DIR)$(APP_NAME).c 15 | $(CC) $(CFLAGS) -c $(SRC_DIR)$(APP_NAME).c -o $(OBJ_DIR)$(APP_NAME).o 16 | 17 | clean: 18 | rm -f $(OBJ_DIR)*.o 19 | 20 | install: 21 | cp $(BIN_DIR)$(APP_NAME) /usr/bin 22 | 23 | -------------------------------------------------------------------------------- /server/Makefile: -------------------------------------------------------------------------------- 1 | CC = gcc 2 | CFLAGS = -Wall 3 | LIBS = -lm 4 | 5 | SRC_DIR = src/ 6 | OBJ_DIR = build/ 7 | BIN_DIR = bin/ 8 | DIRS=$(OBJ_DIR) $(BIN_DIR) 9 | 10 | APP_NAME = server 11 | 12 | $(BIN_DIR)$(APP_NAME): $(OBJ_DIR)$(APP_NAME).o 13 | $(CC) $(CFLAGS) $(LIBS) -o $(BIN_DIR)$(APP_NAME) $(OBJ_DIR)$(APP_NAME).o 14 | 15 | $(OBJ_DIR)$(APP_NAME).o: $(SRC_DIR)$(APP_NAME).c 16 | $(CC) $(CFLAGS) -c $(SRC_DIR)$(APP_NAME).c -o $(OBJ_DIR)$(APP_NAME).o 17 | 18 | clean: 19 | rm -f $(OBJ_DIR)*.o 20 | 21 | install: 22 | cp $(BIN_DIR)$(APP_NAME) /usr/bin 23 | 24 | $(shell mkdir -p $(DIRS)) -------------------------------------------------------------------------------- /test/units/list_hid.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include 10 | 11 | int main(){ 12 | struct dirent *de; 13 | 14 | DIR *dr = opendir("/dev/input"); 15 | 16 | if (dr == NULL){ 17 | printf("Could not open directory"); 18 | return 0; 19 | } 20 | 21 | while ((de = readdir(dr)) != NULL){ 22 | if(strncmp("event", de->d_name, 5) == 0){ 23 | char filename[64]; 24 | int fd = -1; 25 | char name[256] = "???"; 26 | 27 | sprintf(filename, "%s%s", "/dev/input/", de->d_name); 28 | 29 | fd = open(filename, O_RDONLY); 30 | if(fd < 0) 31 | continue; 32 | 33 | ioctl(fd, EVIOCGNAME(sizeof(name)), name); 34 | printf("%s %s\n", name, de->d_name); 35 | 36 | } 37 | } 38 | 39 | closedir(dr); 40 | return 0; 41 | } -------------------------------------------------------------------------------- /test/ideas/udp_client.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #define PORT 8080 11 | 12 | void error_handling(char* msg){ 13 | perror(msg); 14 | exit(EXIT_FAILURE); 15 | } 16 | 17 | // Driver code 18 | int main() { 19 | int sockfd; 20 | struct sockaddr_in servaddr; 21 | 22 | // Creating socket file descriptor 23 | if((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0 ){ 24 | error_handling("socket creation failed"); 25 | } 26 | 27 | memset(&servaddr, 0, sizeof(servaddr)); 28 | 29 | // Filling server information 30 | servaddr.sin_family = AF_INET; 31 | servaddr.sin_port = htons(PORT); 32 | servaddr.sin_addr.s_addr = INADDR_ANY; 33 | 34 | int n, len; 35 | 36 | char *hello = "Hello from client"; 37 | sendto(sockfd, (const char *)hello, strlen(hello), MSG_CONFIRM, (const struct sockaddr *) &servaddr, sizeof(servaddr)); 38 | 39 | close(sockfd); 40 | return 0; 41 | } 42 | -------------------------------------------------------------------------------- /test/units/event_hid.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include 10 | 11 | 12 | int main(int argc, char* argv[]){ 13 | 14 | int fd = -1; 15 | int rd; 16 | char event[256]; 17 | struct input_event ev[64]; 18 | int size = sizeof(struct input_event); 19 | fd_set set; 20 | 21 | fd = open(argv[1], O_RDONLY); 22 | //ioctl(fd, EVIOCGRAB, 1); 23 | 24 | FD_ZERO(&set); 25 | FD_SET(fd, &set); 26 | 27 | while (1) { 28 | select(fd + 1, &set, NULL, NULL, NULL); 29 | 30 | rd = read(fd, ev, sizeof(ev)); 31 | 32 | if (rd < (int) sizeof(struct input_event)){ 33 | printf("expected %d bytes, got %d\n", (int) sizeof(struct input_event), rd); 34 | perror("\nevtest: error reading"); 35 | return 1; 36 | } 37 | 38 | for (int i = 0; i < rd / sizeof(struct input_event); i++){ 39 | printf ("%s: Type[%d] Code[%d] Value[%d]\n", argv[i+1], ev[i].type, ev[i].code, ev[i].value); 40 | } 41 | 42 | } 43 | 44 | //ioctl(fd, EVIOCGRAB, 0); 45 | close(fd); 46 | return 0; 47 | } -------------------------------------------------------------------------------- /test/ideas/udp_server.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #define PORT 8080 11 | 12 | void error_handling(char* msg){ 13 | perror(msg); 14 | exit(EXIT_FAILURE); 15 | } 16 | 17 | int main() { 18 | int sockfd; 19 | char buffer[1024]; 20 | struct sockaddr_in servaddr, cliaddr; 21 | 22 | // Creating socket file descriptor 23 | if((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0 ){ 24 | error_handling("socket creation failed"); 25 | } 26 | 27 | memset(&servaddr, 0, sizeof(servaddr)); 28 | memset(&cliaddr, 0, sizeof(cliaddr)); 29 | 30 | servaddr.sin_family = AF_INET; // IPv4 31 | servaddr.sin_addr.s_addr = INADDR_ANY; 32 | servaddr.sin_port = htons(PORT); 33 | 34 | // Bind the socket with the server address 35 | if(bind(sockfd, (const struct sockaddr *)&servaddr, sizeof(servaddr)) < 0 ) { 36 | error_handling("bind failed"); 37 | } 38 | 39 | while(1){ 40 | int len, n; 41 | n = recvfrom(sockfd, (char *)buffer, 1024, MSG_WAITALL | SO_REUSEADDR, ( struct sockaddr *) &cliaddr, &len); 42 | buffer[n] = '\0'; 43 | 44 | printf("Client : %s\n", buffer); 45 | usleep(100); 46 | } 47 | 48 | return 0; 49 | } 50 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Virtual HID over TCP 2 | 3 | A HID device emulation which can be controlled remotely over network.You can create joystick, keyboard or mouse device and send events like **joystick axis and buttons, keyboard key presses, mouse pointer movements** over network.With new client real device events can be sent.So its perfectly fit for keyboard, mouse sharing. 4 | 5 | Server side is written in C and currently works only in Linux.But client side is platform independent so can be written in any platform&language with using tcp socket connection. 6 | 7 | Supported event codes are found here : [here](https://github.com/torvalds/linux/blob/master/include/uapi/linux/input-event-codes.h) 8 | 9 | ## Installation 10 | 11 | ```sh 12 | git clone https://github.com/nmelihsensoy/virtual-hid-tcp.git 13 | cd virtual-hid-tcp/ 14 | ``` 15 | 16 | ## Server 17 | 18 | ```sh 19 | cd server/ 20 | make 21 | cd bin/ 22 | ./server 23 | ``` 24 | 25 | ## Client 26 | 27 | ### C/C++ Client 28 | 29 | If your server located in diffrent pc then set server ip adress with -ip 11.11.11.11 option. 30 | 31 | ```sh 32 | cd clients/C_Cpp/ 33 | make 34 | ./client --help 35 | ``` 36 | 37 | **For Real Mouse Sharing** 38 | 39 | ```sh 40 | cd clients/C_Cpp/mouse 41 | make 42 | sudo ./mouse_client 127.0.0.1 43 | ``` 44 | 45 | #### Key Press 46 | 47 | 115 -> Volume Up // 114 -> Volume Down 48 | 49 | ```sh 50 | ./client -k 115 51 | ``` 52 | 53 | #### Mouse Pointer 54 | 55 | -pX -pY 56 | 57 | ```sh 58 | ./client -pX 20 59 | ``` 60 | 61 | ### Python Client 62 | 63 | #### Key Press 64 | 65 | 115 -> Volume Up // 114 -> Volume Down 66 | 67 | ```sh 68 | python client.py -k 114 69 | ``` 70 | 71 | #### Mouse Pointer 72 | 73 | -pX -pY 74 | 75 | ```sh 76 | python client.py -pX 20 77 | ``` -------------------------------------------------------------------------------- /clients/Python/client.py: -------------------------------------------------------------------------------- 1 | ''' 2 | * Author: Nuri Melih Sensoy 3 | * github.com/nmelihsensoy 4 | * File: "client.py" 5 | * Desc: Virtual HID Socket Client for Python 6 | * 7 | * Input Event codes can be found here 8 | * https://github.com/torvalds/linux/blob/master/include/uapi/linux/input-event-codes.h 9 | * 10 | ''' 11 | #!/usr/bin/env python3 12 | import socket 13 | import time 14 | import sys 15 | 16 | HOST = '127.0.0.1' 17 | PORT = 8080 18 | 19 | s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 20 | s.connect((HOST, PORT)) 21 | 22 | payload = '' 23 | mode = 0 24 | val = 0 25 | command_index = 0 26 | 27 | for index, arg in enumerate(sys.argv): 28 | if arg in ['--ipaddress', '-ip']: 29 | HOST = sys.argv[index+1] 30 | if arg in ['--keyboard', '-k']: 31 | mode = 3 32 | command_index = index 33 | if arg in ['--pointerX', '-pX']: 34 | mode = 2 35 | val = 0 36 | command_index = index 37 | if arg in ['--pointerY', '-pY']: 38 | mode = 2 39 | val = 1 40 | command_index = index 41 | if arg in ['--help', '-h']: 42 | print("Virtual HID Socket Python Client") 43 | print(" Usage: client -k 115 ") 44 | print("Available Commands: ") 45 | print(" -ip / --ipaddress : Server ip address(Default: 127.0.0.1)") 46 | print(" -h / --help : Show help") 47 | print(" -k / --keyboard [value]: Sends keypress event over socket") 48 | print(" -pX / --pointerX: Sends X axis coordinates over socket") 49 | print(" -pY / --pointerY [value]: Sends Y axis coordinates over socket") 50 | 51 | 52 | payload = str(mode) + str(val) + sys.argv[command_index+1] 53 | 54 | s.send(payload.encode()) 55 | 56 | time.sleep(0.1) 57 | s.close() -------------------------------------------------------------------------------- /test/units/dev_debug.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include 11 | 12 | int device_count = 0; 13 | char **device; 14 | 15 | void error_handling(char *msg){ 16 | perror(msg); 17 | exit(EXIT_FAILURE); 18 | } 19 | 20 | void scan_devices(){ 21 | struct dirent *de; 22 | int i=0; 23 | 24 | DIR *dr = opendir("/dev/input"); 25 | 26 | if (dr == NULL){ 27 | error_handling("Could not open directory"); 28 | } 29 | 30 | device = malloc(50 * sizeof(char*)); 31 | 32 | while((de = readdir(dr)) != NULL){ 33 | if(strncmp("event", de->d_name, 5) == 0){ 34 | char filename[64]; 35 | int fd = -1; 36 | char name[256] = "???"; 37 | 38 | sprintf(filename, "%s%s", "/dev/input/", de->d_name); 39 | 40 | fd = open(filename, O_RDONLY); 41 | if(fd < 0) 42 | continue; 43 | 44 | ioctl(fd, EVIOCGNAME(sizeof(name)), name); 45 | printf("%i - (%s) %s\n", i, de->d_name, name); 46 | device[i] = malloc(100); 47 | strncpy(device[i], filename, sizeof(filename)); 48 | i++; 49 | } 50 | } 51 | device_count = i; 52 | device = realloc(device, (i)* sizeof(char*)); 53 | closedir(dr); 54 | } 55 | 56 | void print_events(char* device){ 57 | int fd = -1; 58 | int rd; 59 | struct input_event ev[64]; 60 | int size = sizeof(struct input_event); 61 | fd_set set; 62 | 63 | if((fd = open(device, O_RDONLY)) == 0){ 64 | error_handling("cannot open device"); 65 | } 66 | 67 | FD_ZERO(&set); 68 | FD_SET(fd, &set); 69 | 70 | while(1){ 71 | select(fd+1, &set, NULL, NULL, NULL); 72 | rd = read(fd, ev, sizeof(ev)); 73 | 74 | if(rd < (int)sizeof(struct input_event)){ 75 | error_handling("read error"); 76 | } 77 | 78 | for (int i = 0; i < rd / sizeof(struct input_event); i++){ 79 | printf("%s: Type[%d] Code[%d] Value[%d]\n", device, ev[i].type, ev[i].code, ev[i].value); 80 | } 81 | 82 | } 83 | 84 | close(fd); 85 | } 86 | 87 | int main(){ 88 | 89 | int choice; 90 | scan_devices(); 91 | printf("Select Event: "); 92 | scanf("%d", &choice); 93 | print_events(*(device+choice)); 94 | 95 | //printf("%s", *(device+choice)); 96 | 97 | return 0; 98 | } -------------------------------------------------------------------------------- /clients/C_Cpp/client.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Author: Nuri Melih Sensoy 3 | * github.com/nmelihsensoy 4 | * File: "client.c" 5 | * Desc: Virtual HID Socket Client for C 6 | * 7 | * Input Event codes can be found here 8 | * https://github.com/torvalds/linux/blob/master/include/uapi/linux/input-event-codes.h 9 | * 10 | * 0 - Key Report 11 | * 1 - Key Press - Any device 12 | * 2 - Mouse Pointer 13 | * 14 | * Ex: 15 | * Left Click Event -> 11272 -> 1:key press, 1: press val, 272: keycode 16 | * X Axis -> 20010 -> 2: pointer, 0: x axis, 010: +10 17 | * Y Axis -> 21-10 -> 2: pointer, 1: y axis, -10: -10 18 | * 19 | */ 20 | 21 | #include //printf 22 | #include //usleep, close 23 | #include //exit 24 | #include //strcmp, memset 25 | #include //inet_pton 26 | #include 27 | 28 | #define PORT 8080 //Socket Port 29 | 30 | int main(int argc, char **argv){ 31 | 32 | int sock = 0; 33 | struct sockaddr_in serv_addr; 34 | char* server_address = "127.0.0.1"; 35 | 36 | int mode = 0; 37 | int element = 0; 38 | int val = 0; 39 | char payload[6]; 40 | // Arguments Handling 41 | for (int i = 1; i < argc; i++){ 42 | if((strcmp(argv[i], "-ip") == 0) || (strcmp(argv[i], "--ipaddress") == 0)){ 43 | server_address = argv[i+1]; 44 | }else if((strcmp(argv[i], "-k") == 0) || (strcmp(argv[i], "--keypress") == 0)){ 45 | mode = 3; 46 | element = i; 47 | }else if((strcmp(argv[i], "-pX") == 0) || (strcmp(argv[i], "--pointerX") == 0)){ 48 | mode = 2; 49 | element = i; 50 | val = 0; 51 | }else if((strcmp(argv[i], "-pY") == 0) || (strcmp(argv[i], "--pointerY") == 0)){ 52 | mode = 2; 53 | element = i; 54 | val = 1; 55 | }else if((strcmp(argv[i], "-h") == 0) || (strcmp(argv[i], "--help") == 0)){ 56 | printf("Virtual HID Socket Client\n"); 57 | printf(" Usage: client -k 115 \n"); 58 | printf("Available Commands: \n"); 59 | printf(" -ip / --ipaddress : Server ip address(Default: 127.0.0.1)"); 60 | printf(" -h / --help : Show help\n"); 61 | printf(" -k / --keypress [value]: Sends key press event over socket\n"); 62 | printf(" -pX / --pointerX: Sends X axis coordinates over socket\n"); 63 | printf(" -pY / --pointerY [value]: Sends Y axis coordinates over socket\n"); 64 | } 65 | } 66 | 67 | if((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0){ 68 | printf("Socket creation error \n"); 69 | exit(EXIT_FAILURE); 70 | } 71 | 72 | memset(&serv_addr, '0', sizeof(serv_addr)); 73 | 74 | serv_addr.sin_family = AF_INET; 75 | serv_addr.sin_port = htons(PORT); 76 | 77 | // Convert IPv4 and IPv6 addresses from text to binary form 78 | if(inet_pton(AF_INET, server_address, &serv_addr.sin_addr)<=0){ 79 | printf("\nInvalid address/ Address not supported \n"); 80 | exit(EXIT_FAILURE); 81 | } 82 | 83 | if (connect(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0){ 84 | printf("Connection Failed \n"); 85 | exit(EXIT_FAILURE); 86 | } 87 | 88 | sprintf(payload, "%i%i%03d", mode, val, atoi(argv[element+1])); 89 | send(sock , payload, 6, 0); 90 | 91 | usleep(5); 92 | 93 | close(sock); 94 | return 0; 95 | } 96 | -------------------------------------------------------------------------------- /clients/C_Cpp/mouse/mouse_client.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Author: Nuri Melih Sensoy 3 | * github.com/nmelihsensoy 4 | * File: "device_client.c" 5 | * 6 | * This client sends real device events 7 | */ 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | #include 20 | 21 | #define PORT 8080 22 | 23 | int device_count = 0; 24 | char **device; 25 | int sock = 0; 26 | 27 | void error_handling(char *msg){ 28 | perror(msg); 29 | exit(EXIT_FAILURE); 30 | } 31 | 32 | void scan_devices(){ 33 | struct dirent *de; 34 | int i=0; 35 | 36 | DIR *dr = opendir("/dev/input"); 37 | 38 | if (dr == NULL){ 39 | error_handling("Could not open directory"); 40 | } 41 | 42 | device = malloc(50 * sizeof(char*)); 43 | 44 | while((de = readdir(dr)) != NULL){ 45 | if(strncmp("event", de->d_name, 5) == 0){ 46 | char filename[64]; 47 | int fd = -1; 48 | char name[256] = "???"; 49 | 50 | sprintf(filename, "%s%s", "/dev/input/", de->d_name); 51 | 52 | fd = open(filename, O_RDONLY); 53 | if(fd < 0) 54 | continue; 55 | 56 | ioctl(fd, EVIOCGNAME(sizeof(name)), name); 57 | printf("%i - (%s) %s\n", i, de->d_name, name); 58 | device[i] = malloc(100); 59 | strncpy(device[i], filename, sizeof(filename)); 60 | i++; 61 | } 62 | } 63 | device_count = i; 64 | device = realloc(device, (i)* sizeof(char*)); 65 | closedir(dr); 66 | } 67 | 68 | void socket_connect(char* server_address){ 69 | struct sockaddr_in serv_addr; 70 | 71 | if((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0){ 72 | error_handling("Socket creation error"); 73 | } 74 | 75 | memset(&serv_addr, '0', sizeof(serv_addr)); 76 | 77 | serv_addr.sin_family = AF_INET; 78 | serv_addr.sin_port = htons(PORT); 79 | 80 | // Convert IPv4 and IPv6 addresses from text to binary form 81 | if(inet_pton(AF_INET, server_address, &serv_addr.sin_addr)<=0){ 82 | error_handling("Invalid Address"); 83 | } 84 | 85 | if (connect(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0){ 86 | error_handling("Connection Failed"); 87 | } 88 | } 89 | 90 | int main(int argc, char* argv[]){ 91 | 92 | char payload[6]; 93 | socket_connect(argv[1]); 94 | 95 | int choice; 96 | char* selected_dev; 97 | 98 | scan_devices(); 99 | printf("Select Device: "); 100 | scanf("%d", &choice); 101 | selected_dev = *(device+choice); 102 | 103 | int fd = -1; 104 | int rd; 105 | struct input_event ev[64]; 106 | int size = sizeof(struct input_event); 107 | fd_set set; 108 | 109 | if((fd = open(selected_dev, O_RDONLY)) == 0){ 110 | error_handling("cannot open device"); 111 | } 112 | 113 | //Exclusive access 114 | ioctl(fd, EVIOCGRAB, 1); 115 | 116 | FD_ZERO(&set); 117 | FD_SET(fd, &set); 118 | 119 | while(1){ 120 | select(fd+1, &set, NULL, NULL, NULL); 121 | rd = read(fd, ev, sizeof(ev)); 122 | 123 | if(rd < size){ 124 | error_handling("read error"); 125 | } 126 | 127 | for(int i = 0; i < rd / size; i++){ 128 | printf("%s: Type[%d] Code[%d] Value[%d]\n", selected_dev, ev[i].type, ev[i].code, ev[i].value); 129 | 130 | if(ev[i].type != 0){ 131 | if(ev[i].type == 2) 132 | sprintf(payload, "%d%d%03d", ev[i].type, ev[i].code, ev[i].value); 133 | else if(ev[i].type == 1) 134 | sprintf(payload, "%d%d%d", ev[i].type, ev[i].value, ev[i].code); 135 | 136 | send(sock , payload, 6, 0); 137 | printf("%s", payload); 138 | } 139 | sleep(0); 140 | } 141 | usleep(10); 142 | } 143 | 144 | ioctl(fd, EVIOCGRAB, 0); 145 | close(sock); 146 | close(fd); 147 | return 0; 148 | } -------------------------------------------------------------------------------- /server/src/server.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Author: Nuri Melih Sensoy 3 | * github.com/nmelihsensoy 4 | * File: "server.c" 5 | * Desc: Virtual HID over TCP Socket. 6 | * 7 | * This is the server side and written in C.It works only on linux because of the dependencies. 8 | * But client side can be written any language with using socket connection. 9 | * 10 | * I am using uinput kernel module for HID emulation.There is a more information the following link 11 | * https://www.kernel.org/doc/html/v4.12/input/uinput.html 12 | * 13 | * Input Event codes can be found here 14 | * https://github.com/torvalds/linux/blob/master/include/uapi/linux/input-event-codes.h 15 | * 16 | */ 17 | #include //usleep, sleep, close, read, write 18 | #include //printf, perror 19 | #include //htons, sockaddr_in 20 | #include //memset, strcpy, strlen 21 | #include //open 22 | #include //exit 23 | #include 24 | #include 25 | #include 26 | 27 | #define PORT 8080 //Socket port 28 | 29 | struct uinput_setup usetup; 30 | 31 | //Key events must be defined in keys[] before using 32 | int keys[] = {BTN_LEFT, BTN_RIGHT}; 33 | 34 | //Socket 35 | int server_fd; 36 | int new_socket, valread; 37 | struct sockaddr_in address; 38 | int opt = 1; 39 | int addrlen = sizeof(address); 40 | char buffer[10]; 41 | 42 | 43 | short mode; 44 | short ev_val; 45 | short neg; 46 | int incoming_code; 47 | 48 | void error_handle(char* msg){ 49 | perror(msg); 50 | exit(EXIT_FAILURE); 51 | } 52 | 53 | /* 54 | * Sends input events 55 | */ 56 | void emit(int fd, int type, int code, int val){ 57 | struct input_event ie; 58 | 59 | ie.type = type; 60 | ie.code = code; 61 | ie.value = val; 62 | /* timestamp values below are ignored */ 63 | ie.time.tv_sec = 0; 64 | ie.time.tv_usec = 0; 65 | 66 | write(fd, &ie, sizeof(ie)); 67 | } 68 | 69 | int socket_accept(){ 70 | if((new_socket = accept(server_fd, (struct sockaddr*)&address, (socklen_t*)&addrlen))<0){ 71 | error_handle("accept"); 72 | return 0; 73 | } 74 | return 1; 75 | } 76 | 77 | int main(){ 78 | int fd = open("/dev/uinput", O_WRONLY | O_NONBLOCK); 79 | 80 | //Custom key events init 81 | ioctl(fd, UI_SET_EVBIT, EV_KEY); 82 | for(int i=0; i0){ 135 | printf("Received: %s Size: %i\n", buffer, 0); //For debugging 136 | 137 | mode = buffer[0]-'0'; //char to int 138 | 139 | buffer[0] = '0'; //Making buffer '0' because atoi giving wrong value 140 | ev_val = buffer[1]-'0'; 141 | buffer[1] = '0'; 142 | neg = 0; 143 | if(buffer[2] == '-'){ 144 | neg = 1; 145 | buffer[2] = '0'; 146 | } 147 | 148 | incoming_code = atoi(buffer); // char* to int for mode 1 149 | 150 | /* 151 | * 0 - Key Report 152 | * 1 - Key Press - Any device 153 | * 2 - Mouse Pointer 154 | * 155 | * Ex: 156 | * Left Click Event -> 11272 -> 1:key press, 1: press val, 272: keycode 157 | * X Axis -> 20010 -> 2: pointer, 0: x axis, 010: +10 158 | * Y Axis -> 21-10 -> 2: pointer, 1: y axis, -10: -10 159 | */ 160 | switch (mode){ 161 | case 0: 162 | emit(fd, EV_SYN, SYN_REPORT, 0); 163 | break; 164 | case 1: 165 | emit(fd, EV_KEY, incoming_code, ev_val); 166 | break; 167 | case 2: 168 | if(neg){ 169 | incoming_code = -incoming_code; 170 | } 171 | if(ev_val == 0){ 172 | emit(fd, EV_REL, REL_X, incoming_code); 173 | }else if(ev_val == 1){ 174 | emit(fd, EV_REL, REL_Y, incoming_code); 175 | } 176 | break; 177 | case 3: 178 | emit(fd, EV_KEY, incoming_code, 1); 179 | emit(fd, EV_SYN, SYN_REPORT, 0); 180 | emit(fd, EV_KEY, incoming_code, 0); 181 | break; 182 | } 183 | emit(fd, EV_SYN, SYN_REPORT, 0); 184 | usleep(5); 185 | }else{ 186 | socket_accept(); 187 | } 188 | usleep(10); 189 | } 190 | 191 | sleep(1); 192 | 193 | //Detach HID Device 194 | ioctl(fd, UI_DEV_DESTROY); 195 | close(fd); 196 | 197 | //Close socket server 198 | close(server_fd); 199 | 200 | return 0; 201 | } 202 | -------------------------------------------------------------------------------- /test/units/uhidlib_test.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0 2 | /* 3 | * UHID Example 4 | * 5 | * Copyright (c) 2012-2013 David Herrmann 6 | * 7 | * The code may be used by anyone for any purpose, 8 | * and can serve as a starting point for developing 9 | * applications using uhid. 10 | */ 11 | 12 | /* 13 | * UHID Example 14 | * This example emulates a basic 3 buttons mouse with wheel over UHID. Run this 15 | * program as root and then use the following keys to control the mouse: 16 | * q: Quit the application 17 | * 1: Toggle left button (down, up, ...) 18 | * 2: Toggle right button 19 | * 3: Toggle middle button 20 | * a: Move mouse left 21 | * d: Move mouse right 22 | * w: Move mouse up 23 | * s: Move mouse down 24 | * r: Move wheel up 25 | * f: Move wheel down 26 | * 27 | * Additionally to 3 button mouse, 3 keyboard LEDs are also supported (LED_NUML, 28 | * LED_CAPSL and LED_SCROLLL). The device doesn't generate any related keyboard 29 | * events, though. You need to manually write the EV_LED/LED_XY/1 activation 30 | * input event to the evdev device to see it being sent to this device. 31 | * 32 | * If uhid is not available as /dev/uhid, then you can pass a different path as 33 | * first argument. 34 | * If is not installed in /usr, then compile this with: 35 | * gcc -o ./uhid_test -Wall -I./include ./samples/uhid/uhid-example.c 36 | * And ignore the warning about kernel headers. However, it is recommended to 37 | * use the installed uhid.h if available. 38 | */ 39 | 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | #include 46 | #include 47 | #include 48 | #include 49 | #include 50 | #include 51 | #include 52 | /* 53 | * HID Report Desciptor 54 | * We emulate a basic 3 button mouse with wheel and 3 keyboard LEDs. This is 55 | * the report-descriptor as the kernel will parse it: 56 | * 57 | * INPUT(1)[INPUT] 58 | * Field(0) 59 | * Physical(GenericDesktop.Pointer) 60 | * Application(GenericDesktop.Mouse) 61 | * Usage(3) 62 | * Button.0001 63 | * Button.0002 64 | * Button.0003 65 | * Logical Minimum(0) 66 | * Logical Maximum(1) 67 | * Report Size(1) 68 | * Report Count(3) 69 | * Report Offset(0) 70 | * Flags( Variable Absolute ) 71 | * Field(1) 72 | * Physical(GenericDesktop.Pointer) 73 | * Application(GenericDesktop.Mouse) 74 | * Usage(3) 75 | * GenericDesktop.X 76 | * GenericDesktop.Y 77 | * GenericDesktop.Wheel 78 | * Logical Minimum(-128) 79 | * Logical Maximum(127) 80 | * Report Size(8) 81 | * Report Count(3) 82 | * Report Offset(8) 83 | * Flags( Variable Relative ) 84 | * OUTPUT(2)[OUTPUT] 85 | * Field(0) 86 | * Application(GenericDesktop.Keyboard) 87 | * Usage(3) 88 | * LED.NumLock 89 | * LED.CapsLock 90 | * LED.ScrollLock 91 | * Logical Minimum(0) 92 | * Logical Maximum(1) 93 | * Report Size(1) 94 | * Report Count(3) 95 | * Report Offset(0) 96 | * Flags( Variable Absolute ) 97 | * 98 | * This is the mapping that we expect: 99 | * Button.0001 ---> Key.LeftBtn 100 | * Button.0002 ---> Key.RightBtn 101 | * Button.0003 ---> Key.MiddleBtn 102 | * GenericDesktop.X ---> Relative.X 103 | * GenericDesktop.Y ---> Relative.Y 104 | * GenericDesktop.Wheel ---> Relative.Wheel 105 | * LED.NumLock ---> LED.NumLock 106 | * LED.CapsLock ---> LED.CapsLock 107 | * LED.ScrollLock ---> LED.ScrollLock 108 | * 109 | * This information can be verified by reading /sys/kernel/debug/hid//rdesc 110 | * This file should print the same information as showed above. 111 | */ 112 | 113 | static unsigned char rdescOLD[] = { 114 | 0x05, 0x01, /* USAGE_PAGE (Generic Desktop) */ 115 | 0x09, 0x02, /* USAGE (Mouse) */ 116 | 0xa1, 0x01, /* COLLECTION (Application) */ 117 | 0x09, 0x01, /* USAGE (Pointer) */ 118 | 0xa1, 0x00, /* COLLECTION (Physical) */ 119 | 0x85, 0x01, /* REPORT_ID (1) */ 120 | 0x05, 0x09, /* USAGE_PAGE (Button) */ 121 | 0x19, 0x01, /* USAGE_MINIMUM (Button 1) */ 122 | 0x29, 0x03, /* USAGE_MAXIMUM (Button 3) */ 123 | 0x15, 0x00, /* LOGICAL_MINIMUM (0) */ 124 | 0x25, 0x01, /* LOGICAL_MAXIMUM (1) */ 125 | 0x95, 0x03, /* REPORT_COUNT (3) */ 126 | 0x75, 0x01, /* REPORT_SIZE (1) */ 127 | 0x81, 0x02, /* INPUT (Data,Var,Abs) */ 128 | 0x95, 0x01, /* REPORT_COUNT (1) */ 129 | 0x75, 0x05, /* REPORT_SIZE (5) */ 130 | 0x81, 0x01, /* INPUT (Cnst,Var,Abs) */ 131 | 0x05, 0x01, /* USAGE_PAGE (Generic Desktop) */ 132 | 0x09, 0x30, /* USAGE (X) */ 133 | 0x09, 0x31, /* USAGE (Y) */ 134 | 0x09, 0x38, /* USAGE (WHEEL) */ 135 | 0x15, 0x81, /* LOGICAL_MINIMUM (-127) */ 136 | 0x25, 0x7f, /* LOGICAL_MAXIMUM (127) */ 137 | 0x75, 0x08, /* REPORT_SIZE (8) */ 138 | 0x95, 0x03, /* REPORT_COUNT (3) */ 139 | 0x81, 0x06, /* INPUT (Data,Var,Rel) */ 140 | 0xc0, /* END_COLLECTION */ 141 | 0xc0, /* END_COLLECTION */ 142 | 0x05, 0x01, /* USAGE_PAGE (Generic Desktop) */ 143 | 0x09, 0x06, /* USAGE (Keyboard) */ 144 | 0xa1, 0x01, /* COLLECTION (Application) */ 145 | 0x85, 0x02, /* REPORT_ID (2) */ 146 | 0x05, 0x08, /* USAGE_PAGE (Led) */ 147 | 0x19, 0x01, /* USAGE_MINIMUM (1) */ 148 | 0x29, 0x03, /* USAGE_MAXIMUM (3) */ 149 | 0x15, 0x00, /* LOGICAL_MINIMUM (0) */ 150 | 0x25, 0x01, /* LOGICAL_MAXIMUM (1) */ 151 | 0x95, 0x03, /* REPORT_COUNT (3) */ 152 | 0x75, 0x01, /* REPORT_SIZE (1) */ 153 | 0x91, 0x02, /* Output (Data,Var,Abs) */ 154 | 0x95, 0x01, /* REPORT_COUNT (1) */ 155 | 0x75, 0x05, /* REPORT_SIZE (5) */ 156 | 0x91, 0x01, /* Output (Cnst,Var,Abs) */ 157 | 0xc0, /* END_COLLECTION */ 158 | }; 159 | 160 | static unsigned char rdescLOGI[] = { 161 | 0x05, 0x01, // Usage Page (Generic Desktop) 0 162 | 0x09, 0x06, // Usage (Keyboard) 2 163 | 0xa1, 0x01, // Collection (Application) 4 164 | 0x85, 0x01, // Report ID (1) 6 165 | 0x05, 0x07, // Usage Page (Keyboard) 8 166 | 0x19, 0xe0, // Usage Minimum (224) 10 167 | 0x29, 0xe7, // Usage Maximum (231) 12 168 | 0x15, 0x00, // Logical Minimum (0) 14 169 | 0x25, 0x01, // Logical Maximum (1) 16 170 | 0x75, 0x01, // Report Size (1) 18 171 | 0x95, 0x08, // Report Count (8) 20 172 | 0x81, 0x02, // Input (Data,Var,Abs) 22 173 | 0x81, 0x03, // Input (Cnst,Var,Abs) 24 174 | 0x95, 0x06, // Report Count (6) 26 175 | 0x75, 0x08, // Report Size (8) 28 176 | 0x15, 0x00, // Logical Minimum (0) 30 177 | 0x26, 0xa4, 0x00, // Logical Maximum (164) 32 178 | 0x19, 0x00, // Usage Minimum (0) 35 179 | 0x2a, 0xa4, 0x00, // Usage Maximum (164) 37 180 | 0x81, 0x00, // Input (Data,Arr,Abs) 40 181 | 0xc0, // End Collection 42 182 | 0x05, 0x0c, // Usage Page (Consumer Devices) 43 183 | 0x09, 0x01, // Usage (Consumer Control) 45 184 | 0xa1, 0x01, // Collection (Application) 47 185 | 0x85, 0x03, // Report ID (3) 49 186 | 0x75, 0x10, // Report Size (16) 51 187 | 0x95, 0x02, // Report Count (2) 53 188 | 0x15, 0x01, // Logical Minimum (1) 55 189 | 0x26, 0x8c, 0x02, // Logical Maximum (652) 57 190 | 0x19, 0x01, // Usage Minimum (1) 60 191 | 0x2a, 0x8c, 0x02, // Usage Maximum (652) 62 192 | 0x81, 0x00, // Input (Data,Arr,Abs) 65 193 | 0xc0, // End Collection 67 194 | 0x05, 0x01, // Usage Page (Generic Desktop) 68 195 | 0x09, 0x80, // Usage (System Control) 70 196 | 0xa1, 0x01, // Collection (Application) 72 197 | 0x85, 0x04, // Report ID (4) 74 198 | 0x75, 0x02, // Report Size (2) 76 199 | 0x95, 0x01, // Report Count (1) 78 200 | 0x15, 0x01, // Logical Minimum (1) 80 201 | 0x25, 0x03, // Logical Maximum (3) 82 202 | 0x09, 0x82, // Usage (System Sleep) 84 203 | 0x09, 0x81, // Usage (System Power Down) 86 204 | 0x09, 0x83, // Usage (System Wake Up) 88 205 | 0x81, 0x60, // Input (Data,Arr,Abs,NoPref,Null) 90 206 | 0x75, 0x06, // Report Size (6) 92 207 | 0x81, 0x03, // Input (Cnst,Var,Abs) 94 208 | 0xc0, // End Collection 96 209 | 0x06, 0x00, 0xff, // Usage Page (Vendor Defined Page 1) 97 210 | 0x09, 0x01, // Usage (Vendor Usage 1) 100 211 | 0xa1, 0x01, // Collection (Application) 102 212 | 0x85, 0x10, // Report ID (16) 104 213 | 0x75, 0x08, // Report Size (8) 106 214 | 0x95, 0x06, // Report Count (6) 108 215 | 0x15, 0x00, // Logical Minimum (0) 110 216 | 0x26, 0xff, 0x00, // Logical Maximum (255) 112 217 | 0x09, 0x01, // Usage (Vendor Usage 1) 115 218 | 0x81, 0x00, // Input (Data,Arr,Abs) 117 219 | 0x09, 0x01, // Usage (Vendor Usage 1) 119 220 | 0x91, 0x00, // Output (Data,Arr,Abs) 121 221 | 0xc0, // End Collection 123 222 | 0x06, 0x00, 0xff, // Usage Page (Vendor Defined Page 1) 124 223 | 0x09, 0x02, // Usage (Vendor Usage 2) 127 224 | 0xa1, 0x01, // Collection (Application) 129 225 | 0x85, 0x11, // Report ID (17) 131 226 | 0x75, 0x08, // Report Size (8) 133 227 | 0x95, 0x13, // Report Count (19) 135 228 | 0x15, 0x00, // Logical Minimum (0) 137 229 | 0x26, 0xff, 0x00, // Logical Maximum (255) 139 230 | 0x09, 0x02, // Usage (Vendor Usage 2) 142 231 | 0x81, 0x00, // Input (Data,Arr,Abs) 144 232 | 0x09, 0x02, // Usage (Vendor Usage 2) 146 233 | 0x91, 0x00, // Output (Data,Arr,Abs) 148 234 | 0xc0, // End Collection 150 235 | }; 236 | 237 | static unsigned char rdesc[] = { 238 | 0x05, 0x01, // USAGE_PAGE (Generic Desktop) 239 | 0x09, 0x02, // USAGE (Mouse) 240 | 0xa1, 0x01, // COLLECTION (Application) 241 | 0x09, 0x01, // USAGE (Pointer) 242 | 0xa1, 0x00, // COLLECTION (Physical) 243 | 0x05, 0x09, // USAGE_PAGE (Button) 244 | 0x19, 0x01, // USAGE_MINIMUM (Button 1) 245 | 0x29, 0x03, // USAGE_MAXIMUM (Button 3) 246 | 0x15, 0x00, // LOGICAL_MINIMUM (0) 247 | 0x25, 0x01, // LOGICAL_MAXIMUM (1) 248 | 0x95, 0x03, // REPORT_COUNT (3) 249 | 0x75, 0x01, // REPORT_SIZE (1) 250 | 0x81, 0x02, // INPUT (Data,Var,Abs) 251 | 0x95, 0x01, // REPORT_COUNT (1) 252 | 0x75, 0x05, // REPORT_SIZE (5) 253 | 0x81, 0x03, // INPUT (Cnst,Var,Abs) 254 | 0x05, 0x01, // USAGE_PAGE (Generic Desktop) 255 | 0x09, 0x30, // USAGE (X) 256 | 0x09, 0x31, // USAGE (Y) 257 | 0x15, 0x81, // LOGICAL_MINIMUM (-127) 258 | 0x25, 0x7f, // LOGICAL_MAXIMUM (127) 259 | 0x75, 0x08, // REPORT_SIZE (8) 260 | 0x95, 0x02, // REPORT_COUNT (2) 261 | 0x81, 0x06, // INPUT (Data,Var,Rel) 262 | 263 | 0x09, 0x38, /* USAGE (WHEEL) */ 264 | 0x15, 0x81, /* LOGICAL_MINIMUM (-127) */ 265 | 0x25, 0x7f, /* LOGICAL_MAXIMUM (127) */ 266 | 0x75, 0x08, /* REPORT_SIZE (8) */ 267 | 0x95, 0x03, /* REPORT_COUNT (3) */ 268 | 0x81, 0x06, /* INPUT (Data,Var,Rel) */ 269 | 0xc0, // END_COLLECTION 270 | 0xc0 // END_COLLECTION 271 | }; 272 | 273 | 274 | static int uhid_write(int fd, const struct uhid_event *ev) 275 | { 276 | ssize_t ret; 277 | 278 | ret = write(fd, ev, sizeof(*ev)); 279 | if (ret < 0) { 280 | fprintf(stderr, "Cannot write to uhid: %m\n"); 281 | return -errno; 282 | } else if (ret != sizeof(*ev)) { 283 | fprintf(stderr, "Wrong size written to uhid: %ld != %lu\n", 284 | ret, sizeof(ev)); 285 | return -EFAULT; 286 | } else { 287 | return 0; 288 | } 289 | } 290 | 291 | static int create(int fd) 292 | { 293 | struct uhid_event ev; 294 | 295 | memset(&ev, 0, sizeof(ev)); 296 | ev.type = UHID_CREATE; 297 | strcpy((char*)ev.u.create.name, "test-uhid-device"); 298 | ev.u.create.rd_data = rdesc; 299 | ev.u.create.rd_size = sizeof(rdesc); 300 | ev.u.create.bus = BUS_USB; 301 | ev.u.create.vendor = 0x15d9; 302 | ev.u.create.product = 0x0a37; 303 | ev.u.create.version = 0; 304 | ev.u.create.country = 0; 305 | 306 | return uhid_write(fd, &ev); 307 | } 308 | 309 | static void destroy(int fd) 310 | { 311 | struct uhid_event ev; 312 | 313 | memset(&ev, 0, sizeof(ev)); 314 | ev.type = UHID_DESTROY; 315 | 316 | uhid_write(fd, &ev); 317 | } 318 | 319 | /* This parses raw output reports sent by the kernel to the device. A normal 320 | * uhid program shouldn't do this but instead just forward the raw report. 321 | * However, for ducomentational purposes, we try to detect LED events here and 322 | * print debug messages for it. */ 323 | static void handle_output(struct uhid_event *ev) 324 | { 325 | /* LED messages are adverised via OUTPUT reports; ignore the rest */ 326 | if (ev->u.output.rtype != UHID_OUTPUT_REPORT) 327 | return; 328 | /* LED reports have length 2 bytes */ 329 | if (ev->u.output.size != 2) 330 | return; 331 | /* first byte is report-id which is 0x02 for LEDs in our rdesc */ 332 | if (ev->u.output.data[0] != 0x2) 333 | return; 334 | 335 | /* print flags payload */ 336 | fprintf(stderr, "LED output report received with flags %x\n", 337 | ev->u.output.data[1]); 338 | } 339 | 340 | static int event(int fd) 341 | { 342 | struct uhid_event ev; 343 | ssize_t ret; 344 | 345 | memset(&ev, 0, sizeof(ev)); 346 | ret = read(fd, &ev, sizeof(ev)); 347 | if (ret == 0) { 348 | fprintf(stderr, "Read HUP on uhid-cdev\n"); 349 | return -EFAULT; 350 | } else if (ret < 0) { 351 | fprintf(stderr, "Cannot read uhid-cdev: %m\n"); 352 | return -errno; 353 | } else if (ret != sizeof(ev)) { 354 | fprintf(stderr, "Invalid size read from uhid-dev: %ld != %lu\n", 355 | ret, sizeof(ev)); 356 | return -EFAULT; 357 | } 358 | 359 | switch (ev.type) { 360 | case UHID_START: 361 | fprintf(stderr, "UHID_START from uhid-dev\n"); 362 | break; 363 | case UHID_STOP: 364 | fprintf(stderr, "UHID_STOP from uhid-dev\n"); 365 | break; 366 | case UHID_OPEN: 367 | fprintf(stderr, "UHID_OPEN from uhid-dev\n"); 368 | break; 369 | case UHID_CLOSE: 370 | fprintf(stderr, "UHID_CLOSE from uhid-dev\n"); 371 | break; 372 | case UHID_OUTPUT: 373 | fprintf(stderr, "UHID_OUTPUT from uhid-dev\n"); 374 | handle_output(&ev); 375 | break; 376 | case UHID_OUTPUT_EV: 377 | fprintf(stderr, "UHID_OUTPUT_EV from uhid-dev\n"); 378 | break; 379 | default: 380 | fprintf(stderr, "Invalid event from uhid-dev: %u\n", ev.type); 381 | } 382 | 383 | return 0; 384 | } 385 | 386 | static bool btn1_down; 387 | static bool btn2_down; 388 | static bool btn3_down; 389 | static signed char abs_hor; 390 | static signed char abs_ver; 391 | static signed char wheel; 392 | 393 | static int send_event(int fd) 394 | { 395 | struct uhid_event ev; 396 | 397 | memset(&ev, 0, sizeof(ev)); 398 | ev.type = UHID_INPUT; 399 | ev.u.input.size = 3; 400 | 401 | ev.u.input.data[0] = 0x0; 402 | if (btn1_down) 403 | ev.u.input.data[0] = 0x1; 404 | 405 | //ev.u.input.data[0] = 0x1; left button 406 | //ev.u.input.data[0] = 0x3, 0x4; right button 407 | //ev.u.input.data[0] = 0x4; middle button 408 | if (btn2_down) 409 | ev.u.input.data[0] = 0x4; 410 | 411 | ev.u.input.data[1] = abs_hor; 412 | ev.u.input.data[2] = abs_ver; 413 | ev.u.input.data[4] = wheel; 414 | 415 | return uhid_write(fd, &ev); 416 | } 417 | 418 | static int keyboard(int fd) 419 | { 420 | char buf[128]; 421 | ssize_t ret, i; 422 | 423 | ret = read(STDIN_FILENO, buf, sizeof(buf)); 424 | if (ret == 0) { 425 | fprintf(stderr, "Read HUP on stdin\n"); 426 | return -EFAULT; 427 | } else if (ret < 0) { 428 | fprintf(stderr, "Cannot read stdin: %m\n"); 429 | return -errno; 430 | } 431 | 432 | for (i = 0; i < ret; ++i) { 433 | switch (buf[i]) { 434 | case '1': 435 | btn1_down = !btn1_down; 436 | ret = send_event(fd); 437 | if (ret) 438 | return ret; 439 | break; 440 | case '2': 441 | btn2_down = !btn2_down; 442 | ret = send_event(fd); 443 | if (ret) 444 | return ret; 445 | break; 446 | case '3': 447 | btn3_down = !btn3_down; 448 | ret = send_event(fd); 449 | if (ret) 450 | return ret; 451 | break; 452 | case 'a': 453 | abs_hor = -20; 454 | ret = send_event(fd); 455 | abs_hor = 0; 456 | if (ret) 457 | return ret; 458 | break; 459 | case 'd': 460 | abs_hor = 20; 461 | ret = send_event(fd); 462 | abs_hor = 0; 463 | if (ret) 464 | return ret; 465 | break; 466 | case 'w': 467 | abs_ver = -20; 468 | ret = send_event(fd); 469 | abs_ver = 0; 470 | if (ret) 471 | return ret; 472 | break; 473 | case 's': 474 | abs_ver = 20; 475 | ret = send_event(fd); 476 | abs_ver = 0; 477 | if (ret) 478 | return ret; 479 | break; 480 | case 'r': 481 | wheel = 1; 482 | ret = send_event(fd); 483 | wheel = 0; 484 | if (ret) 485 | return ret; 486 | break; 487 | case 'f': 488 | wheel = -1; 489 | ret = send_event(fd); 490 | wheel = 0; 491 | if (ret) 492 | return ret; 493 | break; 494 | case 'q': 495 | return -ECANCELED; 496 | default: 497 | fprintf(stderr, "Invalid input: %c\n", buf[i]); 498 | } 499 | } 500 | 501 | return 0; 502 | } 503 | 504 | int main(int argc, char **argv) 505 | { 506 | int fd; 507 | const char *path = "/dev/uhid"; 508 | struct pollfd pfds[2]; 509 | int ret; 510 | struct termios state; 511 | 512 | ret = tcgetattr(STDIN_FILENO, &state); 513 | if (ret) { 514 | fprintf(stderr, "Cannot get tty state\n"); 515 | } else { 516 | state.c_lflag &= ~ICANON; 517 | state.c_cc[VMIN] = 1; 518 | ret = tcsetattr(STDIN_FILENO, TCSANOW, &state); 519 | if (ret) 520 | fprintf(stderr, "Cannot set tty state\n"); 521 | } 522 | 523 | if (argc >= 2) { 524 | if (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) { 525 | fprintf(stderr, "Usage: %s [%s]\n", argv[0], path); 526 | return EXIT_SUCCESS; 527 | } else { 528 | path = argv[1]; 529 | } 530 | } 531 | 532 | fprintf(stderr, "Open uhid-cdev %s\n", path); 533 | fd = open(path, O_RDWR | 0); 534 | if (fd < 0) { 535 | fprintf(stderr, "Cannot open uhid-cdev %s: %m\n", path); 536 | return EXIT_FAILURE; 537 | } 538 | 539 | fprintf(stderr, "Create uhid device\n"); 540 | ret = create(fd); 541 | if (ret) { 542 | close(fd); 543 | return EXIT_FAILURE; 544 | } 545 | 546 | pfds[0].fd = STDIN_FILENO; 547 | pfds[0].events = POLLIN; 548 | pfds[1].fd = fd; 549 | pfds[1].events = POLLIN; 550 | 551 | fprintf(stderr, "Press 'q' to quit...\n"); 552 | while (1) { 553 | ret = poll(pfds, 2, -1); 554 | if (ret < 0) { 555 | fprintf(stderr, "Cannot poll for fds: %m\n"); 556 | break; 557 | } 558 | if (pfds[0].revents & POLLHUP) { 559 | fprintf(stderr, "Received HUP on stdin\n"); 560 | break; 561 | } 562 | if (pfds[1].revents & POLLHUP) { 563 | fprintf(stderr, "Received HUP on uhid-cdev\n"); 564 | break; 565 | } 566 | 567 | if (pfds[0].revents & POLLIN) { 568 | ret = keyboard(fd); 569 | if (ret) 570 | break; 571 | } 572 | if (pfds[1].revents & POLLIN) { 573 | ret = event(fd); 574 | if (ret) 575 | break; 576 | } 577 | } 578 | 579 | fprintf(stderr, "Destroy uhid device\n"); 580 | destroy(fd); 581 | return EXIT_SUCCESS; 582 | } --------------------------------------------------------------------------------