]> git.proxmox.com Git - mirror_qemu.git/blame - hw/input/virtio-input.c
9pfs: Wrap in vmstate
[mirror_qemu.git] / hw / input / virtio-input.c
CommitLineData
f73ddbad
GH
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
9b8bfe21 7#include "qemu/osdep.h"
da34e65c 8#include "qapi/error.h"
f73ddbad 9#include "qemu/iov.h"
c80276b4 10#include "trace.h"
f73ddbad
GH
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
2d738374
GH
18#define VIRTIO_INPUT_VM_VERSION 1
19
f73ddbad
GH
20/* ----------------------------------------------------------------- */
21
22void virtio_input_send(VirtIOInput *vinput, virtio_input_event *event)
23{
51b19ebe 24 VirtQueueElement *elem;
f73ddbad
GH
25 unsigned have, need;
26 int i, len;
27
d9460a75
GH
28 if (!vinput->active) {
29 return;
30 }
31
f73ddbad
GH
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;
c80276b4 51 trace_virtio_input_queue_full();
f73ddbad
GH
52 return;
53 }
54
55 /* ... and finally pass them to the guest */
56 for (i = 0; i < vinput->qindex; i++) {
51b19ebe
PB
57 elem = virtqueue_pop(vinput->evt, sizeof(VirtQueueElement));
58 if (!elem) {
f73ddbad
GH
59 /* should not happen, we've checked for space beforehand */
60 fprintf(stderr, "%s: Huh? No vq elem available ...\n", __func__);
61 return;
62 }
51b19ebe 63 len = iov_from_buf(elem->in_sg, elem->in_num,
f73ddbad 64 0, vinput->queue+i, sizeof(virtio_input_event));
51b19ebe
PB
65 virtqueue_push(vinput->evt, elem, len);
66 g_free(elem);
f73ddbad
GH
67 }
68 virtio_notify(VIRTIO_DEVICE(vinput), vinput->evt);
69 vinput->qindex = 0;
70}
71
72static void virtio_input_handle_evt(VirtIODevice *vdev, VirtQueue *vq)
73{
74 /* nothing */
75}
76
77static 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;
51b19ebe 82 VirtQueueElement *elem;
f73ddbad
GH
83 int len;
84
51b19ebe
PB
85 for (;;) {
86 elem = virtqueue_pop(vinput->sts, sizeof(VirtQueueElement));
87 if (!elem) {
88 break;
89 }
90
f73ddbad 91 memset(&event, 0, sizeof(event));
51b19ebe 92 len = iov_to_buf(elem->out_sg, elem->out_num,
f73ddbad
GH
93 0, &event, sizeof(event));
94 if (vic->handle_status) {
95 vic->handle_status(vinput, &event);
96 }
51b19ebe
PB
97 virtqueue_push(vinput->sts, elem, len);
98 g_free(elem);
f73ddbad
GH
99 }
100 virtio_notify(vdev, vinput->sts);
101}
102
b065e275
LP
103virtio_input_config *virtio_input_find_config(VirtIOInput *vinput,
104 uint8_t select,
105 uint8_t subsel)
f73ddbad
GH
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
118void 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
135void 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
147void 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
161static 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
175static 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
9d5b731d
JW
186static uint64_t virtio_input_get_features(VirtIODevice *vdev, uint64_t f,
187 Error **errp)
f73ddbad
GH
188{
189 return f;
190}
191
192static 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
207static 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
2d738374
GH
220static void virtio_input_save(QEMUFile *f, void *opaque)
221{
222 VirtIOInput *vinput = opaque;
223 VirtIODevice *vdev = VIRTIO_DEVICE(vinput);
224
225 virtio_save(vdev, f);
226}
227
228static int virtio_input_load(QEMUFile *f, void *opaque, int version_id)
229{
230 VirtIOInput *vinput = opaque;
231 VirtIOInputClass *vic = VIRTIO_INPUT_GET_CLASS(vinput);
232 VirtIODevice *vdev = VIRTIO_DEVICE(vinput);
233 int ret;
234
235 if (version_id != VIRTIO_INPUT_VM_VERSION) {
236 return -EINVAL;
237 }
238
239 ret = virtio_load(vdev, f, version_id);
240 if (ret) {
241 return ret;
242 }
243
244 /* post_load() */
245 vinput->active = vdev->status & VIRTIO_CONFIG_S_DRIVER_OK;
246 if (vic->change_active) {
247 vic->change_active(vinput);
248 }
249 return 0;
250}
251
f73ddbad
GH
252static void virtio_input_device_realize(DeviceState *dev, Error **errp)
253{
254 VirtIOInputClass *vic = VIRTIO_INPUT_GET_CLASS(dev);
255 VirtIODevice *vdev = VIRTIO_DEVICE(dev);
256 VirtIOInput *vinput = VIRTIO_INPUT(dev);
257 VirtIOInputConfig *cfg;
258 Error *local_err = NULL;
259
260 if (vic->realize) {
261 vic->realize(dev, &local_err);
262 if (local_err) {
263 error_propagate(errp, local_err);
264 return;
265 }
266 }
267
268 virtio_input_idstr_config(vinput, VIRTIO_INPUT_CFG_ID_SERIAL,
6f2b9a5b 269 vinput->serial);
f73ddbad
GH
270
271 QTAILQ_FOREACH(cfg, &vinput->cfg_list, node) {
272 if (vinput->cfg_size < cfg->config.size) {
273 vinput->cfg_size = cfg->config.size;
274 }
275 }
276 vinput->cfg_size += 8;
277 assert(vinput->cfg_size <= sizeof(virtio_input_config));
278
279 virtio_init(vdev, "virtio-input", VIRTIO_ID_INPUT,
280 vinput->cfg_size);
281 vinput->evt = virtio_add_queue(vdev, 64, virtio_input_handle_evt);
282 vinput->sts = virtio_add_queue(vdev, 64, virtio_input_handle_sts);
2d738374
GH
283
284 register_savevm(dev, "virtio-input", -1, VIRTIO_INPUT_VM_VERSION,
285 virtio_input_save, virtio_input_load, vinput);
f73ddbad
GH
286}
287
288static void virtio_input_device_unrealize(DeviceState *dev, Error **errp)
289{
290 VirtIOInputClass *vic = VIRTIO_INPUT_GET_CLASS(dev);
291 VirtIODevice *vdev = VIRTIO_DEVICE(dev);
2d738374 292 VirtIOInput *vinput = VIRTIO_INPUT(dev);
f73ddbad
GH
293 Error *local_err = NULL;
294
2d738374
GH
295 unregister_savevm(dev, "virtio-input", vinput);
296
f73ddbad
GH
297 if (vic->unrealize) {
298 vic->unrealize(dev, &local_err);
299 if (local_err) {
300 error_propagate(errp, local_err);
301 return;
302 }
303 }
304 virtio_cleanup(vdev);
305}
306
6f2b9a5b
GH
307static Property virtio_input_properties[] = {
308 DEFINE_PROP_STRING("serial", VirtIOInput, serial),
309 DEFINE_PROP_END_OF_LIST(),
310};
311
f73ddbad
GH
312static void virtio_input_class_init(ObjectClass *klass, void *data)
313{
314 DeviceClass *dc = DEVICE_CLASS(klass);
315 VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass);
316
6f2b9a5b 317 dc->props = virtio_input_properties;
f73ddbad
GH
318 set_bit(DEVICE_CATEGORY_INPUT, dc->categories);
319 vdc->realize = virtio_input_device_realize;
320 vdc->unrealize = virtio_input_device_unrealize;
321 vdc->get_config = virtio_input_get_config;
322 vdc->set_config = virtio_input_set_config;
323 vdc->get_features = virtio_input_get_features;
324 vdc->set_status = virtio_input_set_status;
325 vdc->reset = virtio_input_reset;
326}
327
328static const TypeInfo virtio_input_info = {
329 .name = TYPE_VIRTIO_INPUT,
330 .parent = TYPE_VIRTIO_DEVICE,
331 .instance_size = sizeof(VirtIOInput),
332 .class_size = sizeof(VirtIOInputClass),
333 .class_init = virtio_input_class_init,
334 .abstract = true,
335};
336
337/* ----------------------------------------------------------------- */
338
339static void virtio_register_types(void)
340{
341 type_register_static(&virtio_input_info);
342}
343
344type_init(virtio_register_types)