1b0de25cbSAndy Fiddaman /*-
2*32640292SAndy Fiddaman  * SPDX-License-Identifier: BSD-2-Clause
3b0de25cbSAndy Fiddaman  *
4b0de25cbSAndy Fiddaman  * Copyright (c) 2021 Beckhoff Automation GmbH & Co. KG
5b0de25cbSAndy Fiddaman  * All rights reserved.
6b0de25cbSAndy Fiddaman  *
7b0de25cbSAndy Fiddaman  * Redistribution and use in source and binary forms, with or without
8b0de25cbSAndy Fiddaman  * modification, are permitted provided that the following conditions
9b0de25cbSAndy Fiddaman  * are met:
10b0de25cbSAndy Fiddaman  * 1. Redistributions of source code must retain the above copyright
11b0de25cbSAndy Fiddaman  *    notice, this list of conditions and the following disclaimer
12b0de25cbSAndy Fiddaman  *    in this position and unchanged.
13b0de25cbSAndy Fiddaman  * 2. Redistributions in binary form must reproduce the above copyright
14b0de25cbSAndy Fiddaman  *    notice, this list of conditions and the following disclaimer in the
15b0de25cbSAndy Fiddaman  *    documentation and/or other materials provided with the distribution.
16b0de25cbSAndy Fiddaman  *
17b0de25cbSAndy Fiddaman  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18b0de25cbSAndy Fiddaman  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19b0de25cbSAndy Fiddaman  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20b0de25cbSAndy Fiddaman  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21b0de25cbSAndy Fiddaman  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22b0de25cbSAndy Fiddaman  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23b0de25cbSAndy Fiddaman  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24b0de25cbSAndy Fiddaman  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25b0de25cbSAndy Fiddaman  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26b0de25cbSAndy Fiddaman  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27b0de25cbSAndy Fiddaman  * SUCH DAMAGE.
28b0de25cbSAndy Fiddaman  */
29b0de25cbSAndy Fiddaman 
30b0de25cbSAndy Fiddaman /*
31b0de25cbSAndy Fiddaman  * virtio input device emulation.
32b0de25cbSAndy Fiddaman  */
33b0de25cbSAndy Fiddaman 
34b0de25cbSAndy Fiddaman #include <sys/cdefs.h>
35b0de25cbSAndy Fiddaman 
36b0de25cbSAndy Fiddaman #include <sys/param.h>
37b0de25cbSAndy Fiddaman #ifndef WITHOUT_CAPSICUM
38b0de25cbSAndy Fiddaman #include <sys/capsicum.h>
39b0de25cbSAndy Fiddaman 
40b0de25cbSAndy Fiddaman #include <capsicum_helpers.h>
41b0de25cbSAndy Fiddaman #endif
42b0de25cbSAndy Fiddaman #include <sys/ioctl.h>
43b0de25cbSAndy Fiddaman #include <sys/linker_set.h>
44b0de25cbSAndy Fiddaman #include <sys/uio.h>
45b0de25cbSAndy Fiddaman 
46b0de25cbSAndy Fiddaman #include <dev/evdev/input.h>
47b0de25cbSAndy Fiddaman 
48b0de25cbSAndy Fiddaman #include <assert.h>
49b0de25cbSAndy Fiddaman #include <err.h>
50b0de25cbSAndy Fiddaman #include <errno.h>
51b0de25cbSAndy Fiddaman #include <fcntl.h>
52b0de25cbSAndy Fiddaman #include <pthread.h>
53b0de25cbSAndy Fiddaman #include <stddef.h>
54b0de25cbSAndy Fiddaman #include <stdio.h>
55b0de25cbSAndy Fiddaman #include <stdlib.h>
56b0de25cbSAndy Fiddaman #include <string.h>
57b0de25cbSAndy Fiddaman #include <sysexits.h>
58b0de25cbSAndy Fiddaman #include <unistd.h>
59b0de25cbSAndy Fiddaman 
60b0de25cbSAndy Fiddaman #include "bhyverun.h"
61b0de25cbSAndy Fiddaman #include "config.h"
62b0de25cbSAndy Fiddaman #include "debug.h"
63b0de25cbSAndy Fiddaman #include "mevent.h"
64b0de25cbSAndy Fiddaman #include "pci_emul.h"
65b0de25cbSAndy Fiddaman #include "virtio.h"
66b0de25cbSAndy Fiddaman 
67b0de25cbSAndy Fiddaman #define VTINPUT_RINGSZ 64
68b0de25cbSAndy Fiddaman 
69b0de25cbSAndy Fiddaman #define VTINPUT_MAX_PKT_LEN 10
70b0de25cbSAndy Fiddaman 
71b0de25cbSAndy Fiddaman /*
72b0de25cbSAndy Fiddaman  * Queue definitions.
73b0de25cbSAndy Fiddaman  */
74b0de25cbSAndy Fiddaman #define VTINPUT_EVENTQ 0
75b0de25cbSAndy Fiddaman #define VTINPUT_STATUSQ 1
76b0de25cbSAndy Fiddaman 
77b0de25cbSAndy Fiddaman #define VTINPUT_MAXQ 2
78b0de25cbSAndy Fiddaman 
79b0de25cbSAndy Fiddaman static int pci_vtinput_debug;
80b0de25cbSAndy Fiddaman #define DPRINTF(params)        \
81b0de25cbSAndy Fiddaman 	if (pci_vtinput_debug) \
82b0de25cbSAndy Fiddaman 	PRINTLN params
83b0de25cbSAndy Fiddaman #define WPRINTF(params) PRINTLN params
84b0de25cbSAndy Fiddaman 
85b0de25cbSAndy Fiddaman enum vtinput_config_select {
86b0de25cbSAndy Fiddaman 	VTINPUT_CFG_UNSET = 0x00,
87b0de25cbSAndy Fiddaman 	VTINPUT_CFG_ID_NAME = 0x01,
88b0de25cbSAndy Fiddaman 	VTINPUT_CFG_ID_SERIAL = 0x02,
89b0de25cbSAndy Fiddaman 	VTINPUT_CFG_ID_DEVIDS = 0x03,
90b0de25cbSAndy Fiddaman 	VTINPUT_CFG_PROP_BITS = 0x10,
91b0de25cbSAndy Fiddaman 	VTINPUT_CFG_EV_BITS = 0x11,
92b0de25cbSAndy Fiddaman 	VTINPUT_CFG_ABS_INFO = 0x12
93b0de25cbSAndy Fiddaman };
94b0de25cbSAndy Fiddaman 
95b0de25cbSAndy Fiddaman struct vtinput_absinfo {
96b0de25cbSAndy Fiddaman 	uint32_t min;
97b0de25cbSAndy Fiddaman 	uint32_t max;
98b0de25cbSAndy Fiddaman 	uint32_t fuzz;
99b0de25cbSAndy Fiddaman 	uint32_t flat;
100b0de25cbSAndy Fiddaman 	uint32_t res;
101b0de25cbSAndy Fiddaman } __packed;
102b0de25cbSAndy Fiddaman 
103b0de25cbSAndy Fiddaman struct vtinput_devids {
104b0de25cbSAndy Fiddaman 	uint16_t bustype;
105b0de25cbSAndy Fiddaman 	uint16_t vendor;
106b0de25cbSAndy Fiddaman 	uint16_t product;
107b0de25cbSAndy Fiddaman 	uint16_t version;
108b0de25cbSAndy Fiddaman } __packed;
109b0de25cbSAndy Fiddaman 
110b0de25cbSAndy Fiddaman struct vtinput_config {
111b0de25cbSAndy Fiddaman 	uint8_t select;
112b0de25cbSAndy Fiddaman 	uint8_t subsel;
113b0de25cbSAndy Fiddaman 	uint8_t size;
114b0de25cbSAndy Fiddaman 	uint8_t reserved[5];
115b0de25cbSAndy Fiddaman 	union {
116b0de25cbSAndy Fiddaman 		char string[128];
117b0de25cbSAndy Fiddaman 		uint8_t bitmap[128];
118b0de25cbSAndy Fiddaman 		struct vtinput_absinfo abs;
119b0de25cbSAndy Fiddaman 		struct vtinput_devids ids;
120b0de25cbSAndy Fiddaman 	} u;
121b0de25cbSAndy Fiddaman } __packed;
122b0de25cbSAndy Fiddaman 
123b0de25cbSAndy Fiddaman struct vtinput_event {
124b0de25cbSAndy Fiddaman 	uint16_t type;
125b0de25cbSAndy Fiddaman 	uint16_t code;
126b0de25cbSAndy Fiddaman 	uint32_t value;
127b0de25cbSAndy Fiddaman } __packed;
128b0de25cbSAndy Fiddaman 
129b0de25cbSAndy Fiddaman struct vtinput_event_elem {
130b0de25cbSAndy Fiddaman 	struct vtinput_event event;
131b0de25cbSAndy Fiddaman 	struct iovec iov;
132b0de25cbSAndy Fiddaman 	uint16_t idx;
133b0de25cbSAndy Fiddaman };
134b0de25cbSAndy Fiddaman 
135b0de25cbSAndy Fiddaman struct vtinput_eventqueue {
136b0de25cbSAndy Fiddaman 	struct vtinput_event_elem *events;
137b0de25cbSAndy Fiddaman 	uint32_t size;
138b0de25cbSAndy Fiddaman 	uint32_t idx;
139b0de25cbSAndy Fiddaman };
140b0de25cbSAndy Fiddaman 
141b0de25cbSAndy Fiddaman /*
142b0de25cbSAndy Fiddaman  * Per-device softc
143b0de25cbSAndy Fiddaman  */
144b0de25cbSAndy Fiddaman struct pci_vtinput_softc {
145b0de25cbSAndy Fiddaman 	struct virtio_softc vsc_vs;
146b0de25cbSAndy Fiddaman 	struct vqueue_info vsc_queues[VTINPUT_MAXQ];
147b0de25cbSAndy Fiddaman 	pthread_mutex_t vsc_mtx;
148b0de25cbSAndy Fiddaman 	const char *vsc_evdev;
149b0de25cbSAndy Fiddaman 	int vsc_fd;
150b0de25cbSAndy Fiddaman 	struct vtinput_config vsc_config;
151b0de25cbSAndy Fiddaman 	int vsc_config_valid;
152b0de25cbSAndy Fiddaman 	struct mevent *vsc_evp;
153b0de25cbSAndy Fiddaman 	struct vtinput_eventqueue vsc_eventqueue;
154b0de25cbSAndy Fiddaman };
155b0de25cbSAndy Fiddaman 
156b0de25cbSAndy Fiddaman static void pci_vtinput_reset(void *);
157b0de25cbSAndy Fiddaman static int pci_vtinput_cfgread(void *, int, int, uint32_t *);
158b0de25cbSAndy Fiddaman static int pci_vtinput_cfgwrite(void *, int, int, uint32_t);
159b0de25cbSAndy Fiddaman 
160b0de25cbSAndy Fiddaman static struct virtio_consts vtinput_vi_consts = {
16159d65d31SAndy Fiddaman 	.vc_name =	"vtinput",
16259d65d31SAndy Fiddaman 	.vc_nvq =	VTINPUT_MAXQ,
16359d65d31SAndy Fiddaman 	.vc_cfgsize =	sizeof(struct vtinput_config),
16459d65d31SAndy Fiddaman 	.vc_reset =	pci_vtinput_reset,
16559d65d31SAndy Fiddaman 	.vc_cfgread =	pci_vtinput_cfgread,
16659d65d31SAndy Fiddaman 	.vc_cfgwrite =	pci_vtinput_cfgwrite,
16759d65d31SAndy Fiddaman 	.vc_hv_caps =	0,
168b0de25cbSAndy Fiddaman };
169b0de25cbSAndy Fiddaman 
170b0de25cbSAndy Fiddaman static void
pci_vtinput_reset(void * vsc)171b0de25cbSAndy Fiddaman pci_vtinput_reset(void *vsc)
172b0de25cbSAndy Fiddaman {
173b0de25cbSAndy Fiddaman 	struct pci_vtinput_softc *sc = vsc;
174b0de25cbSAndy Fiddaman 
175b0de25cbSAndy Fiddaman 	DPRINTF(("%s: device reset requested", __func__));
176b0de25cbSAndy Fiddaman 	vi_reset_dev(&sc->vsc_vs);
177b0de25cbSAndy Fiddaman }
178b0de25cbSAndy Fiddaman 
179b0de25cbSAndy Fiddaman static void
pci_vtinput_notify_eventq(void * vsc __unused,struct vqueue_info * vq __unused)18059d65d31SAndy Fiddaman pci_vtinput_notify_eventq(void *vsc __unused, struct vqueue_info *vq __unused)
181b0de25cbSAndy Fiddaman {
182b0de25cbSAndy Fiddaman 	DPRINTF(("%s", __func__));
183b0de25cbSAndy Fiddaman }
184b0de25cbSAndy Fiddaman 
185b0de25cbSAndy Fiddaman static void
pci_vtinput_notify_statusq(void * vsc,struct vqueue_info * vq)186b0de25cbSAndy Fiddaman pci_vtinput_notify_statusq(void *vsc, struct vqueue_info *vq)
187b0de25cbSAndy Fiddaman {
188b0de25cbSAndy Fiddaman 	struct pci_vtinput_softc *sc = vsc;
189b0de25cbSAndy Fiddaman 
190b0de25cbSAndy Fiddaman 	while (vq_has_descs(vq)) {
191b0de25cbSAndy Fiddaman 		/* get descriptor chain */
192b0de25cbSAndy Fiddaman 		struct iovec iov;
193b0de25cbSAndy Fiddaman 		struct vi_req req;
194b0de25cbSAndy Fiddaman 		const int n = vq_getchain(vq, &iov, 1, &req);
195b0de25cbSAndy Fiddaman 		if (n <= 0) {
196b0de25cbSAndy Fiddaman 			WPRINTF(("%s: invalid descriptor: %d", __func__, n));
197b0de25cbSAndy Fiddaman 			return;
198b0de25cbSAndy Fiddaman 		}
199b0de25cbSAndy Fiddaman 
200b0de25cbSAndy Fiddaman 		/* get event */
201b0de25cbSAndy Fiddaman 		struct vtinput_event event;
202b0de25cbSAndy Fiddaman 		memcpy(&event, iov.iov_base, sizeof(event));
203b0de25cbSAndy Fiddaman 
204b0de25cbSAndy Fiddaman 		/*
205b0de25cbSAndy Fiddaman 		 * on multi touch devices:
206b0de25cbSAndy Fiddaman 		 * - host send EV_MSC to guest
207b0de25cbSAndy Fiddaman 		 * - guest sends EV_MSC back to host
208b0de25cbSAndy Fiddaman 		 * - host writes EV_MSC to evdev
209b0de25cbSAndy Fiddaman 		 * - evdev saves EV_MSC in it's event buffer
210b0de25cbSAndy Fiddaman 		 * - host receives an extra EV_MSC by reading the evdev event
211b0de25cbSAndy Fiddaman 		 *   buffer
212b0de25cbSAndy Fiddaman 		 * - frames become larger and larger
213b0de25cbSAndy Fiddaman 		 * avoid endless loops by ignoring EV_MSC
214b0de25cbSAndy Fiddaman 		 */
215b0de25cbSAndy Fiddaman 		if (event.type == EV_MSC) {
216b0de25cbSAndy Fiddaman 			vq_relchain(vq, req.idx, sizeof(event));
217b0de25cbSAndy Fiddaman 			continue;
218b0de25cbSAndy Fiddaman 		}
219b0de25cbSAndy Fiddaman 
220b0de25cbSAndy Fiddaman 		/* send event to evdev */
221b0de25cbSAndy Fiddaman 		struct input_event host_event;
222b0de25cbSAndy Fiddaman 		host_event.type = event.type;
223b0de25cbSAndy Fiddaman 		host_event.code = event.code;
224b0de25cbSAndy Fiddaman 		host_event.value = event.value;
225b0de25cbSAndy Fiddaman 		if (gettimeofday(&host_event.time, NULL) != 0) {
226b0de25cbSAndy Fiddaman 			WPRINTF(("%s: failed gettimeofday", __func__));
227b0de25cbSAndy Fiddaman 		}
228b0de25cbSAndy Fiddaman 		if (write(sc->vsc_fd, &host_event, sizeof(host_event)) == -1) {
229b0de25cbSAndy Fiddaman 			WPRINTF(("%s: failed to write host_event", __func__));
230b0de25cbSAndy Fiddaman 		}
231b0de25cbSAndy Fiddaman 
232b0de25cbSAndy Fiddaman 		vq_relchain(vq, req.idx, sizeof(event));
233b0de25cbSAndy Fiddaman 	}
234b0de25cbSAndy Fiddaman 	vq_endchains(vq, 1);
235b0de25cbSAndy Fiddaman }
236b0de25cbSAndy Fiddaman 
237b0de25cbSAndy Fiddaman static int
pci_vtinput_get_bitmap(struct pci_vtinput_softc * sc,int cmd,int count)238b0de25cbSAndy Fiddaman pci_vtinput_get_bitmap(struct pci_vtinput_softc *sc, int cmd, int count)
239b0de25cbSAndy Fiddaman {
240b0de25cbSAndy Fiddaman 	if (count <= 0 || !sc) {
241b0de25cbSAndy Fiddaman 		return (-1);
242b0de25cbSAndy Fiddaman 	}
243b0de25cbSAndy Fiddaman 
244b0de25cbSAndy Fiddaman 	/* query bitmap */
245b0de25cbSAndy Fiddaman 	memset(sc->vsc_config.u.bitmap, 0, sizeof(sc->vsc_config.u.bitmap));
246b0de25cbSAndy Fiddaman 	if (ioctl(sc->vsc_fd, cmd, sc->vsc_config.u.bitmap) < 0) {
247b0de25cbSAndy Fiddaman 		return (-1);
248b0de25cbSAndy Fiddaman 	}
249b0de25cbSAndy Fiddaman 
250b0de25cbSAndy Fiddaman 	/* get number of set bytes in bitmap */
251b0de25cbSAndy Fiddaman 	for (int i = count - 1; i >= 0; i--) {
252b0de25cbSAndy Fiddaman 		if (sc->vsc_config.u.bitmap[i]) {
253b0de25cbSAndy Fiddaman 			return i + 1;
254b0de25cbSAndy Fiddaman 		}
255b0de25cbSAndy Fiddaman 	}
256b0de25cbSAndy Fiddaman 
257b0de25cbSAndy Fiddaman 	return (-1);
258b0de25cbSAndy Fiddaman }
259b0de25cbSAndy Fiddaman 
260b0de25cbSAndy Fiddaman static int
pci_vtinput_read_config_id_name(struct pci_vtinput_softc * sc)261b0de25cbSAndy Fiddaman pci_vtinput_read_config_id_name(struct pci_vtinput_softc *sc)
262b0de25cbSAndy Fiddaman {
263b0de25cbSAndy Fiddaman 	char name[128];
264b0de25cbSAndy Fiddaman 	if (ioctl(sc->vsc_fd, EVIOCGNAME(sizeof(name) - 1), name) < 0) {
265b0de25cbSAndy Fiddaman 		return (1);
266b0de25cbSAndy Fiddaman 	}
267b0de25cbSAndy Fiddaman 
268b0de25cbSAndy Fiddaman 	memcpy(sc->vsc_config.u.string, name, sizeof(name));
269b0de25cbSAndy Fiddaman 	sc->vsc_config.size = strnlen(name, sizeof(name));
270b0de25cbSAndy Fiddaman 
271b0de25cbSAndy Fiddaman 	return (0);
272b0de25cbSAndy Fiddaman }
273b0de25cbSAndy Fiddaman 
274b0de25cbSAndy Fiddaman static int
pci_vtinput_read_config_id_serial(struct pci_vtinput_softc * sc)275b0de25cbSAndy Fiddaman pci_vtinput_read_config_id_serial(struct pci_vtinput_softc *sc)
276b0de25cbSAndy Fiddaman {
277b0de25cbSAndy Fiddaman 	/* serial isn't supported */
278b0de25cbSAndy Fiddaman 	sc->vsc_config.size = 0;
279b0de25cbSAndy Fiddaman 
280b0de25cbSAndy Fiddaman 	return (0);
281b0de25cbSAndy Fiddaman }
282b0de25cbSAndy Fiddaman 
283b0de25cbSAndy Fiddaman static int
pci_vtinput_read_config_id_devids(struct pci_vtinput_softc * sc)284b0de25cbSAndy Fiddaman pci_vtinput_read_config_id_devids(struct pci_vtinput_softc *sc)
285b0de25cbSAndy Fiddaman {
286b0de25cbSAndy Fiddaman 	struct input_id devids;
287b0de25cbSAndy Fiddaman 	if (ioctl(sc->vsc_fd, EVIOCGID, &devids)) {
288b0de25cbSAndy Fiddaman 		return (1);
289b0de25cbSAndy Fiddaman 	}
290b0de25cbSAndy Fiddaman 
291b0de25cbSAndy Fiddaman 	sc->vsc_config.u.ids.bustype = devids.bustype;
292b0de25cbSAndy Fiddaman 	sc->vsc_config.u.ids.vendor = devids.vendor;
293b0de25cbSAndy Fiddaman 	sc->vsc_config.u.ids.product = devids.product;
294b0de25cbSAndy Fiddaman 	sc->vsc_config.u.ids.version = devids.version;
295b0de25cbSAndy Fiddaman 	sc->vsc_config.size = sizeof(struct vtinput_devids);
296b0de25cbSAndy Fiddaman 
297b0de25cbSAndy Fiddaman 	return (0);
298b0de25cbSAndy Fiddaman }
299b0de25cbSAndy Fiddaman 
300b0de25cbSAndy Fiddaman static int
pci_vtinput_read_config_prop_bits(struct pci_vtinput_softc * sc)301b0de25cbSAndy Fiddaman pci_vtinput_read_config_prop_bits(struct pci_vtinput_softc *sc)
302b0de25cbSAndy Fiddaman {
303b0de25cbSAndy Fiddaman 	/*
304b0de25cbSAndy Fiddaman 	 * Evdev bitmap countains 1 bit per count. Additionally evdev bitmaps
305b0de25cbSAndy Fiddaman 	 * are arrays of longs instead of chars. Calculate how many longs are
306b0de25cbSAndy Fiddaman 	 * required for evdev bitmap. Multiply that with sizeof(long) to get the
307b0de25cbSAndy Fiddaman 	 * number of elements.
308b0de25cbSAndy Fiddaman 	 */
309b0de25cbSAndy Fiddaman 	const int count = howmany(INPUT_PROP_CNT, sizeof(long) * 8) *
310b0de25cbSAndy Fiddaman 	    sizeof(long);
311b0de25cbSAndy Fiddaman 	const unsigned int cmd = EVIOCGPROP(count);
312b0de25cbSAndy Fiddaman 	const int size = pci_vtinput_get_bitmap(sc, cmd, count);
313b0de25cbSAndy Fiddaman 	if (size <= 0) {
314b0de25cbSAndy Fiddaman 		return (1);
315b0de25cbSAndy Fiddaman 	}
316b0de25cbSAndy Fiddaman 
317b0de25cbSAndy Fiddaman 	sc->vsc_config.size = size;
318b0de25cbSAndy Fiddaman 
319b0de25cbSAndy Fiddaman 	return (0);
320b0de25cbSAndy Fiddaman }
321b0de25cbSAndy Fiddaman 
322b0de25cbSAndy Fiddaman static int
pci_vtinput_read_config_ev_bits(struct pci_vtinput_softc * sc,uint8_t type)323b0de25cbSAndy Fiddaman pci_vtinput_read_config_ev_bits(struct pci_vtinput_softc *sc, uint8_t type)
324b0de25cbSAndy Fiddaman {
325b0de25cbSAndy Fiddaman 	int count;
326b0de25cbSAndy Fiddaman 
327b0de25cbSAndy Fiddaman 	switch (type) {
328b0de25cbSAndy Fiddaman 	case EV_KEY:
329b0de25cbSAndy Fiddaman 		count = KEY_CNT;
330b0de25cbSAndy Fiddaman 		break;
331b0de25cbSAndy Fiddaman 	case EV_REL:
332b0de25cbSAndy Fiddaman 		count = REL_CNT;
333b0de25cbSAndy Fiddaman 		break;
334b0de25cbSAndy Fiddaman 	case EV_ABS:
335b0de25cbSAndy Fiddaman 		count = ABS_CNT;
336b0de25cbSAndy Fiddaman 		break;
337b0de25cbSAndy Fiddaman 	case EV_MSC:
338b0de25cbSAndy Fiddaman 		count = MSC_CNT;
339b0de25cbSAndy Fiddaman 		break;
340b0de25cbSAndy Fiddaman 	case EV_SW:
341b0de25cbSAndy Fiddaman 		count = SW_CNT;
342b0de25cbSAndy Fiddaman 		break;
343b0de25cbSAndy Fiddaman 	case EV_LED:
344b0de25cbSAndy Fiddaman 		count = LED_CNT;
345b0de25cbSAndy Fiddaman 		break;
346b0de25cbSAndy Fiddaman 	default:
347b0de25cbSAndy Fiddaman 		return (1);
348b0de25cbSAndy Fiddaman 	}
349b0de25cbSAndy Fiddaman 
350b0de25cbSAndy Fiddaman 	/*
351b0de25cbSAndy Fiddaman 	 * Evdev bitmap countains 1 bit per count. Additionally evdev bitmaps
352b0de25cbSAndy Fiddaman 	 * are arrays of longs instead of chars. Calculate how many longs are
353b0de25cbSAndy Fiddaman 	 * required for evdev bitmap. Multiply that with sizeof(long) to get the
354b0de25cbSAndy Fiddaman 	 * number of elements.
355b0de25cbSAndy Fiddaman 	 */
356b0de25cbSAndy Fiddaman 	count = howmany(count, sizeof(long) * 8) * sizeof(long);
357b0de25cbSAndy Fiddaman 	const unsigned int cmd = EVIOCGBIT(sc->vsc_config.subsel, count);
358b0de25cbSAndy Fiddaman 	const int size = pci_vtinput_get_bitmap(sc, cmd, count);
359b0de25cbSAndy Fiddaman 	if (size <= 0) {
360b0de25cbSAndy Fiddaman 		return (1);
361b0de25cbSAndy Fiddaman 	}
362b0de25cbSAndy Fiddaman 
363b0de25cbSAndy Fiddaman 	sc->vsc_config.size = size;
364b0de25cbSAndy Fiddaman 
365b0de25cbSAndy Fiddaman 	return (0);
366b0de25cbSAndy Fiddaman }
367b0de25cbSAndy Fiddaman 
368b0de25cbSAndy Fiddaman static int
pci_vtinput_read_config_abs_info(struct pci_vtinput_softc * sc)369b0de25cbSAndy Fiddaman pci_vtinput_read_config_abs_info(struct pci_vtinput_softc *sc)
370b0de25cbSAndy Fiddaman {
371b0de25cbSAndy Fiddaman 	/* check if evdev has EV_ABS */
372b0de25cbSAndy Fiddaman 	if (!pci_vtinput_read_config_ev_bits(sc, EV_ABS)) {
373b0de25cbSAndy Fiddaman 		return (1);
374b0de25cbSAndy Fiddaman 	}
375b0de25cbSAndy Fiddaman 
376b0de25cbSAndy Fiddaman 	/* get abs information */
377b0de25cbSAndy Fiddaman 	struct input_absinfo abs;
378b0de25cbSAndy Fiddaman 	if (ioctl(sc->vsc_fd, EVIOCGABS(sc->vsc_config.subsel), &abs) < 0) {
379b0de25cbSAndy Fiddaman 		return (1);
380b0de25cbSAndy Fiddaman 	}
381b0de25cbSAndy Fiddaman 
382b0de25cbSAndy Fiddaman 	/* save abs information */
383b0de25cbSAndy Fiddaman 	sc->vsc_config.u.abs.min = abs.minimum;
384b0de25cbSAndy Fiddaman 	sc->vsc_config.u.abs.max = abs.maximum;
385b0de25cbSAndy Fiddaman 	sc->vsc_config.u.abs.fuzz = abs.fuzz;
386b0de25cbSAndy Fiddaman 	sc->vsc_config.u.abs.flat = abs.flat;
387b0de25cbSAndy Fiddaman 	sc->vsc_config.u.abs.res = abs.resolution;
388b0de25cbSAndy Fiddaman 	sc->vsc_config.size = sizeof(struct vtinput_absinfo);
389b0de25cbSAndy Fiddaman 
390b0de25cbSAndy Fiddaman 	return (0);
391b0de25cbSAndy Fiddaman }
392b0de25cbSAndy Fiddaman 
393b0de25cbSAndy Fiddaman static int
pci_vtinput_read_config(struct pci_vtinput_softc * sc)394b0de25cbSAndy Fiddaman pci_vtinput_read_config(struct pci_vtinput_softc *sc)
395b0de25cbSAndy Fiddaman {
396b0de25cbSAndy Fiddaman 	switch (sc->vsc_config.select) {
397b0de25cbSAndy Fiddaman 	case VTINPUT_CFG_UNSET:
398b0de25cbSAndy Fiddaman 		return (0);
399b0de25cbSAndy Fiddaman 	case VTINPUT_CFG_ID_NAME:
400b0de25cbSAndy Fiddaman 		return pci_vtinput_read_config_id_name(sc);
401b0de25cbSAndy Fiddaman 	case VTINPUT_CFG_ID_SERIAL:
402b0de25cbSAndy Fiddaman 		return pci_vtinput_read_config_id_serial(sc);
403b0de25cbSAndy Fiddaman 	case VTINPUT_CFG_ID_DEVIDS:
404b0de25cbSAndy Fiddaman 		return pci_vtinput_read_config_id_devids(sc);
405b0de25cbSAndy Fiddaman 	case VTINPUT_CFG_PROP_BITS:
406b0de25cbSAndy Fiddaman 		return pci_vtinput_read_config_prop_bits(sc);
407b0de25cbSAndy Fiddaman 	case VTINPUT_CFG_EV_BITS:
408b0de25cbSAndy Fiddaman 		return pci_vtinput_read_config_ev_bits(
409b0de25cbSAndy Fiddaman 		    sc, sc->vsc_config.subsel);
410b0de25cbSAndy Fiddaman 	case VTINPUT_CFG_ABS_INFO:
411b0de25cbSAndy Fiddaman 		return pci_vtinput_read_config_abs_info(sc);
412b0de25cbSAndy Fiddaman 	default:
413b0de25cbSAndy Fiddaman 		return (1);
414b0de25cbSAndy Fiddaman 	}
415b0de25cbSAndy Fiddaman }
416b0de25cbSAndy Fiddaman 
417b0de25cbSAndy Fiddaman static int
pci_vtinput_cfgread(void * vsc,int offset,int size,uint32_t * retval)418b0de25cbSAndy Fiddaman pci_vtinput_cfgread(void *vsc, int offset, int size, uint32_t *retval)
419b0de25cbSAndy Fiddaman {
420b0de25cbSAndy Fiddaman 	struct pci_vtinput_softc *sc = vsc;
421b0de25cbSAndy Fiddaman 
422b0de25cbSAndy Fiddaman 	/* check for valid offset and size */
42359d65d31SAndy Fiddaman 	if (offset + size > (int)sizeof(struct vtinput_config)) {
424b0de25cbSAndy Fiddaman 		WPRINTF(("%s: read to invalid offset/size %d/%d", __func__,
425b0de25cbSAndy Fiddaman 		    offset, size));
426b0de25cbSAndy Fiddaman 		memset(retval, 0, size);
427b0de25cbSAndy Fiddaman 		return (0);
428b0de25cbSAndy Fiddaman 	}
429b0de25cbSAndy Fiddaman 
430b0de25cbSAndy Fiddaman 	/* read new config values, if select and subsel changed. */
431b0de25cbSAndy Fiddaman 	if (!sc->vsc_config_valid) {
432b0de25cbSAndy Fiddaman 		if (pci_vtinput_read_config(sc) != 0) {
433b0de25cbSAndy Fiddaman 			DPRINTF(("%s: could not read config %d/%d", __func__,
434b0de25cbSAndy Fiddaman 			    sc->vsc_config.select, sc->vsc_config.subsel));
435b0de25cbSAndy Fiddaman 			memset(retval, 0, size);
436b0de25cbSAndy Fiddaman 			return (0);
437b0de25cbSAndy Fiddaman 		}
438b0de25cbSAndy Fiddaman 		sc->vsc_config_valid = 1;
439b0de25cbSAndy Fiddaman 	}
440b0de25cbSAndy Fiddaman 
441b0de25cbSAndy Fiddaman 	uint8_t *ptr = (uint8_t *)&sc->vsc_config;
442b0de25cbSAndy Fiddaman 	memcpy(retval, ptr + offset, size);
443b0de25cbSAndy Fiddaman 
444b0de25cbSAndy Fiddaman 	return (0);
445b0de25cbSAndy Fiddaman }
446b0de25cbSAndy Fiddaman 
447b0de25cbSAndy Fiddaman static int
pci_vtinput_cfgwrite(void * vsc,int offset,int size,uint32_t value)448b0de25cbSAndy Fiddaman pci_vtinput_cfgwrite(void *vsc, int offset, int size, uint32_t value)
449b0de25cbSAndy Fiddaman {
450b0de25cbSAndy Fiddaman 	struct pci_vtinput_softc *sc = vsc;
451b0de25cbSAndy Fiddaman 
452b0de25cbSAndy Fiddaman 	/* guest can only write to select and subsel fields */
453b0de25cbSAndy Fiddaman 	if (offset + size > 2) {
454b0de25cbSAndy Fiddaman 		WPRINTF(("%s: write to readonly reg %d", __func__, offset));
455b0de25cbSAndy Fiddaman 		return (1);
456b0de25cbSAndy Fiddaman 	}
457b0de25cbSAndy Fiddaman 
458b0de25cbSAndy Fiddaman 	/* copy value into config */
459b0de25cbSAndy Fiddaman 	uint8_t *ptr = (uint8_t *)&sc->vsc_config;
460b0de25cbSAndy Fiddaman 	memcpy(ptr + offset, &value, size);
461b0de25cbSAndy Fiddaman 
462b0de25cbSAndy Fiddaman 	/* select/subsel changed, query new config on next cfgread */
463b0de25cbSAndy Fiddaman 	sc->vsc_config_valid = 0;
464b0de25cbSAndy Fiddaman 
465b0de25cbSAndy Fiddaman 	return (0);
466b0de25cbSAndy Fiddaman }
467b0de25cbSAndy Fiddaman 
468b0de25cbSAndy Fiddaman static int
vtinput_eventqueue_add_event(struct vtinput_eventqueue * queue,struct input_event * e)469b0de25cbSAndy Fiddaman vtinput_eventqueue_add_event(
470b0de25cbSAndy Fiddaman     struct vtinput_eventqueue *queue, struct input_event *e)
471b0de25cbSAndy Fiddaman {
472b0de25cbSAndy Fiddaman 	/* check if queue is full */
473b0de25cbSAndy Fiddaman 	if (queue->idx >= queue->size) {
474b0de25cbSAndy Fiddaman 		/* alloc new elements for queue */
475b0de25cbSAndy Fiddaman 		const uint32_t newSize = queue->idx;
47659d65d31SAndy Fiddaman 		void *newPtr = realloc(queue->events,
477b0de25cbSAndy Fiddaman 		    queue->size * sizeof(struct vtinput_event_elem));
478b0de25cbSAndy Fiddaman 		if (newPtr == NULL) {
479b0de25cbSAndy Fiddaman 			WPRINTF(("%s: realloc memory for eventqueue failed!",
480b0de25cbSAndy Fiddaman 			    __func__));
481b0de25cbSAndy Fiddaman 			return (1);
482b0de25cbSAndy Fiddaman 		}
48359d65d31SAndy Fiddaman 		queue->events = newPtr;
484b0de25cbSAndy Fiddaman 		queue->size = newSize;
485b0de25cbSAndy Fiddaman 	}
486b0de25cbSAndy Fiddaman 
487b0de25cbSAndy Fiddaman 	/* save event */
488b0de25cbSAndy Fiddaman 	struct vtinput_event *event = &queue->events[queue->idx].event;
489b0de25cbSAndy Fiddaman 	event->type = e->type;
490b0de25cbSAndy Fiddaman 	event->code = e->code;
491b0de25cbSAndy Fiddaman 	event->value = e->value;
492b0de25cbSAndy Fiddaman 	queue->idx++;
493b0de25cbSAndy Fiddaman 
494b0de25cbSAndy Fiddaman 	return (0);
495b0de25cbSAndy Fiddaman }
496b0de25cbSAndy Fiddaman 
497b0de25cbSAndy Fiddaman static void
vtinput_eventqueue_clear(struct vtinput_eventqueue * queue)498b0de25cbSAndy Fiddaman vtinput_eventqueue_clear(struct vtinput_eventqueue *queue)
499b0de25cbSAndy Fiddaman {
500b0de25cbSAndy Fiddaman 	/* just reset index to clear queue */
501b0de25cbSAndy Fiddaman 	queue->idx = 0;
502b0de25cbSAndy Fiddaman }
503b0de25cbSAndy Fiddaman 
504b0de25cbSAndy Fiddaman static void
vtinput_eventqueue_send_events(struct vtinput_eventqueue * queue,struct vqueue_info * vq)505b0de25cbSAndy Fiddaman vtinput_eventqueue_send_events(
506b0de25cbSAndy Fiddaman     struct vtinput_eventqueue *queue, struct vqueue_info *vq)
507b0de25cbSAndy Fiddaman {
508b0de25cbSAndy Fiddaman 	/*
509b0de25cbSAndy Fiddaman 	 * First iteration through eventqueue:
510b0de25cbSAndy Fiddaman 	 *   Get descriptor chains.
511b0de25cbSAndy Fiddaman 	 */
512b0de25cbSAndy Fiddaman 	for (uint32_t i = 0; i < queue->idx; ++i) {
513b0de25cbSAndy Fiddaman 		/* get descriptor */
514b0de25cbSAndy Fiddaman 		if (!vq_has_descs(vq)) {
515b0de25cbSAndy Fiddaman 			/*
516b0de25cbSAndy Fiddaman 			 * We don't have enough descriptors for all events.
517b0de25cbSAndy Fiddaman 			 * Return chains back to guest.
518b0de25cbSAndy Fiddaman 			 */
519b0de25cbSAndy Fiddaman 			vq_retchains(vq, i);
520b0de25cbSAndy Fiddaman 			WPRINTF((
521b0de25cbSAndy Fiddaman 			    "%s: not enough available descriptors, dropping %d events",
522b0de25cbSAndy Fiddaman 			    __func__, queue->idx));
523b0de25cbSAndy Fiddaman 			goto done;
524b0de25cbSAndy Fiddaman 		}
525b0de25cbSAndy Fiddaman 
526b0de25cbSAndy Fiddaman 		/* get descriptor chain */
527b0de25cbSAndy Fiddaman 		struct iovec iov;
528b0de25cbSAndy Fiddaman 		struct vi_req req;
529b0de25cbSAndy Fiddaman 		const int n = vq_getchain(vq, &iov, 1, &req);
530b0de25cbSAndy Fiddaman 		if (n <= 0) {
531b0de25cbSAndy Fiddaman 			WPRINTF(("%s: invalid descriptor: %d", __func__, n));
532b0de25cbSAndy Fiddaman 			return;
533b0de25cbSAndy Fiddaman 		}
534b0de25cbSAndy Fiddaman 		if (n != 1) {
535b0de25cbSAndy Fiddaman 			WPRINTF(
536b0de25cbSAndy Fiddaman 			    ("%s: invalid number of descriptors in chain: %d",
537b0de25cbSAndy Fiddaman 				__func__, n));
538b0de25cbSAndy Fiddaman 			/* release invalid chain */
539b0de25cbSAndy Fiddaman 			vq_relchain(vq, req.idx, 0);
540b0de25cbSAndy Fiddaman 			return;
541b0de25cbSAndy Fiddaman 		}
542b0de25cbSAndy Fiddaman 		if (iov.iov_len < sizeof(struct vtinput_event)) {
543b0de25cbSAndy Fiddaman 			WPRINTF(("%s: invalid descriptor length: %lu", __func__,
544b0de25cbSAndy Fiddaman 			    iov.iov_len));
545b0de25cbSAndy Fiddaman 			/* release invalid chain */
546b0de25cbSAndy Fiddaman 			vq_relchain(vq, req.idx, 0);
547b0de25cbSAndy Fiddaman 			return;
548b0de25cbSAndy Fiddaman 		}
549b0de25cbSAndy Fiddaman 
550b0de25cbSAndy Fiddaman 		/* save descriptor */
551b0de25cbSAndy Fiddaman 		queue->events[i].iov = iov;
552b0de25cbSAndy Fiddaman 		queue->events[i].idx = req.idx;
553b0de25cbSAndy Fiddaman 	}
554b0de25cbSAndy Fiddaman 
555b0de25cbSAndy Fiddaman 	/*
556b0de25cbSAndy Fiddaman 	 * Second iteration through eventqueue:
557b0de25cbSAndy Fiddaman 	 *   Send events to guest by releasing chains
558b0de25cbSAndy Fiddaman 	 */
559b0de25cbSAndy Fiddaman 	for (uint32_t i = 0; i < queue->idx; ++i) {
560b0de25cbSAndy Fiddaman 		struct vtinput_event_elem event = queue->events[i];
561b0de25cbSAndy Fiddaman 		memcpy(event.iov.iov_base, &event.event,
562b0de25cbSAndy Fiddaman 		    sizeof(struct vtinput_event));
563b0de25cbSAndy Fiddaman 		vq_relchain(vq, event.idx, sizeof(struct vtinput_event));
564b0de25cbSAndy Fiddaman 	}
565b0de25cbSAndy Fiddaman done:
566b0de25cbSAndy Fiddaman 	/* clear queue and send interrupt to guest */
567b0de25cbSAndy Fiddaman 	vtinput_eventqueue_clear(queue);
568b0de25cbSAndy Fiddaman 	vq_endchains(vq, 1);
569b0de25cbSAndy Fiddaman }
570b0de25cbSAndy Fiddaman 
571b0de25cbSAndy Fiddaman static int
vtinput_read_event_from_host(int fd,struct input_event * event)572b0de25cbSAndy Fiddaman vtinput_read_event_from_host(int fd, struct input_event *event)
573b0de25cbSAndy Fiddaman {
574b0de25cbSAndy Fiddaman 	const int len = read(fd, event, sizeof(struct input_event));
575b0de25cbSAndy Fiddaman 	if (len != sizeof(struct input_event)) {
576b0de25cbSAndy Fiddaman 		if (len == -1 && errno != EAGAIN) {
577b0de25cbSAndy Fiddaman 			WPRINTF(("%s: event read failed! len = %d, errno = %d",
578b0de25cbSAndy Fiddaman 			    __func__, len, errno));
579b0de25cbSAndy Fiddaman 		}
580b0de25cbSAndy Fiddaman 
581b0de25cbSAndy Fiddaman 		/* host doesn't have more events for us */
582b0de25cbSAndy Fiddaman 		return (1);
583b0de25cbSAndy Fiddaman 	}
584b0de25cbSAndy Fiddaman 
585b0de25cbSAndy Fiddaman 	return (0);
586b0de25cbSAndy Fiddaman }
587b0de25cbSAndy Fiddaman 
588b0de25cbSAndy Fiddaman static void
vtinput_read_event(int fd __attribute ((unused)),enum ev_type t,void * arg)589b0de25cbSAndy Fiddaman vtinput_read_event(int fd __attribute((unused)),
590b0de25cbSAndy Fiddaman     enum ev_type t __attribute__((unused)), void *arg __attribute__((unused)))
591b0de25cbSAndy Fiddaman {
592b0de25cbSAndy Fiddaman 	struct pci_vtinput_softc *sc = arg;
593b0de25cbSAndy Fiddaman 
594b0de25cbSAndy Fiddaman 	/* skip if driver isn't ready */
595b0de25cbSAndy Fiddaman 	if (!(sc->vsc_vs.vs_status & VIRTIO_CONFIG_STATUS_DRIVER_OK))
596b0de25cbSAndy Fiddaman 		return;
597b0de25cbSAndy Fiddaman 
598b0de25cbSAndy Fiddaman 	/* read all events from host */
599b0de25cbSAndy Fiddaman 	struct input_event event;
600b0de25cbSAndy Fiddaman 	while (vtinput_read_event_from_host(sc->vsc_fd, &event) == 0) {
601b0de25cbSAndy Fiddaman 		/* add events to our queue */
602b0de25cbSAndy Fiddaman 		vtinput_eventqueue_add_event(&sc->vsc_eventqueue, &event);
603b0de25cbSAndy Fiddaman 
604b0de25cbSAndy Fiddaman 		/* only send events to guest on EV_SYN or SYN_REPORT */
605b0de25cbSAndy Fiddaman 		if (event.type != EV_SYN || event.type != SYN_REPORT) {
606b0de25cbSAndy Fiddaman 			continue;
607b0de25cbSAndy Fiddaman 		}
608b0de25cbSAndy Fiddaman 
609b0de25cbSAndy Fiddaman 		/* send host events to guest */
610b0de25cbSAndy Fiddaman 		vtinput_eventqueue_send_events(
611b0de25cbSAndy Fiddaman 		    &sc->vsc_eventqueue, &sc->vsc_queues[VTINPUT_EVENTQ]);
612b0de25cbSAndy Fiddaman 	}
613b0de25cbSAndy Fiddaman }
614b0de25cbSAndy Fiddaman 
615b0de25cbSAndy Fiddaman static int
pci_vtinput_legacy_config(nvlist_t * nvl,const char * opts)616b0de25cbSAndy Fiddaman pci_vtinput_legacy_config(nvlist_t *nvl, const char *opts)
617b0de25cbSAndy Fiddaman {
618b0de25cbSAndy Fiddaman 	if (opts == NULL)
619b0de25cbSAndy Fiddaman 		return (-1);
620b0de25cbSAndy Fiddaman 
621b0de25cbSAndy Fiddaman 	/*
622b0de25cbSAndy Fiddaman 	 * parse opts:
623b0de25cbSAndy Fiddaman 	 *   virtio-input,/dev/input/eventX
624b0de25cbSAndy Fiddaman 	 */
625b0de25cbSAndy Fiddaman 	char *cp = strchr(opts, ',');
626b0de25cbSAndy Fiddaman 	if (cp == NULL) {
627b0de25cbSAndy Fiddaman 		set_config_value_node(nvl, "path", opts);
628b0de25cbSAndy Fiddaman 		return (0);
629b0de25cbSAndy Fiddaman 	}
630b0de25cbSAndy Fiddaman 	char *path = strndup(opts, cp - opts);
631b0de25cbSAndy Fiddaman 	set_config_value_node(nvl, "path", path);
632b0de25cbSAndy Fiddaman 	free(path);
633b0de25cbSAndy Fiddaman 
634b0de25cbSAndy Fiddaman 	return (pci_parse_legacy_config(nvl, cp + 1));
635b0de25cbSAndy Fiddaman }
636b0de25cbSAndy Fiddaman 
637b0de25cbSAndy Fiddaman static int
pci_vtinput_init(struct pci_devinst * pi,nvlist_t * nvl)638*32640292SAndy Fiddaman pci_vtinput_init(struct pci_devinst *pi, nvlist_t *nvl)
639b0de25cbSAndy Fiddaman {
640b0de25cbSAndy Fiddaman 	struct pci_vtinput_softc *sc;
641b0de25cbSAndy Fiddaman 
642b0de25cbSAndy Fiddaman 	/*
643b0de25cbSAndy Fiddaman 	 * Keep it here.
644b0de25cbSAndy Fiddaman 	 * Else it's possible to access it uninitialized by jumping to failed.
645b0de25cbSAndy Fiddaman 	 */
646b0de25cbSAndy Fiddaman 	pthread_mutexattr_t mtx_attr = NULL;
647b0de25cbSAndy Fiddaman 
648b0de25cbSAndy Fiddaman 	sc = calloc(1, sizeof(struct pci_vtinput_softc));
649b0de25cbSAndy Fiddaman 
650b0de25cbSAndy Fiddaman 	sc->vsc_evdev = get_config_value_node(nvl, "path");
651b0de25cbSAndy Fiddaman 	if (sc->vsc_evdev == NULL) {
652b0de25cbSAndy Fiddaman 		WPRINTF(("%s: missing required path config value", __func__));
653b0de25cbSAndy Fiddaman 		goto failed;
654b0de25cbSAndy Fiddaman 	}
655b0de25cbSAndy Fiddaman 
656b0de25cbSAndy Fiddaman 	/*
657b0de25cbSAndy Fiddaman 	 * open evdev by using non blocking I/O:
658b0de25cbSAndy Fiddaman 	 *   read from /dev/input/eventX would block our thread otherwise
659b0de25cbSAndy Fiddaman 	 */
660b0de25cbSAndy Fiddaman 	sc->vsc_fd = open(sc->vsc_evdev, O_RDWR | O_NONBLOCK);
661b0de25cbSAndy Fiddaman 	if (sc->vsc_fd < 0) {
662b0de25cbSAndy Fiddaman 		WPRINTF(("%s: failed to open %s", __func__, sc->vsc_evdev));
663b0de25cbSAndy Fiddaman 		goto failed;
664b0de25cbSAndy Fiddaman 	}
665b0de25cbSAndy Fiddaman 
666b0de25cbSAndy Fiddaman 	/* check if evdev is really a evdev */
667b0de25cbSAndy Fiddaman 	int evversion;
668b0de25cbSAndy Fiddaman 	int error = ioctl(sc->vsc_fd, EVIOCGVERSION, &evversion);
669b0de25cbSAndy Fiddaman 	if (error < 0) {
670b0de25cbSAndy Fiddaman 		WPRINTF(("%s: %s is no evdev", __func__, sc->vsc_evdev));
671b0de25cbSAndy Fiddaman 		goto failed;
672b0de25cbSAndy Fiddaman 	}
673b0de25cbSAndy Fiddaman 
674b0de25cbSAndy Fiddaman 	/* gain exclusive access to evdev */
675b0de25cbSAndy Fiddaman 	error = ioctl(sc->vsc_fd, EVIOCGRAB, 1);
676b0de25cbSAndy Fiddaman 	if (error < 0) {
677b0de25cbSAndy Fiddaman 		WPRINTF(("%s: failed to grab %s", __func__, sc->vsc_evdev));
678b0de25cbSAndy Fiddaman 		goto failed;
679b0de25cbSAndy Fiddaman 	}
680b0de25cbSAndy Fiddaman 
681b0de25cbSAndy Fiddaman 	if (pthread_mutexattr_init(&mtx_attr)) {
682b0de25cbSAndy Fiddaman 		WPRINTF(("%s: init mutexattr failed", __func__));
683b0de25cbSAndy Fiddaman 		goto failed;
684b0de25cbSAndy Fiddaman 	}
685b0de25cbSAndy Fiddaman 	if (pthread_mutexattr_settype(&mtx_attr, PTHREAD_MUTEX_RECURSIVE)) {
686b0de25cbSAndy Fiddaman 		WPRINTF(("%s: settype mutexattr failed", __func__));
687b0de25cbSAndy Fiddaman 		goto failed;
688b0de25cbSAndy Fiddaman 	}
689b0de25cbSAndy Fiddaman 	if (pthread_mutex_init(&sc->vsc_mtx, &mtx_attr)) {
690b0de25cbSAndy Fiddaman 		WPRINTF(("%s: init mutex failed", __func__));
691b0de25cbSAndy Fiddaman 		goto failed;
692b0de25cbSAndy Fiddaman 	}
693b0de25cbSAndy Fiddaman 
694b0de25cbSAndy Fiddaman 	/* init softc */
695b0de25cbSAndy Fiddaman 	sc->vsc_eventqueue.idx = 0;
696b0de25cbSAndy Fiddaman 	sc->vsc_eventqueue.size = VTINPUT_MAX_PKT_LEN;
697b0de25cbSAndy Fiddaman 	sc->vsc_eventqueue.events = calloc(
698b0de25cbSAndy Fiddaman 	    sc->vsc_eventqueue.size, sizeof(struct vtinput_event_elem));
699b0de25cbSAndy Fiddaman 	sc->vsc_config_valid = 0;
700b0de25cbSAndy Fiddaman 	if (sc->vsc_eventqueue.events == NULL) {
701b0de25cbSAndy Fiddaman 		WPRINTF(("%s: failed to alloc eventqueue", __func__));
702b0de25cbSAndy Fiddaman 		goto failed;
703b0de25cbSAndy Fiddaman 	}
704b0de25cbSAndy Fiddaman 
705b0de25cbSAndy Fiddaman 	/* register event handler */
706b0de25cbSAndy Fiddaman 	sc->vsc_evp = mevent_add(sc->vsc_fd, EVF_READ, vtinput_read_event, sc);
707b0de25cbSAndy Fiddaman 	if (sc->vsc_evp == NULL) {
708b0de25cbSAndy Fiddaman 		WPRINTF(("%s: could not register mevent", __func__));
709b0de25cbSAndy Fiddaman 		goto failed;
710b0de25cbSAndy Fiddaman 	}
711b0de25cbSAndy Fiddaman 
712b0de25cbSAndy Fiddaman #ifndef WITHOUT_CAPSICUM
713b0de25cbSAndy Fiddaman 	cap_rights_t rights;
714b0de25cbSAndy Fiddaman 	cap_rights_init(&rights, CAP_EVENT, CAP_IOCTL, CAP_READ, CAP_WRITE);
715b0de25cbSAndy Fiddaman 	if (caph_rights_limit(sc->vsc_fd, &rights) == -1) {
716b0de25cbSAndy Fiddaman 		errx(EX_OSERR, "Unable to apply rights for sandbox");
717b0de25cbSAndy Fiddaman 	}
718b0de25cbSAndy Fiddaman #endif
719b0de25cbSAndy Fiddaman 
720b0de25cbSAndy Fiddaman 	/* link virtio to softc */
721b0de25cbSAndy Fiddaman 	vi_softc_linkup(
722b0de25cbSAndy Fiddaman 	    &sc->vsc_vs, &vtinput_vi_consts, sc, pi, sc->vsc_queues);
723b0de25cbSAndy Fiddaman 	sc->vsc_vs.vs_mtx = &sc->vsc_mtx;
724b0de25cbSAndy Fiddaman 
725b0de25cbSAndy Fiddaman 	/* init virtio queues */
726b0de25cbSAndy Fiddaman 	sc->vsc_queues[VTINPUT_EVENTQ].vq_qsize = VTINPUT_RINGSZ;
727b0de25cbSAndy Fiddaman 	sc->vsc_queues[VTINPUT_EVENTQ].vq_notify = pci_vtinput_notify_eventq;
728b0de25cbSAndy Fiddaman 	sc->vsc_queues[VTINPUT_STATUSQ].vq_qsize = VTINPUT_RINGSZ;
729b0de25cbSAndy Fiddaman 	sc->vsc_queues[VTINPUT_STATUSQ].vq_notify = pci_vtinput_notify_statusq;
730b0de25cbSAndy Fiddaman 
731b0de25cbSAndy Fiddaman 	/* initialize config space */
732b0de25cbSAndy Fiddaman 	pci_set_cfgdata16(pi, PCIR_DEVICE, VIRTIO_DEV_INPUT);
733b0de25cbSAndy Fiddaman 	pci_set_cfgdata16(pi, PCIR_VENDOR, VIRTIO_VENDOR);
734b0de25cbSAndy Fiddaman 	pci_set_cfgdata8(pi, PCIR_CLASS, PCIC_INPUTDEV);
735b0de25cbSAndy Fiddaman 	pci_set_cfgdata8(pi, PCIR_SUBCLASS, PCIS_INPUTDEV_OTHER);
736b0de25cbSAndy Fiddaman 	pci_set_cfgdata8(pi, PCIR_REVID, VIRTIO_REV_INPUT);
737b0de25cbSAndy Fiddaman 	pci_set_cfgdata16(pi, PCIR_SUBDEV_0, VIRTIO_SUBDEV_INPUT);
738b0de25cbSAndy Fiddaman 	pci_set_cfgdata16(pi, PCIR_SUBVEND_0, VIRTIO_SUBVEN_INPUT);
739b0de25cbSAndy Fiddaman 
740b0de25cbSAndy Fiddaman 	/* add MSI-X table BAR */
741b0de25cbSAndy Fiddaman 	if (vi_intr_init(&sc->vsc_vs, 1, fbsdrun_virtio_msix()))
742b0de25cbSAndy Fiddaman 		goto failed;
743b0de25cbSAndy Fiddaman 	/* add virtio register */
744b0de25cbSAndy Fiddaman 	vi_set_io_bar(&sc->vsc_vs, 0);
745b0de25cbSAndy Fiddaman 
746b0de25cbSAndy Fiddaman 	return (0);
747b0de25cbSAndy Fiddaman 
748b0de25cbSAndy Fiddaman failed:
749b0de25cbSAndy Fiddaman 	if (sc == NULL) {
750b0de25cbSAndy Fiddaman 		return (-1);
751b0de25cbSAndy Fiddaman 	}
752b0de25cbSAndy Fiddaman 
753b0de25cbSAndy Fiddaman 	if (sc->vsc_evp)
754b0de25cbSAndy Fiddaman 		mevent_delete(sc->vsc_evp);
755b0de25cbSAndy Fiddaman 	if (sc->vsc_eventqueue.events)
756b0de25cbSAndy Fiddaman 		free(sc->vsc_eventqueue.events);
757b0de25cbSAndy Fiddaman 	if (sc->vsc_mtx)
758b0de25cbSAndy Fiddaman 		pthread_mutex_destroy(&sc->vsc_mtx);
759b0de25cbSAndy Fiddaman 	if (mtx_attr)
760b0de25cbSAndy Fiddaman 		pthread_mutexattr_destroy(&mtx_attr);
761b0de25cbSAndy Fiddaman 	if (sc->vsc_fd)
762b0de25cbSAndy Fiddaman 		close(sc->vsc_fd);
763b0de25cbSAndy Fiddaman 
764b0de25cbSAndy Fiddaman 	free(sc);
765b0de25cbSAndy Fiddaman 
766b0de25cbSAndy Fiddaman 	return (-1);
767b0de25cbSAndy Fiddaman }
768b0de25cbSAndy Fiddaman 
7694f3f3e9aSAndy Fiddaman static const struct pci_devemu pci_de_vinput = {
770b0de25cbSAndy Fiddaman 	.pe_emu = "virtio-input",
771b0de25cbSAndy Fiddaman 	.pe_init = pci_vtinput_init,
772b0de25cbSAndy Fiddaman 	.pe_legacy_config = pci_vtinput_legacy_config,
773b0de25cbSAndy Fiddaman 	.pe_barwrite = vi_pci_write,
774b0de25cbSAndy Fiddaman 	.pe_barread = vi_pci_read,
775b0de25cbSAndy Fiddaman };
776b0de25cbSAndy Fiddaman PCI_EMUL_SET(pci_de_vinput);
777