-
Notifications
You must be signed in to change notification settings - Fork 21
/
Copy pathk.c
174 lines (153 loc) · 5.95 KB
/
k.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <linux/uinput.h>
#define KINESIS_PACKET_SIZE 3
#define MUTE KEY_MUTE
#define VDWN KEY_VOLUMEDOWN
#define VUP KEY_VOLUMEUP
#define CALC KEY_CALC
#define EJCT KEY_EJECTCLOSECD
#define PREV KEY_PREVIOUSSONG
#define PLAY KEY_PLAYPAUSE
#define NEXT KEY_NEXTSONG
// Maps from events received on the raw hid device to events sent out on uinput device
const unsigned char mapping[256] = {
/* 0 1 2 3 4 5 6 7 8 9 A B C D E F */
/* 0 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/* 1 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/* 2 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/* 3 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/* 4 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/* 5 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/* 6 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/* 7 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/* 8 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/* 9 */ 0, 0, CALC, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/* A */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/* B */ 0, 0, 0, NEXT, PREV, 0, 0, 0, EJCT, 0, 0, 0, 0, 0, 0, 0,
/* C */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, PLAY, 0, 0,
/* D */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/* E */ 0, 0, MUTE, 0, 0, 0, 0, 0, 0, VUP, VDWN, 0, 0, 0, 0, 0,
/* F */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};
// Send the sequence of keypresses corresponding to pressing and releasing
// a single key
#define KINESIS_SEND_KEY(input_key, fd) emit((fd), (EV_KEY), (input_key), 1);\
emit((fd), (EV_SYN), (SYN_REPORT), 0);\
emit((fd), (EV_KEY), (input_key), 0);\
emit((fd), (EV_SYN), (SYN_REPORT), 0)
// Send a single uinput event
void emit(int fd, int type, int code, int val) {
struct input_event ie;
ie.type = type;
ie.code = code;
ie.value = val;
ie.time.tv_sec = 0;
ie.time.tv_usec = 0;
if (write(fd, &ie, sizeof(ie)) < 0) {
perror("Error emitting keypress");
}
}
// Assume fd is the file descriptor of a raw hid file for
// a Kinesis Freestyle 2
// Use UDEV4 style initialization
void watch(int rawfd) {
// If we're on UInput 5, then we can use the new style initialization. Both structures have the
// same fields that we care about, so they can beuse interchangeably here.
#if UINPUT_VERSION>=5
struct uinput_setup usetup;
#else
struct uinput_user_dev usetup;
#endif
int ioctl_err;
// Open the uinput device file for writing
// Ensure that our writes do not block
int fd = open("/dev/uinput", O_WRONLY | O_NONBLOCK);
if (fd < 0) {
perror("Unable to open uinput");
exit(4);
}
// Enable needed keys
if (ioctl(fd, UI_SET_EVBIT, EV_KEY) < 0) {
perror("Unable to configure virtual keyboard");
exit(8);
}
for (int i = 0; i < 256; i++) {
if (mapping[i] && ioctl(fd, UI_SET_KEYBIT, mapping[i]) < 0) {
fprintf(stderr, "Unable to configure virtual key ID 0x%x: %s\n", mapping[i], strerror(errno));
exit(8);
}
}
// Configure virtual device
memset(&usetup, 0, sizeof(usetup));
usetup.id.bustype = BUS_USB;
usetup.id.vendor = 0x058f; // Alcor Micro Corp. Same as real keyboard
usetup.id.product = 0x9410; // Keyboard
strcpy(usetup.name, "KB800 Kinesis Freestyle");
// Create virtual device
#if UINPUT_VERSION>=5
ioctl_err = ioctl(fd, UI_DEV_SETUP, &usetup);
#else
fprintf(stderr, "Warning: uinput is out of date, using old style initialization. This is unsupported and may be broken.\n");
ioctl_err = write(fd, &usetup, sizeof(usetup));
#endif
if (ioctl_err < 0) {
perror("Unable to finalize virtual device configuration");
exit(16);
}
if (ioctl(fd, UI_DEV_CREATE) < 0) {
perror("Unable to create virtual device");
exit(32);
}
unsigned char data[KINESIS_PACKET_SIZE];
unsigned char key;
memset(data, 0, sizeof(data));
while (1) {
if (read(rawfd, data, sizeof(data)) < 0) {
perror("Error reading raw device");
break;
}
key = mapping[data[1]];
if (key) {
KINESIS_SEND_KEY(key, fd);
}
else if (data[1]) {
printf("Unknown HID Usage ID: 0x%x\n", data[1]);
}
}
// Destroy virtual device
if (ioctl(fd, UI_DEV_DESTROY) < 0) {
perror("Unable to destroy virtual device");
exit(32);
}
if (close(fd) < 0) {
perror("Error closing uinput file");
exit(32);
}
}
int main(int argc, char* argv[]) {
char * devpath;
int rawfd;
printf("Unofficial kinesis userspace driver\n");
if (argc < 2) {
printf("Usage: %s <device>\n", argv[0]);
exit(1);
}
devpath = argv[1];
rawfd = open(devpath, O_RDONLY);
if (rawfd < 0) {
perror("Unable to open raw device file");
exit(2);
}
watch(rawfd);
if (close(rawfd) < 0) {
perror("Error closing raw device file");
exit(2);
}
return 0;
}