xref: /openbmc/qemu/hw/input/virtio-input.c (revision 48151859)
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 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 
228 static 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 
252 static 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,
269                               vinput->serial);
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);
283 
284     register_savevm(dev, "virtio-input", -1, VIRTIO_INPUT_VM_VERSION,
285                     virtio_input_save, virtio_input_load, vinput);
286 }
287 
288 static void virtio_input_device_unrealize(DeviceState *dev, Error **errp)
289 {
290     VirtIOInputClass *vic = VIRTIO_INPUT_GET_CLASS(dev);
291     VirtIODevice *vdev = VIRTIO_DEVICE(dev);
292     VirtIOInput *vinput = VIRTIO_INPUT(dev);
293     Error *local_err = NULL;
294 
295     unregister_savevm(dev, "virtio-input", vinput);
296 
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 
307 static Property virtio_input_properties[] = {
308     DEFINE_PROP_STRING("serial", VirtIOInput, serial),
309     DEFINE_PROP_END_OF_LIST(),
310 };
311 
312 static void virtio_input_class_init(ObjectClass *klass, void *data)
313 {
314     DeviceClass *dc = DEVICE_CLASS(klass);
315     VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass);
316 
317     dc->props          = virtio_input_properties;
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 
328 static 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 
339 static void virtio_register_types(void)
340 {
341     type_register_static(&virtio_input_info);
342 }
343 
344 type_init(virtio_register_types)
345