1 | /* |
2 | * This work is licensed under the terms of the GNU GPL, version 2 or |
3 | * (at your option) any later version. See the COPYING file in the |
4 | * top-level directory. |
5 | */ |
6 | |
7 | #include "qemu/osdep.h" |
8 | |
9 | #include <glib.h> |
10 | #include <linux/input.h> |
11 | |
12 | #include "qemu/iov.h" |
13 | #include "qemu/bswap.h" |
14 | #include "qemu/sockets.h" |
15 | #include "contrib/libvhost-user/libvhost-user.h" |
16 | #include "contrib/libvhost-user/libvhost-user-glib.h" |
17 | #include "standard-headers/linux/virtio_input.h" |
18 | #include "qapi/error.h" |
19 | |
20 | enum { |
21 | VHOST_USER_INPUT_MAX_QUEUES = 2, |
22 | }; |
23 | |
24 | typedef struct virtio_input_event virtio_input_event; |
25 | typedef struct virtio_input_config virtio_input_config; |
26 | |
27 | typedef struct VuInput { |
28 | VugDev dev; |
29 | GSource *evsrc; |
30 | int evdevfd; |
31 | GArray *config; |
32 | virtio_input_config *sel_config; |
33 | struct { |
34 | virtio_input_event event; |
35 | VuVirtqElement *elem; |
36 | } *queue; |
37 | uint32_t qindex, qsize; |
38 | } VuInput; |
39 | |
40 | static void vi_input_send(VuInput *vi, struct virtio_input_event *event) |
41 | { |
42 | VuDev *dev = &vi->dev.parent; |
43 | VuVirtq *vq = vu_get_queue(dev, 0); |
44 | VuVirtqElement *elem; |
45 | int i, len; |
46 | |
47 | /* queue up events ... */ |
48 | if (vi->qindex == vi->qsize) { |
49 | vi->qsize++; |
50 | vi->queue = g_realloc_n(vi->queue, vi->qsize, sizeof(vi->queue[0])); |
51 | } |
52 | vi->queue[vi->qindex++].event = *event; |
53 | |
54 | /* ... until we see a report sync ... */ |
55 | if (event->type != htole16(EV_SYN) || |
56 | event->code != htole16(SYN_REPORT)) { |
57 | return; |
58 | } |
59 | |
60 | /* ... then check available space ... */ |
61 | for (i = 0; i < vi->qindex; i++) { |
62 | elem = vu_queue_pop(dev, vq, sizeof(VuVirtqElement)); |
63 | if (!elem) { |
64 | while (--i >= 0) { |
65 | vu_queue_unpop(dev, vq, vi->queue[i].elem, 0); |
66 | } |
67 | vi->qindex = 0; |
68 | g_warning("virtio-input queue full" ); |
69 | return; |
70 | } |
71 | vi->queue[i].elem = elem; |
72 | } |
73 | |
74 | /* ... and finally pass them to the guest */ |
75 | for (i = 0; i < vi->qindex; i++) { |
76 | elem = vi->queue[i].elem; |
77 | len = iov_from_buf(elem->in_sg, elem->in_num, |
78 | 0, &vi->queue[i].event, sizeof(virtio_input_event)); |
79 | vu_queue_push(dev, vq, elem, len); |
80 | g_free(elem); |
81 | } |
82 | |
83 | vu_queue_notify(&vi->dev.parent, vq); |
84 | vi->qindex = 0; |
85 | } |
86 | |
87 | static void |
88 | vi_evdev_watch(VuDev *dev, int condition, void *data) |
89 | { |
90 | VuInput *vi = data; |
91 | int fd = vi->evdevfd; |
92 | |
93 | g_debug("Got evdev condition %x" , condition); |
94 | |
95 | struct virtio_input_event virtio; |
96 | struct input_event evdev; |
97 | int rc; |
98 | |
99 | for (;;) { |
100 | rc = read(fd, &evdev, sizeof(evdev)); |
101 | if (rc != sizeof(evdev)) { |
102 | break; |
103 | } |
104 | |
105 | g_debug("input %d %d %d" , evdev.type, evdev.code, evdev.value); |
106 | |
107 | virtio.type = htole16(evdev.type); |
108 | virtio.code = htole16(evdev.code); |
109 | virtio.value = htole32(evdev.value); |
110 | vi_input_send(vi, &virtio); |
111 | } |
112 | } |
113 | |
114 | |
115 | static void vi_handle_status(VuInput *vi, virtio_input_event *event) |
116 | { |
117 | struct input_event evdev; |
118 | int rc; |
119 | |
120 | if (gettimeofday(&evdev.time, NULL)) { |
121 | perror("vi_handle_status: gettimeofday" ); |
122 | return; |
123 | } |
124 | |
125 | evdev.type = le16toh(event->type); |
126 | evdev.code = le16toh(event->code); |
127 | evdev.value = le32toh(event->value); |
128 | |
129 | rc = write(vi->evdevfd, &evdev, sizeof(evdev)); |
130 | if (rc == -1) { |
131 | perror("vi_host_handle_status: write" ); |
132 | } |
133 | } |
134 | |
135 | static void vi_handle_sts(VuDev *dev, int qidx) |
136 | { |
137 | VuInput *vi = container_of(dev, VuInput, dev.parent); |
138 | VuVirtq *vq = vu_get_queue(dev, qidx); |
139 | virtio_input_event event; |
140 | VuVirtqElement *elem; |
141 | int len; |
142 | |
143 | g_debug("%s" , G_STRFUNC); |
144 | |
145 | for (;;) { |
146 | elem = vu_queue_pop(dev, vq, sizeof(VuVirtqElement)); |
147 | if (!elem) { |
148 | break; |
149 | } |
150 | |
151 | memset(&event, 0, sizeof(event)); |
152 | len = iov_to_buf(elem->out_sg, elem->out_num, |
153 | 0, &event, sizeof(event)); |
154 | vi_handle_status(vi, &event); |
155 | vu_queue_push(dev, vq, elem, len); |
156 | g_free(elem); |
157 | } |
158 | |
159 | vu_queue_notify(&vi->dev.parent, vq); |
160 | } |
161 | |
162 | static void |
163 | vi_panic(VuDev *dev, const char *msg) |
164 | { |
165 | g_critical("%s\n" , msg); |
166 | exit(EXIT_FAILURE); |
167 | } |
168 | |
169 | static void |
170 | vi_queue_set_started(VuDev *dev, int qidx, bool started) |
171 | { |
172 | VuInput *vi = container_of(dev, VuInput, dev.parent); |
173 | VuVirtq *vq = vu_get_queue(dev, qidx); |
174 | |
175 | g_debug("queue started %d:%d" , qidx, started); |
176 | |
177 | if (qidx == 1) { |
178 | vu_set_queue_handler(dev, vq, started ? vi_handle_sts : NULL); |
179 | } |
180 | |
181 | started = vu_queue_started(dev, vu_get_queue(dev, 0)) && |
182 | vu_queue_started(dev, vu_get_queue(dev, 1)); |
183 | |
184 | if (started && !vi->evsrc) { |
185 | vi->evsrc = vug_source_new(&vi->dev, vi->evdevfd, |
186 | G_IO_IN, vi_evdev_watch, vi); |
187 | } |
188 | |
189 | if (!started && vi->evsrc) { |
190 | g_source_destroy(vi->evsrc); |
191 | vi->evsrc = NULL; |
192 | } |
193 | } |
194 | |
195 | static virtio_input_config * |
196 | vi_find_config(VuInput *vi, uint8_t select, uint8_t subsel) |
197 | { |
198 | virtio_input_config *cfg; |
199 | int i; |
200 | |
201 | for (i = 0; i < vi->config->len; i++) { |
202 | cfg = &g_array_index(vi->config, virtio_input_config, i); |
203 | if (select == cfg->select && subsel == cfg->subsel) { |
204 | return cfg; |
205 | } |
206 | } |
207 | |
208 | return NULL; |
209 | } |
210 | |
211 | static int vi_get_config(VuDev *dev, uint8_t *config, uint32_t len) |
212 | { |
213 | VuInput *vi = container_of(dev, VuInput, dev.parent); |
214 | |
215 | g_return_val_if_fail(len <= sizeof(*vi->sel_config), -1); |
216 | |
217 | if (vi->sel_config) { |
218 | memcpy(config, vi->sel_config, len); |
219 | } else { |
220 | memset(config, 0, len); |
221 | } |
222 | |
223 | return 0; |
224 | } |
225 | |
226 | static int vi_set_config(VuDev *dev, const uint8_t *data, |
227 | uint32_t offset, uint32_t size, |
228 | uint32_t flags) |
229 | { |
230 | VuInput *vi = container_of(dev, VuInput, dev.parent); |
231 | virtio_input_config *config = (virtio_input_config *)data; |
232 | |
233 | vi->sel_config = vi_find_config(vi, config->select, config->subsel); |
234 | |
235 | return 0; |
236 | } |
237 | |
238 | static const VuDevIface vuiface = { |
239 | .queue_set_started = vi_queue_set_started, |
240 | .get_config = vi_get_config, |
241 | .set_config = vi_set_config, |
242 | }; |
243 | |
244 | static void |
245 | vi_bits_config(VuInput *vi, int type, int count) |
246 | { |
247 | virtio_input_config bits; |
248 | int rc, i, size = 0; |
249 | |
250 | memset(&bits, 0, sizeof(bits)); |
251 | rc = ioctl(vi->evdevfd, EVIOCGBIT(type, count / 8), bits.u.bitmap); |
252 | if (rc < 0) { |
253 | return; |
254 | } |
255 | |
256 | for (i = 0; i < count / 8; i++) { |
257 | if (bits.u.bitmap[i]) { |
258 | size = i + 1; |
259 | } |
260 | } |
261 | if (size == 0) { |
262 | return; |
263 | } |
264 | |
265 | bits.select = VIRTIO_INPUT_CFG_EV_BITS; |
266 | bits.subsel = type; |
267 | bits.size = size; |
268 | g_array_append_val(vi->config, bits); |
269 | } |
270 | |
271 | static char *opt_evdev; |
272 | static int opt_fdnum = -1; |
273 | static char *opt_socket_path; |
274 | static gboolean opt_nograb; |
275 | static gboolean opt_print_caps; |
276 | |
277 | static GOptionEntry entries[] = { |
278 | { "print-capabilities" , 'c', 0, G_OPTION_ARG_NONE, &opt_print_caps, |
279 | "Print capabilities" , NULL }, |
280 | { "no-grab" , 'n', 0, G_OPTION_ARG_NONE, &opt_nograb, |
281 | "Don't grab device" , NULL }, |
282 | { "fd" , 'f', 0, G_OPTION_ARG_INT, &opt_fdnum, |
283 | "Use inherited fd socket" , "FDNUM" }, |
284 | { "socket-path" , 's', 0, G_OPTION_ARG_FILENAME, &opt_socket_path, |
285 | "Use UNIX socket path" , "PATH" }, |
286 | { "evdev-path" , 'p', 0, G_OPTION_ARG_FILENAME, &opt_evdev, |
287 | "evdev input device path" , "PATH" }, |
288 | { NULL, } |
289 | }; |
290 | |
291 | int |
292 | main(int argc, char *argv[]) |
293 | { |
294 | GMainLoop *loop = NULL; |
295 | VuInput vi = { 0, }; |
296 | int rc, ver, fd; |
297 | virtio_input_config id; |
298 | struct input_id ids; |
299 | GError *error = NULL; |
300 | GOptionContext *context; |
301 | |
302 | context = g_option_context_new(NULL); |
303 | g_option_context_add_main_entries(context, entries, NULL); |
304 | if (!g_option_context_parse(context, &argc, &argv, &error)) { |
305 | g_printerr("Option parsing failed: %s\n" , error->message); |
306 | exit(EXIT_FAILURE); |
307 | } |
308 | if (opt_print_caps) { |
309 | g_print("{\n" ); |
310 | g_print(" \"type\": \"input\",\n" ); |
311 | g_print(" \"features\": [\n" ); |
312 | g_print(" \"evdev-path\",\n" ); |
313 | g_print(" \"no-grab\"\n" ); |
314 | g_print(" ]\n" ); |
315 | g_print("}\n" ); |
316 | exit(EXIT_SUCCESS); |
317 | } |
318 | if (!opt_evdev) { |
319 | g_printerr("Please specify an evdev path\n" ); |
320 | exit(EXIT_FAILURE); |
321 | } |
322 | if ((!!opt_socket_path + (opt_fdnum != -1)) != 1) { |
323 | g_printerr("Please specify either --fd or --socket-path\n" ); |
324 | exit(EXIT_FAILURE); |
325 | } |
326 | |
327 | vi.evdevfd = open(opt_evdev, O_RDWR); |
328 | if (vi.evdevfd < 0) { |
329 | g_printerr("Failed to open evdev: %s\n" , g_strerror(errno)); |
330 | exit(EXIT_FAILURE); |
331 | } |
332 | |
333 | rc = ioctl(vi.evdevfd, EVIOCGVERSION, &ver); |
334 | if (rc < 0) { |
335 | g_printerr("%s: is not an evdev device\n" , argv[1]); |
336 | exit(EXIT_FAILURE); |
337 | } |
338 | |
339 | if (!opt_nograb) { |
340 | rc = ioctl(vi.evdevfd, EVIOCGRAB, 1); |
341 | if (rc < 0) { |
342 | g_printerr("Failed to grab device\n" ); |
343 | exit(EXIT_FAILURE); |
344 | } |
345 | } |
346 | |
347 | vi.config = g_array_new(false, false, sizeof(virtio_input_config)); |
348 | memset(&id, 0, sizeof(id)); |
349 | if (ioctl(vi.evdevfd, EVIOCGNAME(sizeof(id.u.string) - 1), |
350 | id.u.string) < 0) { |
351 | g_printerr("Failed to get evdev name: %s\n" , g_strerror(errno)); |
352 | exit(EXIT_FAILURE); |
353 | } |
354 | id.select = VIRTIO_INPUT_CFG_ID_NAME; |
355 | id.size = strlen(id.u.string); |
356 | g_array_append_val(vi.config, id); |
357 | |
358 | if (ioctl(vi.evdevfd, EVIOCGID, &ids) == 0) { |
359 | memset(&id, 0, sizeof(id)); |
360 | id.select = VIRTIO_INPUT_CFG_ID_DEVIDS; |
361 | id.size = sizeof(struct virtio_input_devids); |
362 | id.u.ids.bustype = cpu_to_le16(ids.bustype); |
363 | id.u.ids.vendor = cpu_to_le16(ids.vendor); |
364 | id.u.ids.product = cpu_to_le16(ids.product); |
365 | id.u.ids.version = cpu_to_le16(ids.version); |
366 | g_array_append_val(vi.config, id); |
367 | } |
368 | |
369 | vi_bits_config(&vi, EV_KEY, KEY_CNT); |
370 | vi_bits_config(&vi, EV_REL, REL_CNT); |
371 | vi_bits_config(&vi, EV_ABS, ABS_CNT); |
372 | vi_bits_config(&vi, EV_MSC, MSC_CNT); |
373 | vi_bits_config(&vi, EV_SW, SW_CNT); |
374 | g_debug("config length: %u" , vi.config->len); |
375 | |
376 | if (opt_socket_path) { |
377 | int lsock = unix_listen(opt_socket_path, &error_fatal); |
378 | if (lsock < 0) { |
379 | g_printerr("Failed to listen on %s.\n" , opt_socket_path); |
380 | exit(EXIT_FAILURE); |
381 | } |
382 | fd = accept(lsock, NULL, NULL); |
383 | close(lsock); |
384 | } else { |
385 | fd = opt_fdnum; |
386 | } |
387 | if (fd == -1) { |
388 | g_printerr("Invalid vhost-user socket.\n" ); |
389 | exit(EXIT_FAILURE); |
390 | } |
391 | |
392 | if (!vug_init(&vi.dev, VHOST_USER_INPUT_MAX_QUEUES, fd, vi_panic, |
393 | &vuiface)) { |
394 | g_printerr("Failed to initialize libvhost-user-glib.\n" ); |
395 | exit(EXIT_FAILURE); |
396 | } |
397 | |
398 | loop = g_main_loop_new(NULL, FALSE); |
399 | g_main_loop_run(loop); |
400 | g_main_loop_unref(loop); |
401 | |
402 | vug_deinit(&vi.dev); |
403 | |
404 | if (vi.evsrc) { |
405 | g_source_unref(vi.evsrc); |
406 | } |
407 | g_array_free(vi.config, TRUE); |
408 | g_free(vi.queue); |
409 | return 0; |
410 | } |
411 | |