]>
Commit | Line | Data |
---|---|---|
06914c97 MAL |
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 | ||
6f5fd837 SH |
20 | enum { |
21 | VHOST_USER_INPUT_MAX_QUEUES = 2, | |
22 | }; | |
23 | ||
06914c97 MAL |
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)); | |
be32fd9e MAL |
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 | } | |
06914c97 MAL |
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); | |
24af03b9 MAL |
378 | if (lsock < 0) { |
379 | g_printerr("Failed to listen on %s.\n", opt_socket_path); | |
380 | exit(EXIT_FAILURE); | |
381 | } | |
06914c97 MAL |
382 | fd = accept(lsock, NULL, NULL); |
383 | close(lsock); | |
384 | } else { | |
385 | fd = opt_fdnum; | |
386 | } | |
387 | if (fd == -1) { | |
f55411cf | 388 | g_printerr("Invalid vhost-user socket.\n"); |
06914c97 MAL |
389 | exit(EXIT_FAILURE); |
390 | } | |
6f5fd837 SH |
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 | } | |
06914c97 MAL |
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 | } |