xref: /qemu/hw/input/virtio-input.c (revision bc35d510)
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 #include "qapi/error.h"
9 #include "qemu/iov.h"
10 #include "trace.h"
11 
12 #include "hw/qdev.h"
13 #include "hw/virtio/virtio.h"
14 #include "hw/virtio/virtio-input.h"
15 
16 #include "standard-headers/linux/input.h"
17 
18 #define VIRTIO_INPUT_VM_VERSION 1
19 
20 /* ----------------------------------------------------------------- */
21 
22 void virtio_input_send(VirtIOInput *vinput, virtio_input_event *event)
23 {
24     VirtQueueElement *elem;
25     unsigned have, need;
26     int i, len;
27 
28     if (!vinput->active) {
29         return;
30     }
31 
32     /* queue up events ... */
33     if (vinput->qindex == vinput->qsize) {
34         vinput->qsize++;
35         vinput->queue = realloc(vinput->queue, vinput->qsize *
36                                 sizeof(virtio_input_event));
37     }
38     vinput->queue[vinput->qindex++] = *event;
39 
40     /* ... until we see a report sync ... */
41     if (event->type != cpu_to_le16(EV_SYN) ||
42         event->code != cpu_to_le16(SYN_REPORT)) {
43         return;
44     }
45 
46     /* ... then check available space ... */
47     need = sizeof(virtio_input_event) * vinput->qindex;
48     virtqueue_get_avail_bytes(vinput->evt, &have, NULL, need, 0);
49     if (have < need) {
50         vinput->qindex = 0;
51         trace_virtio_input_queue_full();
52         return;
53     }
54 
55     /* ... and finally pass them to the guest */
56     for (i = 0; i < vinput->qindex; i++) {
57         elem = virtqueue_pop(vinput->evt, sizeof(VirtQueueElement));
58         if (!elem) {
59             /* should not happen, we've checked for space beforehand */
60             fprintf(stderr, "%s: Huh?  No vq elem available ...\n", __func__);
61             return;
62         }
63         len = iov_from_buf(elem->in_sg, elem->in_num,
64                            0, vinput->queue+i, sizeof(virtio_input_event));
65         virtqueue_push(vinput->evt, elem, len);
66         g_free(elem);
67     }
68     virtio_notify(VIRTIO_DEVICE(vinput), vinput->evt);
69     vinput->qindex = 0;
70 }
71 
72 static void virtio_input_handle_evt(VirtIODevice *vdev, VirtQueue *vq)
73 {
74     /* nothing */
75 }
76 
77 static void virtio_input_handle_sts(VirtIODevice *vdev, VirtQueue *vq)
78 {
79     VirtIOInputClass *vic = VIRTIO_INPUT_GET_CLASS(vdev);
80     VirtIOInput *vinput = VIRTIO_INPUT(vdev);
81     virtio_input_event event;
82     VirtQueueElement *elem;
83     int len;
84 
85     for (;;) {
86         elem = virtqueue_pop(vinput->sts, sizeof(VirtQueueElement));
87         if (!elem) {
88             break;
89         }
90 
91         memset(&event, 0, sizeof(event));
92         len = iov_to_buf(elem->out_sg, elem->out_num,
93                          0, &event, sizeof(event));
94         if (vic->handle_status) {
95             vic->handle_status(vinput, &event);
96         }
97         virtqueue_push(vinput->sts, elem, len);
98         g_free(elem);
99     }
100     virtio_notify(vdev, vinput->sts);
101 }
102 
103 virtio_input_config *virtio_input_find_config(VirtIOInput *vinput,
104                                               uint8_t select,
105                                               uint8_t subsel)
106 {
107     VirtIOInputConfig *cfg;
108 
109     QTAILQ_FOREACH(cfg, &vinput->cfg_list, node) {
110         if (select == cfg->config.select &&
111             subsel == cfg->config.subsel) {
112             return &cfg->config;
113         }
114     }
115     return NULL;
116 }
117 
118 void virtio_input_add_config(VirtIOInput *vinput,
119                              virtio_input_config *config)
120 {
121     VirtIOInputConfig *cfg;
122 
123     if (virtio_input_find_config(vinput, config->select, config->subsel)) {
124         /* should not happen */
125         fprintf(stderr, "%s: duplicate config: %d/%d\n",
126                 __func__, config->select, config->subsel);
127         abort();
128     }
129 
130     cfg = g_new0(VirtIOInputConfig, 1);
131     cfg->config = *config;
132     QTAILQ_INSERT_TAIL(&vinput->cfg_list, cfg, node);
133 }
134 
135 void virtio_input_init_config(VirtIOInput *vinput,
136                               virtio_input_config *config)
137 {
138     int i = 0;
139 
140     QTAILQ_INIT(&vinput->cfg_list);
141     while (config[i].select) {
142         virtio_input_add_config(vinput, config + i);
143         i++;
144     }
145 }
146 
147 void virtio_input_idstr_config(VirtIOInput *vinput,
148                                uint8_t select, const char *string)
149 {
150     virtio_input_config id;
151 
152     if (!string) {
153         return;
154     }
155     memset(&id, 0, sizeof(id));
156     id.select = select;
157     id.size = snprintf(id.u.string, sizeof(id.u.string), "%s", string);
158     virtio_input_add_config(vinput, &id);
159 }
160 
161 static void virtio_input_get_config(VirtIODevice *vdev, uint8_t *config_data)
162 {
163     VirtIOInput *vinput = VIRTIO_INPUT(vdev);
164     virtio_input_config *config;
165 
166     config = virtio_input_find_config(vinput, vinput->cfg_select,
167                                       vinput->cfg_subsel);
168     if (config) {
169         memcpy(config_data, config, vinput->cfg_size);
170     } else {
171         memset(config_data, 0, vinput->cfg_size);
172     }
173 }
174 
175 static void virtio_input_set_config(VirtIODevice *vdev,
176                                     const uint8_t *config_data)
177 {
178     VirtIOInput *vinput = VIRTIO_INPUT(vdev);
179     virtio_input_config *config = (virtio_input_config *)config_data;
180 
181     vinput->cfg_select = config->select;
182     vinput->cfg_subsel = config->subsel;
183     virtio_notify_config(vdev);
184 }
185 
186 static uint64_t virtio_input_get_features(VirtIODevice *vdev, uint64_t f,
187                                           Error **errp)
188 {
189     return f;
190 }
191 
192 static void virtio_input_set_status(VirtIODevice *vdev, uint8_t val)
193 {
194     VirtIOInputClass *vic = VIRTIO_INPUT_GET_CLASS(vdev);
195     VirtIOInput *vinput = VIRTIO_INPUT(vdev);
196 
197     if (val & VIRTIO_CONFIG_S_DRIVER_OK) {
198         if (!vinput->active) {
199             vinput->active = true;
200             if (vic->change_active) {
201                 vic->change_active(vinput);
202             }
203         }
204     }
205 }
206 
207 static void virtio_input_reset(VirtIODevice *vdev)
208 {
209     VirtIOInputClass *vic = VIRTIO_INPUT_GET_CLASS(vdev);
210     VirtIOInput *vinput = VIRTIO_INPUT(vdev);
211 
212     if (vinput->active) {
213         vinput->active = false;
214         if (vic->change_active) {
215             vic->change_active(vinput);
216         }
217     }
218 }
219 
220 static int virtio_input_load(QEMUFile *f, void *opaque, size_t size)
221 {
222     VirtIOInput *vinput = opaque;
223     VirtIOInputClass *vic = VIRTIO_INPUT_GET_CLASS(vinput);
224     VirtIODevice *vdev = VIRTIO_DEVICE(vinput);
225     int ret;
226 
227     ret = virtio_load(vdev, f, VIRTIO_INPUT_VM_VERSION);
228     if (ret) {
229         return ret;
230     }
231 
232     /* post_load() */
233     vinput->active = vdev->status & VIRTIO_CONFIG_S_DRIVER_OK;
234     if (vic->change_active) {
235         vic->change_active(vinput);
236     }
237     return 0;
238 }
239 
240 static void virtio_input_device_realize(DeviceState *dev, Error **errp)
241 {
242     VirtIOInputClass *vic = VIRTIO_INPUT_GET_CLASS(dev);
243     VirtIODevice *vdev = VIRTIO_DEVICE(dev);
244     VirtIOInput *vinput = VIRTIO_INPUT(dev);
245     VirtIOInputConfig *cfg;
246     Error *local_err = NULL;
247 
248     if (vic->realize) {
249         vic->realize(dev, &local_err);
250         if (local_err) {
251             error_propagate(errp, local_err);
252             return;
253         }
254     }
255 
256     virtio_input_idstr_config(vinput, VIRTIO_INPUT_CFG_ID_SERIAL,
257                               vinput->serial);
258 
259     QTAILQ_FOREACH(cfg, &vinput->cfg_list, node) {
260         if (vinput->cfg_size < cfg->config.size) {
261             vinput->cfg_size = cfg->config.size;
262         }
263     }
264     vinput->cfg_size += 8;
265     assert(vinput->cfg_size <= sizeof(virtio_input_config));
266 
267     virtio_init(vdev, "virtio-input", VIRTIO_ID_INPUT,
268                 vinput->cfg_size);
269     vinput->evt = virtio_add_queue(vdev, 64, virtio_input_handle_evt);
270     vinput->sts = virtio_add_queue(vdev, 64, virtio_input_handle_sts);
271 }
272 
273 static void virtio_input_device_unrealize(DeviceState *dev, Error **errp)
274 {
275     VirtIOInputClass *vic = VIRTIO_INPUT_GET_CLASS(dev);
276     VirtIODevice *vdev = VIRTIO_DEVICE(dev);
277     Error *local_err = NULL;
278 
279     if (vic->unrealize) {
280         vic->unrealize(dev, &local_err);
281         if (local_err) {
282             error_propagate(errp, local_err);
283             return;
284         }
285     }
286     virtio_cleanup(vdev);
287 }
288 
289 VMSTATE_VIRTIO_DEVICE(input, VIRTIO_INPUT_VM_VERSION, virtio_input_load,
290                       virtio_vmstate_save);
291 
292 static Property virtio_input_properties[] = {
293     DEFINE_PROP_STRING("serial", VirtIOInput, serial),
294     DEFINE_PROP_END_OF_LIST(),
295 };
296 
297 static void virtio_input_class_init(ObjectClass *klass, void *data)
298 {
299     DeviceClass *dc = DEVICE_CLASS(klass);
300     VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass);
301 
302     dc->props          = virtio_input_properties;
303     dc->vmsd           = &vmstate_virtio_input;
304     set_bit(DEVICE_CATEGORY_INPUT, dc->categories);
305     vdc->realize      = virtio_input_device_realize;
306     vdc->unrealize    = virtio_input_device_unrealize;
307     vdc->get_config   = virtio_input_get_config;
308     vdc->set_config   = virtio_input_set_config;
309     vdc->get_features = virtio_input_get_features;
310     vdc->set_status   = virtio_input_set_status;
311     vdc->reset        = virtio_input_reset;
312 }
313 
314 static const TypeInfo virtio_input_info = {
315     .name          = TYPE_VIRTIO_INPUT,
316     .parent        = TYPE_VIRTIO_DEVICE,
317     .instance_size = sizeof(VirtIOInput),
318     .class_size    = sizeof(VirtIOInputClass),
319     .class_init    = virtio_input_class_init,
320     .abstract      = true,
321 };
322 
323 /* ----------------------------------------------------------------- */
324 
325 static void virtio_register_types(void)
326 {
327     type_register_static(&virtio_input_info);
328 }
329 
330 type_init(virtio_register_types)
331