summaryrefslogtreecommitdiff
path: root/src/evemu.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/evemu.c')
-rw-r--r--src/evemu.c311
1 files changed, 311 insertions, 0 deletions
diff --git a/src/evemu.c b/src/evemu.c
new file mode 100644
index 0000000..6c99a89
--- /dev/null
+++ b/src/evemu.c
@@ -0,0 +1,311 @@
1/*****************************************************************************
2 *
3 * evemu - Kernel device emulation
4 *
5 * Copyright (C) 2010 Henrik Rydberg <rydberg@euromail.se>
6 * Copyright (C) 2010 Canonical Ltd.
7 *
8 * Permission is hereby granted, free of charge, to any person obtaining a
9 * copy of this software and associated documentation files (the "Software"),
10 * to deal in the Software without restriction, including without limitation
11 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
12 * and/or sell copies of the Software, and to permit persons to whom the
13 * Software is furnished to do so, subject to the following conditions:
14 *
15 * The above copyright notice and this permission notice (including the next
16 * paragraph) shall be included in all copies or substantial portions of the
17 * Software.
18 *
19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
22 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
24 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
25 * DEALINGS IN THE SOFTWARE.
26 *
27 ****************************************************************************/
28
29#include "evemu.h"
30#include <string.h>
31#include <stdio.h>
32#include <fcntl.h>
33#include <errno.h>
34#include <poll.h>
35
36#define SYSCALL(call) while (((call) == -1) && (errno == EINTR))
37
38static void copy_bits(unsigned char *mask, const unsigned long *bits, int bytes)
39{
40 int i;
41 for (i = 0; i < bytes; i++) {
42 int pos = 8 * (i % sizeof(long));
43 mask[i] = (bits[i / sizeof(long)] >> pos) & 0xff;
44 }
45}
46
47int evemu_extract(struct evemu_device *dev, int fd)
48{
49 unsigned long bits[64];
50 int rc, i;
51
52 memset(dev, 0, sizeof(*dev));
53
54 SYSCALL(rc = ioctl(fd, EVIOCGNAME(sizeof(dev->name)), dev->name));
55 if (rc < 0)
56 return rc;
57
58 SYSCALL(rc = ioctl(fd, EVIOCGID, &dev->id));
59 if (rc < 0)
60 return rc;
61
62 for (i = 0; i < EV_CNT; i++) {
63 SYSCALL(rc = ioctl(fd, EVIOCGBIT(i, sizeof(bits)), bits));
64 if (rc < 0)
65 continue;
66 copy_bits(dev->mask[i], bits, rc);
67 dev->bytes[i] = rc;
68 }
69
70 for (i = 0; i < ABS_CNT; i++) {
71 if (!evemu_has(dev, EV_ABS, i))
72 continue;
73 SYSCALL(rc = ioctl(fd, EVIOCGABS(i), &dev->abs[i]));
74 if (rc < 0)
75 return rc;
76 }
77
78 return 0;
79}
80
81static void write_mask(FILE * fp, int index,
82 const unsigned char *mask, int bytes)
83{
84 int i;
85 for (i = 0; i < bytes; i += 8)
86 fprintf(fp, "B: %02x %02x %02x %02x %02x %02x %02x %02x %02x\n",
87 index, mask[i], mask[i + 1], mask[i + 2], mask[i + 3],
88 mask[i + 4], mask[i + 5], mask[i + 6], mask[i + 7]);
89}
90
91static void write_abs(FILE *fp, int index, const struct input_absinfo *abs)
92{
93 fprintf(fp, "A: %02x %d %d %d %d\n", index,
94 abs->minimum, abs->maximum, abs->fuzz, abs->flat);
95}
96
97int evemu_write(const struct evemu_device *dev, FILE *fp)
98{
99 int i;
100
101 fprintf(fp, "N: %s\n", dev->name);
102
103 fprintf(fp, "I: %04x %04x %04x %04x\n",
104 dev->id.bustype, dev->id.vendor,
105 dev->id.product, dev->id.version);
106
107 for (i = 0; i < EV_CNT; i++)
108 write_mask(fp, i, dev->mask[i], dev->bytes[i]);
109
110 for (i = 0; i < ABS_CNT; i++)
111 if (evemu_has(dev, EV_ABS, i))
112 write_abs(fp, i, &dev->abs[i]);
113}
114
115static void read_mask(struct evemu_device *dev, FILE *fp)
116{
117 unsigned int mask[8];
118 int index, i;
119 while (fscanf(fp, "B: %02x %02x %02x %02x %02x %02x %02x %02x %02x\n",
120 &index, mask + 0, mask + 1, mask + 2, mask + 3,
121 mask + 4, mask + 5, mask + 6, mask + 7) > 0) {
122 for (i = 0; i < 8; i++)
123 dev->mask[index][dev->bytes[index]++] = mask[i];
124 }
125}
126
127static void read_abs(struct evemu_device *dev, FILE *fp)
128{
129 struct input_absinfo abs;
130 int index;
131 while (fscanf(fp, "A: %02x %d %d %d %d\n", &index,
132 &abs.minimum, &abs.maximum, &abs.fuzz, &abs.flat) > 0)
133 dev->abs[index] = abs;
134}
135
136int evemu_read(struct evemu_device *dev, FILE *fp)
137{
138 unsigned bustype, vendor, product, version;
139 int ret;
140
141 memset(dev, 0, sizeof(*dev));
142
143 ret = fscanf(fp, "N: %s\n", dev->name);
144 if (ret <= 0)
145 return ret;
146
147 ret = fscanf(fp, "I: %04x %04x %04x %04x\n",
148 &bustype, &vendor, &product, &version);
149 if (ret <= 0)
150 return ret;
151
152 dev->id.bustype = bustype;
153 dev->id.vendor = vendor;
154 dev->id.product = product;
155 dev->id.version = version;
156
157 read_mask(dev, fp);
158
159 read_abs(dev, fp);
160
161 return 1;
162}
163
164int evemu_write_event(FILE *fp, const struct input_event *ev)
165{
166 return fprintf(fp, "E: %lu.%06u %04x %04x %d\n",
167 ev->time.tv_sec, (unsigned)ev->time.tv_usec,
168 ev->type, ev->code, ev->value);
169}
170
171int evemu_record(FILE *fp, int fd, int ms)
172{
173 struct pollfd fds = { fd, POLLIN, 0 };
174 struct input_event ev;
175 int ret;
176
177 while (poll(&fds, 1, ms) > 0) {
178 SYSCALL(ret = read(fd, &ev, sizeof(ev)));
179 if (ret < 0)
180 return ret;
181 if (ret == sizeof(ev))
182 evemu_write_event(fp, &ev);
183 }
184
185 return 0;
186}
187
188int evemu_read_event(FILE *fp, struct input_event *ev)
189{
190 unsigned long sec;
191 unsigned usec, type, code;
192 int value;
193 int ret = fscanf(fp, "E: %lu.%06u %04x %04x %d\n",
194 &sec, &usec, &type, &code, &value);
195 ev->time.tv_sec = sec;
196 ev->time.tv_usec = usec;
197 ev->type = type;
198 ev->code = code;
199 ev->value = value;
200 return ret;
201}
202
203int evemu_play(FILE *fp, int fd)
204{
205 struct input_event prev, ev;
206 long check = 0, usec;
207 int ret;
208
209 memset(&prev, 0, sizeof(prev));
210 while (evemu_read_event(fp, &ev) > 0) {
211 if (!prev.time.tv_sec)
212 prev = ev;
213 usec = 1000000L * (ev.time.tv_sec - prev.time.tv_sec);
214 usec += ev.time.tv_usec - prev.time.tv_usec;
215 if (usec - check > 500) {
216 usleep(usec - check);
217 check = usec;
218 }
219 SYSCALL(ret = write(fd, &ev, sizeof(ev)));
220 }
221
222 return 0;
223}
224
225static int set_bit(int fd, int type, int code)
226{
227 int ret = 0;
228
229 switch(type) {
230 case EV_SYN:
231 SYSCALL(ret = ioctl(fd, UI_SET_EVBIT, code));
232 break;
233 case EV_KEY:
234 SYSCALL(ret = ioctl(fd, UI_SET_KEYBIT, code));
235 break;
236 case EV_REL:
237 SYSCALL(ret = ioctl(fd, UI_SET_RELBIT, code));
238 break;
239 case EV_ABS:
240 SYSCALL(ret = ioctl(fd, UI_SET_ABSBIT, code));
241 break;
242 case EV_MSC:
243 SYSCALL(ret = ioctl(fd, UI_SET_MSCBIT, code));
244 break;
245 case EV_LED:
246 SYSCALL(ret = ioctl(fd, UI_SET_LEDBIT, code));
247 break;
248 case EV_SND:
249 SYSCALL(ret = ioctl(fd, UI_SET_SNDBIT, code));
250 break;
251 case EV_FF:
252 SYSCALL(ret = ioctl(fd, UI_SET_FFBIT, code));
253 break;
254 case EV_SW:
255 SYSCALL(ret = ioctl(fd, UI_SET_SWBIT, code));
256 break;
257 }
258
259 return ret;
260}
261
262static int set_mask(const struct evemu_device *dev, int type, int fd)
263{
264 int bits = 8 * dev->bytes[type];
265 int ret, i;
266 for (i = 0; i < bits; i++) {
267 if (!evemu_has(dev, type, i))
268 continue;
269 ret = set_bit(fd, type, i);
270 if (ret < 0)
271 return ret;
272 }
273 return 0;
274}
275
276int evemu_create(const struct evemu_device *dev, int fd)
277{
278 struct uinput_user_dev udev;
279 int ret, i;
280
281 memset(&udev, 0, sizeof(udev));
282 memcpy(udev.name, dev->name, sizeof(udev.name));
283 udev.id = dev->id;
284 for (i = 0; i < ABS_CNT; i++) {
285 if (!evemu_has(dev, EV_ABS, i))
286 continue;
287 udev.absmax[i] = dev->abs[i].maximum;
288 udev.absmin[i] = dev->abs[i].minimum;
289 udev.absfuzz[i] = dev->abs[i].fuzz;
290 udev.absflat[i] = dev->abs[i].flat;
291 }
292
293 for (i = 0; i < EV_CNT; i++) {
294 ret = set_mask(dev, i, fd);
295 if (ret < 0)
296 return ret;
297 }
298
299 SYSCALL(ret = write(fd, &udev, sizeof(udev)));
300 if (ret < 0)
301 return ret;
302
303 SYSCALL(ret = ioctl(fd, UI_DEV_CREATE, NULL));
304 return ret;
305}
306
307void evemu_destroy(int fd)
308{
309 int ret;
310 SYSCALL(ret = ioctl(fd, UI_DEV_DESTROY, NULL));
311}