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