1054accacSCorvin Köhne /*-
2*4d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause
3054accacSCorvin Köhne *
4054accacSCorvin Köhne * Copyright (c) 2021 Beckhoff Automation GmbH & Co. KG
5054accacSCorvin Köhne * All rights reserved.
6054accacSCorvin Köhne *
7054accacSCorvin Köhne * Redistribution and use in source and binary forms, with or without
8054accacSCorvin Köhne * modification, are permitted provided that the following conditions
9054accacSCorvin Köhne * are met:
10054accacSCorvin Köhne * 1. Redistributions of source code must retain the above copyright
11054accacSCorvin Köhne * notice, this list of conditions and the following disclaimer
12054accacSCorvin Köhne * in this position and unchanged.
13054accacSCorvin Köhne * 2. Redistributions in binary form must reproduce the above copyright
14054accacSCorvin Köhne * notice, this list of conditions and the following disclaimer in the
15054accacSCorvin Köhne * documentation and/or other materials provided with the distribution.
16054accacSCorvin Köhne *
17054accacSCorvin Köhne * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18054accacSCorvin Köhne * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19054accacSCorvin Köhne * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20054accacSCorvin Köhne * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21054accacSCorvin Köhne * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22054accacSCorvin Köhne * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23054accacSCorvin Köhne * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24054accacSCorvin Köhne * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25054accacSCorvin Köhne * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26054accacSCorvin Köhne * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27054accacSCorvin Köhne * SUCH DAMAGE.
28054accacSCorvin Köhne */
29054accacSCorvin Köhne
30054accacSCorvin Köhne /*
31054accacSCorvin Köhne * virtio input device emulation.
32054accacSCorvin Köhne */
33054accacSCorvin Köhne
34054accacSCorvin Köhne #include <sys/param.h>
35054accacSCorvin Köhne #ifndef WITHOUT_CAPSICUM
36054accacSCorvin Köhne #include <sys/capsicum.h>
37054accacSCorvin Köhne
38054accacSCorvin Köhne #include <capsicum_helpers.h>
39054accacSCorvin Köhne #endif
40054accacSCorvin Köhne #include <sys/ioctl.h>
41054accacSCorvin Köhne #include <sys/linker_set.h>
42054accacSCorvin Köhne #include <sys/uio.h>
43054accacSCorvin Köhne
44054accacSCorvin Köhne #include <dev/evdev/input.h>
45054accacSCorvin Köhne
46054accacSCorvin Köhne #include <assert.h>
47054accacSCorvin Köhne #include <err.h>
48054accacSCorvin Köhne #include <errno.h>
49054accacSCorvin Köhne #include <fcntl.h>
50054accacSCorvin Köhne #include <pthread.h>
51054accacSCorvin Köhne #include <stddef.h>
52054accacSCorvin Köhne #include <stdio.h>
53054accacSCorvin Köhne #include <stdlib.h>
54054accacSCorvin Köhne #include <string.h>
55054accacSCorvin Köhne #include <sysexits.h>
56054accacSCorvin Köhne #include <unistd.h>
57054accacSCorvin Köhne
58054accacSCorvin Köhne #include "bhyverun.h"
59054accacSCorvin Köhne #include "config.h"
60054accacSCorvin Köhne #include "debug.h"
61054accacSCorvin Köhne #include "mevent.h"
62054accacSCorvin Köhne #include "pci_emul.h"
63054accacSCorvin Köhne #include "virtio.h"
64054accacSCorvin Köhne
65054accacSCorvin Köhne #define VTINPUT_RINGSZ 64
66054accacSCorvin Köhne
67054accacSCorvin Köhne #define VTINPUT_MAX_PKT_LEN 10
68054accacSCorvin Köhne
69054accacSCorvin Köhne /*
70054accacSCorvin Köhne * Queue definitions.
71054accacSCorvin Köhne */
72054accacSCorvin Köhne #define VTINPUT_EVENTQ 0
73054accacSCorvin Köhne #define VTINPUT_STATUSQ 1
74054accacSCorvin Köhne
75054accacSCorvin Köhne #define VTINPUT_MAXQ 2
76054accacSCorvin Köhne
77054accacSCorvin Köhne static int pci_vtinput_debug;
78054accacSCorvin Köhne #define DPRINTF(params) \
79054accacSCorvin Köhne if (pci_vtinput_debug) \
80054accacSCorvin Köhne PRINTLN params
81054accacSCorvin Köhne #define WPRINTF(params) PRINTLN params
82054accacSCorvin Köhne
83054accacSCorvin Köhne enum vtinput_config_select {
84054accacSCorvin Köhne VTINPUT_CFG_UNSET = 0x00,
85054accacSCorvin Köhne VTINPUT_CFG_ID_NAME = 0x01,
86054accacSCorvin Köhne VTINPUT_CFG_ID_SERIAL = 0x02,
87054accacSCorvin Köhne VTINPUT_CFG_ID_DEVIDS = 0x03,
88054accacSCorvin Köhne VTINPUT_CFG_PROP_BITS = 0x10,
89054accacSCorvin Köhne VTINPUT_CFG_EV_BITS = 0x11,
90054accacSCorvin Köhne VTINPUT_CFG_ABS_INFO = 0x12
91054accacSCorvin Köhne };
92054accacSCorvin Köhne
93054accacSCorvin Köhne struct vtinput_absinfo {
94054accacSCorvin Köhne uint32_t min;
95054accacSCorvin Köhne uint32_t max;
96054accacSCorvin Köhne uint32_t fuzz;
97054accacSCorvin Köhne uint32_t flat;
98054accacSCorvin Köhne uint32_t res;
99054accacSCorvin Köhne } __packed;
100054accacSCorvin Köhne
101054accacSCorvin Köhne struct vtinput_devids {
102054accacSCorvin Köhne uint16_t bustype;
103054accacSCorvin Köhne uint16_t vendor;
104054accacSCorvin Köhne uint16_t product;
105054accacSCorvin Köhne uint16_t version;
106054accacSCorvin Köhne } __packed;
107054accacSCorvin Köhne
108054accacSCorvin Köhne struct vtinput_config {
109054accacSCorvin Köhne uint8_t select;
110054accacSCorvin Köhne uint8_t subsel;
111054accacSCorvin Köhne uint8_t size;
112054accacSCorvin Köhne uint8_t reserved[5];
113054accacSCorvin Köhne union {
114054accacSCorvin Köhne char string[128];
115054accacSCorvin Köhne uint8_t bitmap[128];
116054accacSCorvin Köhne struct vtinput_absinfo abs;
117054accacSCorvin Köhne struct vtinput_devids ids;
118054accacSCorvin Köhne } u;
119054accacSCorvin Köhne } __packed;
120054accacSCorvin Köhne
121054accacSCorvin Köhne struct vtinput_event {
122054accacSCorvin Köhne uint16_t type;
123054accacSCorvin Köhne uint16_t code;
124054accacSCorvin Köhne uint32_t value;
125054accacSCorvin Köhne } __packed;
126054accacSCorvin Köhne
127054accacSCorvin Köhne struct vtinput_event_elem {
128054accacSCorvin Köhne struct vtinput_event event;
129054accacSCorvin Köhne struct iovec iov;
130054accacSCorvin Köhne uint16_t idx;
131054accacSCorvin Köhne };
132054accacSCorvin Köhne
133054accacSCorvin Köhne struct vtinput_eventqueue {
134054accacSCorvin Köhne struct vtinput_event_elem *events;
135054accacSCorvin Köhne uint32_t size;
136054accacSCorvin Köhne uint32_t idx;
137054accacSCorvin Köhne };
138054accacSCorvin Köhne
139054accacSCorvin Köhne /*
140054accacSCorvin Köhne * Per-device softc
141054accacSCorvin Köhne */
142054accacSCorvin Köhne struct pci_vtinput_softc {
143054accacSCorvin Köhne struct virtio_softc vsc_vs;
144054accacSCorvin Köhne struct vqueue_info vsc_queues[VTINPUT_MAXQ];
145054accacSCorvin Köhne pthread_mutex_t vsc_mtx;
146054accacSCorvin Köhne const char *vsc_evdev;
147054accacSCorvin Köhne int vsc_fd;
148054accacSCorvin Köhne struct vtinput_config vsc_config;
149054accacSCorvin Köhne int vsc_config_valid;
150054accacSCorvin Köhne struct mevent *vsc_evp;
151054accacSCorvin Köhne struct vtinput_eventqueue vsc_eventqueue;
152054accacSCorvin Köhne };
153054accacSCorvin Köhne
154054accacSCorvin Köhne static void pci_vtinput_reset(void *);
155054accacSCorvin Köhne static int pci_vtinput_cfgread(void *, int, int, uint32_t *);
156054accacSCorvin Köhne static int pci_vtinput_cfgwrite(void *, int, int, uint32_t);
157054accacSCorvin Köhne
158054accacSCorvin Köhne static struct virtio_consts vtinput_vi_consts = {
1596cb26162SMark Johnston .vc_name = "vtinput",
1606cb26162SMark Johnston .vc_nvq = VTINPUT_MAXQ,
1616cb26162SMark Johnston .vc_cfgsize = sizeof(struct vtinput_config),
1626cb26162SMark Johnston .vc_reset = pci_vtinput_reset,
1636cb26162SMark Johnston .vc_cfgread = pci_vtinput_cfgread,
1646cb26162SMark Johnston .vc_cfgwrite = pci_vtinput_cfgwrite,
1656cb26162SMark Johnston .vc_hv_caps = 0,
166054accacSCorvin Köhne };
167054accacSCorvin Köhne
168054accacSCorvin Köhne static void
pci_vtinput_reset(void * vsc)169054accacSCorvin Köhne pci_vtinput_reset(void *vsc)
170054accacSCorvin Köhne {
171054accacSCorvin Köhne struct pci_vtinput_softc *sc = vsc;
172054accacSCorvin Köhne
173054accacSCorvin Köhne DPRINTF(("%s: device reset requested", __func__));
174054accacSCorvin Köhne vi_reset_dev(&sc->vsc_vs);
175054accacSCorvin Köhne }
176054accacSCorvin Köhne
177054accacSCorvin Köhne static void
pci_vtinput_notify_eventq(void * vsc __unused,struct vqueue_info * vq __unused)17898d920d9SMark Johnston pci_vtinput_notify_eventq(void *vsc __unused, struct vqueue_info *vq __unused)
179054accacSCorvin Köhne {
180054accacSCorvin Köhne DPRINTF(("%s", __func__));
181054accacSCorvin Köhne }
182054accacSCorvin Köhne
183054accacSCorvin Köhne static void
pci_vtinput_notify_statusq(void * vsc,struct vqueue_info * vq)184054accacSCorvin Köhne pci_vtinput_notify_statusq(void *vsc, struct vqueue_info *vq)
185054accacSCorvin Köhne {
186054accacSCorvin Köhne struct pci_vtinput_softc *sc = vsc;
187054accacSCorvin Köhne
188054accacSCorvin Köhne while (vq_has_descs(vq)) {
189054accacSCorvin Köhne /* get descriptor chain */
190054accacSCorvin Köhne struct iovec iov;
191054accacSCorvin Köhne struct vi_req req;
192054accacSCorvin Köhne const int n = vq_getchain(vq, &iov, 1, &req);
193054accacSCorvin Köhne if (n <= 0) {
194054accacSCorvin Köhne WPRINTF(("%s: invalid descriptor: %d", __func__, n));
195054accacSCorvin Köhne return;
196054accacSCorvin Köhne }
197054accacSCorvin Köhne
198054accacSCorvin Köhne /* get event */
199054accacSCorvin Köhne struct vtinput_event event;
200054accacSCorvin Köhne memcpy(&event, iov.iov_base, sizeof(event));
201054accacSCorvin Köhne
202054accacSCorvin Köhne /*
203054accacSCorvin Köhne * on multi touch devices:
204054accacSCorvin Köhne * - host send EV_MSC to guest
205054accacSCorvin Köhne * - guest sends EV_MSC back to host
206054accacSCorvin Köhne * - host writes EV_MSC to evdev
207054accacSCorvin Köhne * - evdev saves EV_MSC in it's event buffer
208054accacSCorvin Köhne * - host receives an extra EV_MSC by reading the evdev event
209054accacSCorvin Köhne * buffer
210054accacSCorvin Köhne * - frames become larger and larger
211054accacSCorvin Köhne * avoid endless loops by ignoring EV_MSC
212054accacSCorvin Köhne */
213054accacSCorvin Köhne if (event.type == EV_MSC) {
214054accacSCorvin Köhne vq_relchain(vq, req.idx, sizeof(event));
215054accacSCorvin Köhne continue;
216054accacSCorvin Köhne }
217054accacSCorvin Köhne
218054accacSCorvin Köhne /* send event to evdev */
219054accacSCorvin Köhne struct input_event host_event;
220054accacSCorvin Köhne host_event.type = event.type;
221054accacSCorvin Köhne host_event.code = event.code;
222054accacSCorvin Köhne host_event.value = event.value;
223054accacSCorvin Köhne if (gettimeofday(&host_event.time, NULL) != 0) {
224054accacSCorvin Köhne WPRINTF(("%s: failed gettimeofday", __func__));
225054accacSCorvin Köhne }
226054accacSCorvin Köhne if (write(sc->vsc_fd, &host_event, sizeof(host_event)) == -1) {
227054accacSCorvin Köhne WPRINTF(("%s: failed to write host_event", __func__));
228054accacSCorvin Köhne }
229054accacSCorvin Köhne
230054accacSCorvin Köhne vq_relchain(vq, req.idx, sizeof(event));
231054accacSCorvin Köhne }
232054accacSCorvin Köhne vq_endchains(vq, 1);
233054accacSCorvin Köhne }
234054accacSCorvin Köhne
235054accacSCorvin Köhne static int
pci_vtinput_get_bitmap(struct pci_vtinput_softc * sc,int cmd,int count)236054accacSCorvin Köhne pci_vtinput_get_bitmap(struct pci_vtinput_softc *sc, int cmd, int count)
237054accacSCorvin Köhne {
238054accacSCorvin Köhne if (count <= 0 || !sc) {
239054accacSCorvin Köhne return (-1);
240054accacSCorvin Köhne }
241054accacSCorvin Köhne
242054accacSCorvin Köhne /* query bitmap */
243054accacSCorvin Köhne memset(sc->vsc_config.u.bitmap, 0, sizeof(sc->vsc_config.u.bitmap));
244054accacSCorvin Köhne if (ioctl(sc->vsc_fd, cmd, sc->vsc_config.u.bitmap) < 0) {
245054accacSCorvin Köhne return (-1);
246054accacSCorvin Köhne }
247054accacSCorvin Köhne
248054accacSCorvin Köhne /* get number of set bytes in bitmap */
249054accacSCorvin Köhne for (int i = count - 1; i >= 0; i--) {
250054accacSCorvin Köhne if (sc->vsc_config.u.bitmap[i]) {
251054accacSCorvin Köhne return i + 1;
252054accacSCorvin Köhne }
253054accacSCorvin Köhne }
254054accacSCorvin Köhne
255054accacSCorvin Köhne return (-1);
256054accacSCorvin Köhne }
257054accacSCorvin Köhne
258054accacSCorvin Köhne static int
pci_vtinput_read_config_id_name(struct pci_vtinput_softc * sc)259054accacSCorvin Köhne pci_vtinput_read_config_id_name(struct pci_vtinput_softc *sc)
260054accacSCorvin Köhne {
261054accacSCorvin Köhne char name[128];
262054accacSCorvin Köhne if (ioctl(sc->vsc_fd, EVIOCGNAME(sizeof(name) - 1), name) < 0) {
263054accacSCorvin Köhne return (1);
264054accacSCorvin Köhne }
265054accacSCorvin Köhne
266054accacSCorvin Köhne memcpy(sc->vsc_config.u.string, name, sizeof(name));
267054accacSCorvin Köhne sc->vsc_config.size = strnlen(name, sizeof(name));
268054accacSCorvin Köhne
269054accacSCorvin Köhne return (0);
270054accacSCorvin Köhne }
271054accacSCorvin Köhne
272054accacSCorvin Köhne static int
pci_vtinput_read_config_id_serial(struct pci_vtinput_softc * sc)273054accacSCorvin Köhne pci_vtinput_read_config_id_serial(struct pci_vtinput_softc *sc)
274054accacSCorvin Köhne {
275054accacSCorvin Köhne /* serial isn't supported */
276054accacSCorvin Köhne sc->vsc_config.size = 0;
277054accacSCorvin Köhne
278054accacSCorvin Köhne return (0);
279054accacSCorvin Köhne }
280054accacSCorvin Köhne
281054accacSCorvin Köhne static int
pci_vtinput_read_config_id_devids(struct pci_vtinput_softc * sc)282054accacSCorvin Köhne pci_vtinput_read_config_id_devids(struct pci_vtinput_softc *sc)
283054accacSCorvin Köhne {
284054accacSCorvin Köhne struct input_id devids;
285054accacSCorvin Köhne if (ioctl(sc->vsc_fd, EVIOCGID, &devids)) {
286054accacSCorvin Köhne return (1);
287054accacSCorvin Köhne }
288054accacSCorvin Köhne
289054accacSCorvin Köhne sc->vsc_config.u.ids.bustype = devids.bustype;
290054accacSCorvin Köhne sc->vsc_config.u.ids.vendor = devids.vendor;
291054accacSCorvin Köhne sc->vsc_config.u.ids.product = devids.product;
292054accacSCorvin Köhne sc->vsc_config.u.ids.version = devids.version;
293054accacSCorvin Köhne sc->vsc_config.size = sizeof(struct vtinput_devids);
294054accacSCorvin Köhne
295054accacSCorvin Köhne return (0);
296054accacSCorvin Köhne }
297054accacSCorvin Köhne
298054accacSCorvin Köhne static int
pci_vtinput_read_config_prop_bits(struct pci_vtinput_softc * sc)299054accacSCorvin Köhne pci_vtinput_read_config_prop_bits(struct pci_vtinput_softc *sc)
300054accacSCorvin Köhne {
301054accacSCorvin Köhne /*
302054accacSCorvin Köhne * Evdev bitmap countains 1 bit per count. Additionally evdev bitmaps
303054accacSCorvin Köhne * are arrays of longs instead of chars. Calculate how many longs are
304054accacSCorvin Köhne * required for evdev bitmap. Multiply that with sizeof(long) to get the
305054accacSCorvin Köhne * number of elements.
306054accacSCorvin Köhne */
307054accacSCorvin Köhne const int count = howmany(INPUT_PROP_CNT, sizeof(long) * 8) *
308054accacSCorvin Köhne sizeof(long);
309054accacSCorvin Köhne const unsigned int cmd = EVIOCGPROP(count);
310054accacSCorvin Köhne const int size = pci_vtinput_get_bitmap(sc, cmd, count);
311054accacSCorvin Köhne if (size <= 0) {
312054accacSCorvin Köhne return (1);
313054accacSCorvin Köhne }
314054accacSCorvin Köhne
315054accacSCorvin Köhne sc->vsc_config.size = size;
316054accacSCorvin Köhne
317054accacSCorvin Köhne return (0);
318054accacSCorvin Köhne }
319054accacSCorvin Köhne
320054accacSCorvin Köhne static int
pci_vtinput_read_config_ev_bits(struct pci_vtinput_softc * sc,uint8_t type)321054accacSCorvin Köhne pci_vtinput_read_config_ev_bits(struct pci_vtinput_softc *sc, uint8_t type)
322054accacSCorvin Köhne {
323054accacSCorvin Köhne int count;
324054accacSCorvin Köhne
325054accacSCorvin Köhne switch (type) {
326054accacSCorvin Köhne case EV_KEY:
327054accacSCorvin Köhne count = KEY_CNT;
328054accacSCorvin Köhne break;
329054accacSCorvin Köhne case EV_REL:
330054accacSCorvin Köhne count = REL_CNT;
331054accacSCorvin Köhne break;
332054accacSCorvin Köhne case EV_ABS:
333054accacSCorvin Köhne count = ABS_CNT;
334054accacSCorvin Köhne break;
335054accacSCorvin Köhne case EV_MSC:
336054accacSCorvin Köhne count = MSC_CNT;
337054accacSCorvin Köhne break;
338054accacSCorvin Köhne case EV_SW:
339054accacSCorvin Köhne count = SW_CNT;
340054accacSCorvin Köhne break;
341054accacSCorvin Köhne case EV_LED:
342054accacSCorvin Köhne count = LED_CNT;
343054accacSCorvin Köhne break;
344054accacSCorvin Köhne default:
345054accacSCorvin Köhne return (1);
346054accacSCorvin Köhne }
347054accacSCorvin Köhne
348054accacSCorvin Köhne /*
349054accacSCorvin Köhne * Evdev bitmap countains 1 bit per count. Additionally evdev bitmaps
350054accacSCorvin Köhne * are arrays of longs instead of chars. Calculate how many longs are
351054accacSCorvin Köhne * required for evdev bitmap. Multiply that with sizeof(long) to get the
352054accacSCorvin Köhne * number of elements.
353054accacSCorvin Köhne */
354054accacSCorvin Köhne count = howmany(count, sizeof(long) * 8) * sizeof(long);
355054accacSCorvin Köhne const unsigned int cmd = EVIOCGBIT(sc->vsc_config.subsel, count);
356054accacSCorvin Köhne const int size = pci_vtinput_get_bitmap(sc, cmd, count);
357054accacSCorvin Köhne if (size <= 0) {
358054accacSCorvin Köhne return (1);
359054accacSCorvin Köhne }
360054accacSCorvin Köhne
361054accacSCorvin Köhne sc->vsc_config.size = size;
362054accacSCorvin Köhne
363054accacSCorvin Köhne return (0);
364054accacSCorvin Köhne }
365054accacSCorvin Köhne
366054accacSCorvin Köhne static int
pci_vtinput_read_config_abs_info(struct pci_vtinput_softc * sc)367054accacSCorvin Köhne pci_vtinput_read_config_abs_info(struct pci_vtinput_softc *sc)
368054accacSCorvin Köhne {
369054accacSCorvin Köhne /* check if evdev has EV_ABS */
370054accacSCorvin Köhne if (!pci_vtinput_read_config_ev_bits(sc, EV_ABS)) {
371054accacSCorvin Köhne return (1);
372054accacSCorvin Köhne }
373054accacSCorvin Köhne
374054accacSCorvin Köhne /* get abs information */
375054accacSCorvin Köhne struct input_absinfo abs;
376054accacSCorvin Köhne if (ioctl(sc->vsc_fd, EVIOCGABS(sc->vsc_config.subsel), &abs) < 0) {
377054accacSCorvin Köhne return (1);
378054accacSCorvin Köhne }
379054accacSCorvin Köhne
380054accacSCorvin Köhne /* save abs information */
381054accacSCorvin Köhne sc->vsc_config.u.abs.min = abs.minimum;
382054accacSCorvin Köhne sc->vsc_config.u.abs.max = abs.maximum;
383054accacSCorvin Köhne sc->vsc_config.u.abs.fuzz = abs.fuzz;
384054accacSCorvin Köhne sc->vsc_config.u.abs.flat = abs.flat;
385054accacSCorvin Köhne sc->vsc_config.u.abs.res = abs.resolution;
386054accacSCorvin Köhne sc->vsc_config.size = sizeof(struct vtinput_absinfo);
387054accacSCorvin Köhne
388054accacSCorvin Köhne return (0);
389054accacSCorvin Köhne }
390054accacSCorvin Köhne
391054accacSCorvin Köhne static int
pci_vtinput_read_config(struct pci_vtinput_softc * sc)392054accacSCorvin Köhne pci_vtinput_read_config(struct pci_vtinput_softc *sc)
393054accacSCorvin Köhne {
394054accacSCorvin Köhne switch (sc->vsc_config.select) {
395054accacSCorvin Köhne case VTINPUT_CFG_UNSET:
396054accacSCorvin Köhne return (0);
397054accacSCorvin Köhne case VTINPUT_CFG_ID_NAME:
398054accacSCorvin Köhne return pci_vtinput_read_config_id_name(sc);
399054accacSCorvin Köhne case VTINPUT_CFG_ID_SERIAL:
400054accacSCorvin Köhne return pci_vtinput_read_config_id_serial(sc);
401054accacSCorvin Köhne case VTINPUT_CFG_ID_DEVIDS:
402054accacSCorvin Köhne return pci_vtinput_read_config_id_devids(sc);
403054accacSCorvin Köhne case VTINPUT_CFG_PROP_BITS:
404054accacSCorvin Köhne return pci_vtinput_read_config_prop_bits(sc);
405054accacSCorvin Köhne case VTINPUT_CFG_EV_BITS:
406054accacSCorvin Köhne return pci_vtinput_read_config_ev_bits(
407054accacSCorvin Köhne sc, sc->vsc_config.subsel);
408054accacSCorvin Köhne case VTINPUT_CFG_ABS_INFO:
409054accacSCorvin Köhne return pci_vtinput_read_config_abs_info(sc);
410054accacSCorvin Köhne default:
411054accacSCorvin Köhne return (1);
412054accacSCorvin Köhne }
413054accacSCorvin Köhne }
414054accacSCorvin Köhne
415054accacSCorvin Köhne static int
pci_vtinput_cfgread(void * vsc,int offset,int size,uint32_t * retval)416054accacSCorvin Köhne pci_vtinput_cfgread(void *vsc, int offset, int size, uint32_t *retval)
417054accacSCorvin Köhne {
418054accacSCorvin Köhne struct pci_vtinput_softc *sc = vsc;
419054accacSCorvin Köhne
420054accacSCorvin Köhne /* check for valid offset and size */
421ed721684SMark Johnston if (offset + size > (int)sizeof(struct vtinput_config)) {
422054accacSCorvin Köhne WPRINTF(("%s: read to invalid offset/size %d/%d", __func__,
423054accacSCorvin Köhne offset, size));
424054accacSCorvin Köhne memset(retval, 0, size);
425054accacSCorvin Köhne return (0);
426054accacSCorvin Köhne }
427054accacSCorvin Köhne
428054accacSCorvin Köhne /* read new config values, if select and subsel changed. */
429054accacSCorvin Köhne if (!sc->vsc_config_valid) {
430054accacSCorvin Köhne if (pci_vtinput_read_config(sc) != 0) {
431054accacSCorvin Köhne DPRINTF(("%s: could not read config %d/%d", __func__,
432054accacSCorvin Köhne sc->vsc_config.select, sc->vsc_config.subsel));
433054accacSCorvin Köhne memset(retval, 0, size);
434054accacSCorvin Köhne return (0);
435054accacSCorvin Köhne }
436054accacSCorvin Köhne sc->vsc_config_valid = 1;
437054accacSCorvin Köhne }
438054accacSCorvin Köhne
439054accacSCorvin Köhne uint8_t *ptr = (uint8_t *)&sc->vsc_config;
440054accacSCorvin Köhne memcpy(retval, ptr + offset, size);
441054accacSCorvin Köhne
442054accacSCorvin Köhne return (0);
443054accacSCorvin Köhne }
444054accacSCorvin Köhne
445054accacSCorvin Köhne static int
pci_vtinput_cfgwrite(void * vsc,int offset,int size,uint32_t value)446054accacSCorvin Köhne pci_vtinput_cfgwrite(void *vsc, int offset, int size, uint32_t value)
447054accacSCorvin Köhne {
448054accacSCorvin Köhne struct pci_vtinput_softc *sc = vsc;
449054accacSCorvin Köhne
450054accacSCorvin Köhne /* guest can only write to select and subsel fields */
451054accacSCorvin Köhne if (offset + size > 2) {
452054accacSCorvin Köhne WPRINTF(("%s: write to readonly reg %d", __func__, offset));
453054accacSCorvin Köhne return (1);
454054accacSCorvin Köhne }
455054accacSCorvin Köhne
456054accacSCorvin Köhne /* copy value into config */
457054accacSCorvin Köhne uint8_t *ptr = (uint8_t *)&sc->vsc_config;
458054accacSCorvin Köhne memcpy(ptr + offset, &value, size);
459054accacSCorvin Köhne
460054accacSCorvin Köhne /* select/subsel changed, query new config on next cfgread */
461054accacSCorvin Köhne sc->vsc_config_valid = 0;
462054accacSCorvin Köhne
463054accacSCorvin Köhne return (0);
464054accacSCorvin Köhne }
465054accacSCorvin Köhne
466054accacSCorvin Köhne static int
vtinput_eventqueue_add_event(struct vtinput_eventqueue * queue,struct input_event * e)467054accacSCorvin Köhne vtinput_eventqueue_add_event(
468054accacSCorvin Köhne struct vtinput_eventqueue *queue, struct input_event *e)
469054accacSCorvin Köhne {
470054accacSCorvin Köhne /* check if queue is full */
471054accacSCorvin Köhne if (queue->idx >= queue->size) {
472054accacSCorvin Köhne /* alloc new elements for queue */
473054accacSCorvin Köhne const uint32_t newSize = queue->idx;
474eefd863cSMark Johnston void *newPtr = realloc(queue->events,
475054accacSCorvin Köhne queue->size * sizeof(struct vtinput_event_elem));
476054accacSCorvin Köhne if (newPtr == NULL) {
477054accacSCorvin Köhne WPRINTF(("%s: realloc memory for eventqueue failed!",
478054accacSCorvin Köhne __func__));
479054accacSCorvin Köhne return (1);
480054accacSCorvin Köhne }
481eefd863cSMark Johnston queue->events = newPtr;
482054accacSCorvin Köhne queue->size = newSize;
483054accacSCorvin Köhne }
484054accacSCorvin Köhne
485054accacSCorvin Köhne /* save event */
486054accacSCorvin Köhne struct vtinput_event *event = &queue->events[queue->idx].event;
487054accacSCorvin Köhne event->type = e->type;
488054accacSCorvin Köhne event->code = e->code;
489054accacSCorvin Köhne event->value = e->value;
490054accacSCorvin Köhne queue->idx++;
491054accacSCorvin Köhne
492054accacSCorvin Köhne return (0);
493054accacSCorvin Köhne }
494054accacSCorvin Köhne
495054accacSCorvin Köhne static void
vtinput_eventqueue_clear(struct vtinput_eventqueue * queue)496054accacSCorvin Köhne vtinput_eventqueue_clear(struct vtinput_eventqueue *queue)
497054accacSCorvin Köhne {
498054accacSCorvin Köhne /* just reset index to clear queue */
499054accacSCorvin Köhne queue->idx = 0;
500054accacSCorvin Köhne }
501054accacSCorvin Köhne
502054accacSCorvin Köhne static void
vtinput_eventqueue_send_events(struct vtinput_eventqueue * queue,struct vqueue_info * vq)503054accacSCorvin Köhne vtinput_eventqueue_send_events(
504054accacSCorvin Köhne struct vtinput_eventqueue *queue, struct vqueue_info *vq)
505054accacSCorvin Köhne {
506054accacSCorvin Köhne /*
507054accacSCorvin Köhne * First iteration through eventqueue:
508054accacSCorvin Köhne * Get descriptor chains.
509054accacSCorvin Köhne */
510054accacSCorvin Köhne for (uint32_t i = 0; i < queue->idx; ++i) {
511054accacSCorvin Köhne /* get descriptor */
512054accacSCorvin Köhne if (!vq_has_descs(vq)) {
513054accacSCorvin Köhne /*
514054accacSCorvin Köhne * We don't have enough descriptors for all events.
515054accacSCorvin Köhne * Return chains back to guest.
516054accacSCorvin Köhne */
517054accacSCorvin Köhne vq_retchains(vq, i);
518054accacSCorvin Köhne WPRINTF((
519054accacSCorvin Köhne "%s: not enough available descriptors, dropping %d events",
520054accacSCorvin Köhne __func__, queue->idx));
521054accacSCorvin Köhne goto done;
522054accacSCorvin Köhne }
523054accacSCorvin Köhne
524054accacSCorvin Köhne /* get descriptor chain */
525054accacSCorvin Köhne struct iovec iov;
526054accacSCorvin Köhne struct vi_req req;
527054accacSCorvin Köhne const int n = vq_getchain(vq, &iov, 1, &req);
528054accacSCorvin Köhne if (n <= 0) {
529054accacSCorvin Köhne WPRINTF(("%s: invalid descriptor: %d", __func__, n));
530054accacSCorvin Köhne return;
531054accacSCorvin Köhne }
532054accacSCorvin Köhne if (n != 1) {
533054accacSCorvin Köhne WPRINTF(
534054accacSCorvin Köhne ("%s: invalid number of descriptors in chain: %d",
535054accacSCorvin Köhne __func__, n));
536054accacSCorvin Köhne /* release invalid chain */
537054accacSCorvin Köhne vq_relchain(vq, req.idx, 0);
538054accacSCorvin Köhne return;
539054accacSCorvin Köhne }
540054accacSCorvin Köhne if (iov.iov_len < sizeof(struct vtinput_event)) {
541054accacSCorvin Köhne WPRINTF(("%s: invalid descriptor length: %lu", __func__,
542054accacSCorvin Köhne iov.iov_len));
543054accacSCorvin Köhne /* release invalid chain */
544054accacSCorvin Köhne vq_relchain(vq, req.idx, 0);
545054accacSCorvin Köhne return;
546054accacSCorvin Köhne }
547054accacSCorvin Köhne
548054accacSCorvin Köhne /* save descriptor */
549054accacSCorvin Köhne queue->events[i].iov = iov;
550054accacSCorvin Köhne queue->events[i].idx = req.idx;
551054accacSCorvin Köhne }
552054accacSCorvin Köhne
553054accacSCorvin Köhne /*
554054accacSCorvin Köhne * Second iteration through eventqueue:
555054accacSCorvin Köhne * Send events to guest by releasing chains
556054accacSCorvin Köhne */
557054accacSCorvin Köhne for (uint32_t i = 0; i < queue->idx; ++i) {
558054accacSCorvin Köhne struct vtinput_event_elem event = queue->events[i];
559054accacSCorvin Köhne memcpy(event.iov.iov_base, &event.event,
560054accacSCorvin Köhne sizeof(struct vtinput_event));
561054accacSCorvin Köhne vq_relchain(vq, event.idx, sizeof(struct vtinput_event));
562054accacSCorvin Köhne }
563054accacSCorvin Köhne done:
564054accacSCorvin Köhne /* clear queue and send interrupt to guest */
565054accacSCorvin Köhne vtinput_eventqueue_clear(queue);
566054accacSCorvin Köhne vq_endchains(vq, 1);
567054accacSCorvin Köhne }
568054accacSCorvin Köhne
569054accacSCorvin Köhne static int
vtinput_read_event_from_host(int fd,struct input_event * event)570054accacSCorvin Köhne vtinput_read_event_from_host(int fd, struct input_event *event)
571054accacSCorvin Köhne {
572054accacSCorvin Köhne const int len = read(fd, event, sizeof(struct input_event));
573054accacSCorvin Köhne if (len != sizeof(struct input_event)) {
574054accacSCorvin Köhne if (len == -1 && errno != EAGAIN) {
575054accacSCorvin Köhne WPRINTF(("%s: event read failed! len = %d, errno = %d",
576054accacSCorvin Köhne __func__, len, errno));
577054accacSCorvin Köhne }
578054accacSCorvin Köhne
579054accacSCorvin Köhne /* host doesn't have more events for us */
580054accacSCorvin Köhne return (1);
581054accacSCorvin Köhne }
582054accacSCorvin Köhne
583054accacSCorvin Köhne return (0);
584054accacSCorvin Köhne }
585054accacSCorvin Köhne
586054accacSCorvin Köhne static void
vtinput_read_event(int fd __attribute ((unused)),enum ev_type t,void * arg)587054accacSCorvin Köhne vtinput_read_event(int fd __attribute((unused)),
588054accacSCorvin Köhne enum ev_type t __attribute__((unused)), void *arg __attribute__((unused)))
589054accacSCorvin Köhne {
590054accacSCorvin Köhne struct pci_vtinput_softc *sc = arg;
591054accacSCorvin Köhne
592054accacSCorvin Köhne /* skip if driver isn't ready */
593054accacSCorvin Köhne if (!(sc->vsc_vs.vs_status & VIRTIO_CONFIG_STATUS_DRIVER_OK))
594054accacSCorvin Köhne return;
595054accacSCorvin Köhne
596054accacSCorvin Köhne /* read all events from host */
597054accacSCorvin Köhne struct input_event event;
598054accacSCorvin Köhne while (vtinput_read_event_from_host(sc->vsc_fd, &event) == 0) {
599054accacSCorvin Köhne /* add events to our queue */
600054accacSCorvin Köhne vtinput_eventqueue_add_event(&sc->vsc_eventqueue, &event);
601054accacSCorvin Köhne
602054accacSCorvin Köhne /* only send events to guest on EV_SYN or SYN_REPORT */
603054accacSCorvin Köhne if (event.type != EV_SYN || event.type != SYN_REPORT) {
604054accacSCorvin Köhne continue;
605054accacSCorvin Köhne }
606054accacSCorvin Köhne
607054accacSCorvin Köhne /* send host events to guest */
608054accacSCorvin Köhne vtinput_eventqueue_send_events(
609054accacSCorvin Köhne &sc->vsc_eventqueue, &sc->vsc_queues[VTINPUT_EVENTQ]);
610054accacSCorvin Köhne }
611054accacSCorvin Köhne }
612054accacSCorvin Köhne
613054accacSCorvin Köhne static int
pci_vtinput_legacy_config(nvlist_t * nvl,const char * opts)614054accacSCorvin Köhne pci_vtinput_legacy_config(nvlist_t *nvl, const char *opts)
615054accacSCorvin Köhne {
616054accacSCorvin Köhne if (opts == NULL)
617054accacSCorvin Köhne return (-1);
618054accacSCorvin Köhne
619054accacSCorvin Köhne /*
620054accacSCorvin Köhne * parse opts:
621054accacSCorvin Köhne * virtio-input,/dev/input/eventX
622054accacSCorvin Köhne */
623054accacSCorvin Köhne char *cp = strchr(opts, ',');
624054accacSCorvin Köhne if (cp == NULL) {
625054accacSCorvin Köhne set_config_value_node(nvl, "path", opts);
626054accacSCorvin Köhne return (0);
627054accacSCorvin Köhne }
628054accacSCorvin Köhne char *path = strndup(opts, cp - opts);
629054accacSCorvin Köhne set_config_value_node(nvl, "path", path);
630054accacSCorvin Köhne free(path);
631054accacSCorvin Köhne
632054accacSCorvin Köhne return (pci_parse_legacy_config(nvl, cp + 1));
633054accacSCorvin Köhne }
634054accacSCorvin Köhne
635054accacSCorvin Köhne static int
pci_vtinput_init(struct pci_devinst * pi,nvlist_t * nvl)6366a284cacSJohn Baldwin pci_vtinput_init(struct pci_devinst *pi, nvlist_t *nvl)
637054accacSCorvin Köhne {
638054accacSCorvin Köhne struct pci_vtinput_softc *sc;
639054accacSCorvin Köhne
640054accacSCorvin Köhne /*
641054accacSCorvin Köhne * Keep it here.
642054accacSCorvin Köhne * Else it's possible to access it uninitialized by jumping to failed.
643054accacSCorvin Köhne */
644054accacSCorvin Köhne pthread_mutexattr_t mtx_attr = NULL;
645054accacSCorvin Köhne
646054accacSCorvin Köhne sc = calloc(1, sizeof(struct pci_vtinput_softc));
647054accacSCorvin Köhne
648054accacSCorvin Köhne sc->vsc_evdev = get_config_value_node(nvl, "path");
649054accacSCorvin Köhne if (sc->vsc_evdev == NULL) {
650054accacSCorvin Köhne WPRINTF(("%s: missing required path config value", __func__));
651054accacSCorvin Köhne goto failed;
652054accacSCorvin Köhne }
653054accacSCorvin Köhne
654054accacSCorvin Köhne /*
655054accacSCorvin Köhne * open evdev by using non blocking I/O:
656054accacSCorvin Köhne * read from /dev/input/eventX would block our thread otherwise
657054accacSCorvin Köhne */
658054accacSCorvin Köhne sc->vsc_fd = open(sc->vsc_evdev, O_RDWR | O_NONBLOCK);
659054accacSCorvin Köhne if (sc->vsc_fd < 0) {
660054accacSCorvin Köhne WPRINTF(("%s: failed to open %s", __func__, sc->vsc_evdev));
661054accacSCorvin Köhne goto failed;
662054accacSCorvin Köhne }
663054accacSCorvin Köhne
664054accacSCorvin Köhne /* check if evdev is really a evdev */
665054accacSCorvin Köhne int evversion;
666054accacSCorvin Köhne int error = ioctl(sc->vsc_fd, EVIOCGVERSION, &evversion);
667054accacSCorvin Köhne if (error < 0) {
668054accacSCorvin Köhne WPRINTF(("%s: %s is no evdev", __func__, sc->vsc_evdev));
669054accacSCorvin Köhne goto failed;
670054accacSCorvin Köhne }
671054accacSCorvin Köhne
672054accacSCorvin Köhne /* gain exclusive access to evdev */
673054accacSCorvin Köhne error = ioctl(sc->vsc_fd, EVIOCGRAB, 1);
674054accacSCorvin Köhne if (error < 0) {
675054accacSCorvin Köhne WPRINTF(("%s: failed to grab %s", __func__, sc->vsc_evdev));
676054accacSCorvin Köhne goto failed;
677054accacSCorvin Köhne }
678054accacSCorvin Köhne
679054accacSCorvin Köhne if (pthread_mutexattr_init(&mtx_attr)) {
680054accacSCorvin Köhne WPRINTF(("%s: init mutexattr failed", __func__));
681054accacSCorvin Köhne goto failed;
682054accacSCorvin Köhne }
683054accacSCorvin Köhne if (pthread_mutexattr_settype(&mtx_attr, PTHREAD_MUTEX_RECURSIVE)) {
684054accacSCorvin Köhne WPRINTF(("%s: settype mutexattr failed", __func__));
685054accacSCorvin Köhne goto failed;
686054accacSCorvin Köhne }
687054accacSCorvin Köhne if (pthread_mutex_init(&sc->vsc_mtx, &mtx_attr)) {
688054accacSCorvin Köhne WPRINTF(("%s: init mutex failed", __func__));
689054accacSCorvin Köhne goto failed;
690054accacSCorvin Köhne }
691054accacSCorvin Köhne
692054accacSCorvin Köhne /* init softc */
693054accacSCorvin Köhne sc->vsc_eventqueue.idx = 0;
694054accacSCorvin Köhne sc->vsc_eventqueue.size = VTINPUT_MAX_PKT_LEN;
695054accacSCorvin Köhne sc->vsc_eventqueue.events = calloc(
696054accacSCorvin Köhne sc->vsc_eventqueue.size, sizeof(struct vtinput_event_elem));
697054accacSCorvin Köhne sc->vsc_config_valid = 0;
698054accacSCorvin Köhne if (sc->vsc_eventqueue.events == NULL) {
699054accacSCorvin Köhne WPRINTF(("%s: failed to alloc eventqueue", __func__));
700054accacSCorvin Köhne goto failed;
701054accacSCorvin Köhne }
702054accacSCorvin Köhne
703054accacSCorvin Köhne /* register event handler */
704054accacSCorvin Köhne sc->vsc_evp = mevent_add(sc->vsc_fd, EVF_READ, vtinput_read_event, sc);
705054accacSCorvin Köhne if (sc->vsc_evp == NULL) {
706054accacSCorvin Köhne WPRINTF(("%s: could not register mevent", __func__));
707054accacSCorvin Köhne goto failed;
708054accacSCorvin Köhne }
709054accacSCorvin Köhne
710054accacSCorvin Köhne #ifndef WITHOUT_CAPSICUM
711054accacSCorvin Köhne cap_rights_t rights;
712054accacSCorvin Köhne cap_rights_init(&rights, CAP_EVENT, CAP_IOCTL, CAP_READ, CAP_WRITE);
713054accacSCorvin Köhne if (caph_rights_limit(sc->vsc_fd, &rights) == -1) {
714054accacSCorvin Köhne errx(EX_OSERR, "Unable to apply rights for sandbox");
715054accacSCorvin Köhne }
716054accacSCorvin Köhne #endif
717054accacSCorvin Köhne
718054accacSCorvin Köhne /* link virtio to softc */
719054accacSCorvin Köhne vi_softc_linkup(
720054accacSCorvin Köhne &sc->vsc_vs, &vtinput_vi_consts, sc, pi, sc->vsc_queues);
721054accacSCorvin Köhne sc->vsc_vs.vs_mtx = &sc->vsc_mtx;
722054accacSCorvin Köhne
723054accacSCorvin Köhne /* init virtio queues */
724054accacSCorvin Köhne sc->vsc_queues[VTINPUT_EVENTQ].vq_qsize = VTINPUT_RINGSZ;
725054accacSCorvin Köhne sc->vsc_queues[VTINPUT_EVENTQ].vq_notify = pci_vtinput_notify_eventq;
726054accacSCorvin Köhne sc->vsc_queues[VTINPUT_STATUSQ].vq_qsize = VTINPUT_RINGSZ;
727054accacSCorvin Köhne sc->vsc_queues[VTINPUT_STATUSQ].vq_notify = pci_vtinput_notify_statusq;
728054accacSCorvin Köhne
729054accacSCorvin Köhne /* initialize config space */
730054accacSCorvin Köhne pci_set_cfgdata16(pi, PCIR_DEVICE, VIRTIO_DEV_INPUT);
731054accacSCorvin Köhne pci_set_cfgdata16(pi, PCIR_VENDOR, VIRTIO_VENDOR);
732054accacSCorvin Köhne pci_set_cfgdata8(pi, PCIR_CLASS, PCIC_INPUTDEV);
733054accacSCorvin Köhne pci_set_cfgdata8(pi, PCIR_SUBCLASS, PCIS_INPUTDEV_OTHER);
734054accacSCorvin Köhne pci_set_cfgdata8(pi, PCIR_REVID, VIRTIO_REV_INPUT);
735054accacSCorvin Köhne pci_set_cfgdata16(pi, PCIR_SUBDEV_0, VIRTIO_SUBDEV_INPUT);
736054accacSCorvin Köhne pci_set_cfgdata16(pi, PCIR_SUBVEND_0, VIRTIO_SUBVEN_INPUT);
737054accacSCorvin Köhne
738054accacSCorvin Köhne /* add MSI-X table BAR */
739054accacSCorvin Köhne if (vi_intr_init(&sc->vsc_vs, 1, fbsdrun_virtio_msix()))
740054accacSCorvin Köhne goto failed;
741054accacSCorvin Köhne /* add virtio register */
742054accacSCorvin Köhne vi_set_io_bar(&sc->vsc_vs, 0);
743054accacSCorvin Köhne
744054accacSCorvin Köhne return (0);
745054accacSCorvin Köhne
746054accacSCorvin Köhne failed:
747054accacSCorvin Köhne if (sc == NULL) {
748054accacSCorvin Köhne return (-1);
749054accacSCorvin Köhne }
750054accacSCorvin Köhne
751054accacSCorvin Köhne if (sc->vsc_evp)
752054accacSCorvin Köhne mevent_delete(sc->vsc_evp);
753054accacSCorvin Köhne if (sc->vsc_eventqueue.events)
754054accacSCorvin Köhne free(sc->vsc_eventqueue.events);
755054accacSCorvin Köhne if (sc->vsc_mtx)
756054accacSCorvin Köhne pthread_mutex_destroy(&sc->vsc_mtx);
757054accacSCorvin Köhne if (mtx_attr)
758054accacSCorvin Köhne pthread_mutexattr_destroy(&mtx_attr);
759054accacSCorvin Köhne if (sc->vsc_fd)
760054accacSCorvin Köhne close(sc->vsc_fd);
761054accacSCorvin Köhne
762054accacSCorvin Köhne free(sc);
763054accacSCorvin Köhne
764054accacSCorvin Köhne return (-1);
765054accacSCorvin Köhne }
766054accacSCorvin Köhne
76737045dfaSMark Johnston static const struct pci_devemu pci_de_vinput = {
768054accacSCorvin Köhne .pe_emu = "virtio-input",
769054accacSCorvin Köhne .pe_init = pci_vtinput_init,
770054accacSCorvin Köhne .pe_legacy_config = pci_vtinput_legacy_config,
771054accacSCorvin Köhne .pe_barwrite = vi_pci_write,
772054accacSCorvin Köhne .pe_barread = vi_pci_read,
773054accacSCorvin Köhne };
774054accacSCorvin Köhne PCI_EMUL_SET(pci_de_vinput);
775